JBR-7616: improved MTLRenderQueue exception handling

(cherry picked from commit ea57bf75e7)
This commit is contained in:
bourgesl
2024-09-11 22:23:25 +02:00
committed by jbrbot
parent e0b9c49a4f
commit 7b8214e753
4 changed files with 224 additions and 51 deletions

View File

@@ -36,7 +36,10 @@
#include "MTLRenderQueue.h"
#include "MTLRenderer.h"
#include "MTLTextRenderer.h"
#import "ThreadUtilities.h"
#define TRACE_OP 0
#define DST_TYPE(dstOps) ((dstOps != NULL) ? dstOps->drawableType : MTLSD_UNDEFINED)
/**
* References to the "current" context and destination surface.
@@ -48,6 +51,63 @@ jint mtlPreviousOp = MTL_OP_INIT;
extern BOOL isDisplaySyncEnabled();
extern void MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo);
static const char* mtlOpCodeToStr(uint opcode);
static const char* mtlOpToStr(uint op);
static const char* mtlDstTypeToStr(uint op);
/*
* Derived from JNI_COCOA_ENTER(env):
* Create a pool and initiate a try block to catch any exception
*/
#define RENDER_LOOP_ENTER(env) \
BOOL sync = NO; \
jint opcode = -1; \
const NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; \
@try
/*
* Derived from JNI_COCOA_EXIT(env):
* Don't allow NSExceptions to escape to Java.
* If there is a Java exception that has been thrown that should escape.
* And ensure we drain the auto-release pool.
*/
#define RENDER_LOOP_EXIT(env, className) \
@catch (NSException *e) { \
NSLog(@"%s_flushBuffer: Failed opcode=%s op=%s dstType=%s ctx=%p", \
className, mtlOpCodeToStr(opcode), mtlOpToStr(mtlPreviousOp), \
mtlDstTypeToStr(DST_TYPE(dstOps)), mtlc); \
NSLog(@"%s_flushBuffer Exception: %@", className, [e description]); \
NSLog(@"%s_flushBuffer callstack: %@", className, [e callStackSymbols]); \
/* Finally (JetBrains Runtime only) report this message to JVM crash log: */ \
JNU_LOG_EVENT(env, "%s_flushBuffer: Failed opcode=%s op=%s dstType=%s ctx=%p", \
className, mtlOpCodeToStr(opcode), mtlOpToStr(mtlPreviousOp), \
mtlDstTypeToStr(DST_TYPE(dstOps)), mtlc); \
/* report failure to the UncaughtExceptionHandler to make a crash report: */ \
@throw e; \
} \
@finally { \
/* flush GPU state before draining pool: */ \
MTLRenderQueue_reset(mtlc, sync); \
[pool drain]; \
}
void MTLRenderQueue_reset(MTLContext* context, BOOL sync)
{
// Ensure flushing encoder before draining the NSAutoreleasePool:
if (context != NULL) {
if (mtlPreviousOp == MTL_OP_MASK_OP) {
MTLVertexCache_DisableMaskCache(context);
}
if (isDisplaySyncEnabled()) {
[context commitCommandBuffer:NO display:YES];
} else {
[context commitCommandBuffer:sync display:YES];
}
}
RESET_PREVIOUS_OP();
}
void MTLRenderQueue_CheckPreviousOp(jint op) {
if (mtlPreviousOp == op) {
@@ -69,6 +129,9 @@ void MTLRenderQueue_CheckPreviousOp(jint op) {
J2dTraceLn1(J2D_TRACE_VERBOSE,
"MTLRenderQueue_CheckPreviousOp: new op=%d", op);
if (TRACE_OP) J2dRlsTraceLn2(J2D_TRACE_INFO, "MTLRenderQueue_CheckPreviousOp: op=%s\tnew op=%s",
mtlOpToStr(mtlPreviousOp), mtlOpToStr(op));
switch (mtlPreviousOp) {
case MTL_OP_INIT :
mtlPreviousOp = op;
@@ -99,7 +162,6 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
jlong buf, jint limit)
{
unsigned char *b, *end;
BOOL sync = NO;
J2dTraceLn1(J2D_TRACE_INFO,
"MTLRenderQueue_flushBuffer: limit=%d", limit);
@@ -111,13 +173,17 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
}
end = b + limit;
@autoreleasepool {
// Handle any NSException thrown:
RENDER_LOOP_ENTER(env)
{
while (b < end) {
jint opcode = NEXT_INT(b);
opcode = NEXT_INT(b);
J2dTraceLn2(J2D_TRACE_VERBOSE,
"MTLRenderQueue_flushBuffer: opcode=%d, rem=%d",
opcode, (end-b));
if (TRACE_OP) J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "MTLRenderQueue_flushBuffer: opcode=%s", mtlOpCodeToStr(opcode));
switch (opcode) {
@@ -143,7 +209,6 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
CHECK_RENDER_OP(MTL_OP_OTHER, dstOps, sync);
if ([mtlc useXORComposite]) {
[mtlc commitCommandBuffer:YES display:NO];
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_RECT in XOR mode - Force commit earlier draw calls before DRAW_RECT.");
@@ -660,6 +725,20 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
break;
}
case sun_java2d_pipe_BufferedOpCodes_FLUSH_BUFFER:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pLayerPtr = NEXT_LONG(b);
MTLLayer* layer = (MTLLayer*)pLayerPtr;
if (layer != nil) {
[layer flushBuffer];
} else {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer(FLUSH_BUFFER): MTLLayer is nil");
}
break;
}
case sun_java2d_pipe_BufferedOpCodes_SYNC:
{
CHECK_PREVIOUS_OP(MTL_OP_SYNC);
@@ -862,40 +941,14 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
break;
}
case sun_java2d_pipe_BufferedOpCodes_FLUSH_BUFFER:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pLayerPtr = NEXT_LONG(b);
MTLLayer* layer = (MTLLayer*)pLayerPtr;
if (layer != nil) {
[layer flushBuffer];
} else {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer(FLUSH_BUFFER): MTLLayer is nil");
}
break;
}
default:
J2dRlsTraceLn1(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer: invalid opcode=%d", opcode);
return;
}
}
if (mtlc != NULL) {
if (mtlPreviousOp == MTL_OP_MASK_OP) {
MTLVertexCache_DisableMaskCache(mtlc);
}
if (isDisplaySyncEnabled()) {
[mtlc commitCommandBuffer:NO display:YES];
} else {
[mtlc commitCommandBuffer:sync display:YES];
}
}
RESET_PREVIOUS_OP();
} // while op
}
RENDER_LOOP_EXIT(env, "MTLRenderQueue");
}
/**
@@ -916,4 +969,126 @@ BMTLSDOps *
MTLRenderQueue_GetCurrentDestination()
{
return dstOps;
}
}
/* debugging helper functions */
static const char* mtlOpToStr(uint op) {
#undef CASE_MTL_OP
#define CASE_MTL_OP(X) \
case MTL_OP_##X: \
return #X;
switch (op) {
CASE_MTL_OP(INIT)
CASE_MTL_OP(AA)
CASE_MTL_OP(SET_COLOR)
CASE_MTL_OP(RESET_PAINT)
CASE_MTL_OP(SYNC)
CASE_MTL_OP(SHAPE_CLIP_SPANS)
CASE_MTL_OP(MASK_OP)
CASE_MTL_OP(OTHER)
default:
return "";
}
#undef CASE_MTL_OP
}
static const char* mtlOpCodeToStr(uint opcode) {
#undef CASE_BUF_OP
#define CASE_BUF_OP(X) \
case sun_java2d_pipe_BufferedOpCodes_##X: \
return #X;
switch (opcode) {
// draw ops
CASE_BUF_OP(DRAW_LINE)
CASE_BUF_OP(DRAW_RECT)
CASE_BUF_OP(DRAW_POLY)
CASE_BUF_OP(DRAW_PIXEL)
CASE_BUF_OP(DRAW_SCANLINES)
CASE_BUF_OP(DRAW_PARALLELOGRAM)
CASE_BUF_OP(DRAW_AAPARALLELOGRAM)
// fill ops
CASE_BUF_OP(FILL_RECT)
CASE_BUF_OP(FILL_SPANS)
CASE_BUF_OP(FILL_PARALLELOGRAM)
CASE_BUF_OP(FILL_AAPARALLELOGRAM)
// copy-related ops
CASE_BUF_OP(COPY_AREA)
CASE_BUF_OP(BLIT)
CASE_BUF_OP(MASK_FILL)
CASE_BUF_OP(MASK_BLIT)
CASE_BUF_OP(SURFACE_TO_SW_BLIT)
// text-related ops
CASE_BUF_OP(DRAW_GLYPH_LIST)
// state-related ops
CASE_BUF_OP(SET_RECT_CLIP)
CASE_BUF_OP(BEGIN_SHAPE_CLIP)
CASE_BUF_OP(SET_SHAPE_CLIP_SPANS)
CASE_BUF_OP(END_SHAPE_CLIP)
CASE_BUF_OP(RESET_CLIP)
CASE_BUF_OP(SET_ALPHA_COMPOSITE)
CASE_BUF_OP(SET_XOR_COMPOSITE)
CASE_BUF_OP(RESET_COMPOSITE)
CASE_BUF_OP(SET_TRANSFORM)
CASE_BUF_OP(RESET_TRANSFORM)
// context-related ops
CASE_BUF_OP(SET_SURFACES)
CASE_BUF_OP(SET_SCRATCH_SURFACE)
CASE_BUF_OP(FLUSH_SURFACE)
CASE_BUF_OP(DISPOSE_SURFACE)
CASE_BUF_OP(DISPOSE_CONFIG)
CASE_BUF_OP(INVALIDATE_CONTEXT)
CASE_BUF_OP(SYNC)
CASE_BUF_OP(RESTORE_DEVICES)
CASE_BUF_OP(CONFIGURE_SURFACE) /* unsupported */
CASE_BUF_OP(SWAP_BUFFERS) /* unsupported */
CASE_BUF_OP(FLUSH_BUFFER)
// special no-op (mainly used for achieving 8-byte alignment)
CASE_BUF_OP(NOOP)
// paint-related ops
CASE_BUF_OP(RESET_PAINT)
CASE_BUF_OP(SET_COLOR)
CASE_BUF_OP(SET_GRADIENT_PAINT)
CASE_BUF_OP(SET_LINEAR_GRADIENT_PAINT)
CASE_BUF_OP(SET_RADIAL_GRADIENT_PAINT)
CASE_BUF_OP(SET_TEXTURE_PAINT)
// BufferedImageOp-related ops
CASE_BUF_OP(ENABLE_CONVOLVE_OP)
CASE_BUF_OP(DISABLE_CONVOLVE_OP)
CASE_BUF_OP(ENABLE_RESCALE_OP)
CASE_BUF_OP(DISABLE_RESCALE_OP)
CASE_BUF_OP(ENABLE_LOOKUP_OP)
CASE_BUF_OP(DISABLE_LOOKUP_OP)
default:
return "";
}
#undef CASE_BUF_OP
}
static const char* mtlDstTypeToStr(uint op) {
#undef CASE_MTLSD_OP
#define CASE_MTLSD_OP(X) \
case MTLSD_##X: \
return #X;
switch (op) {
CASE_MTLSD_OP(UNDEFINED)
CASE_MTLSD_OP(WINDOW)
CASE_MTLSD_OP(TEXTURE)
CASE_MTLSD_OP(FLIP_BACKBUFFER)
CASE_MTLSD_OP(RT_TEXTURE)
default:
return "";
}
#undef CASE_MTLSD_OP
}

View File

@@ -222,11 +222,12 @@
*/
#define JNI_COCOA_EXIT(env) \
} \
@catch (NSException *e) { \
NSLog(@"%@", [e callStackSymbols]); \
@catch (NSException *e) { \
NSLog(@"Apple AWT Cocoa Exception: %@", [e description]); \
NSLog(@"Apple AWT Cocoa Exception callstack: %@", [e callStackSymbols]); \
} \
@finally { \
[pool drain]; \
[pool drain]; \
};
/* Same as above but adds a clean up action.
@@ -236,10 +237,11 @@
} \
@catch (NSException *e) { \
{ action; }; \
NSLog(@"%@", [e callStackSymbols]); \
NSLog(@"Apple AWT Cocoa Exception: %@", [e description]); \
NSLog(@"Apple AWT Cocoa Exception callstack: %@", [e callStackSymbols]); \
} \
@finally { \
[pool drain]; \
[pool drain]; \
};
/******** STRING CONVERSION SUPPORT *********/

View File

@@ -140,7 +140,6 @@ __attribute__((visibility("default")))
+ (void)setAppkitThreadGroup:(jobject)group;
+ (void)setApplicationOwner:(BOOL)owner;
+ (NSString*)getCaller;
+ (void)performOnMainThreadNowOrLater:(void (^)())block;
+ (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block;
+ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait;

View File

@@ -151,7 +151,7 @@ AWT_ASSERT_APPKIT_THREAD;
if ([NSThread isMainThread]) {
block();
} else {
[self performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:NO];
[ThreadUtilities performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:NO];
}
}
@@ -159,7 +159,7 @@ AWT_ASSERT_APPKIT_THREAD;
if ([NSThread isMainThread] && wait) {
block();
} else {
[self performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:wait];
[ThreadUtilities performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:wait];
}
}
@@ -189,22 +189,19 @@ AWT_ASSERT_APPKIT_THREAD;
setBlockingEventDispatchThread(NO);
}
});
[self performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:YES modes:javaModes];
[ThreadUtilities performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:YES modes:javaModes];
} else {
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
}
} else {
// Perform instrumentation on selector:
const NSString* caller = [self getCaller];
const NSString* caller = [ThreadUtilities getCaller];
BOOL invokeDirect = NO;
BOOL blockingEDT;
BOOL blockingEDT = NO;
if ([NSThread isMainThread] && wait) {
invokeDirect = YES;
blockingEDT = NO;
} else if (wait && isEventDispatchThread()) {
blockingEDT = YES;
} else {
blockingEDT = NO;
}
const char* operation = (invokeDirect ? "now " : (blockingEDT ? "block" : "later"));
@@ -222,7 +219,7 @@ AWT_ASSERT_APPKIT_THREAD;
const double elapsedMs = (CACurrentMediaTime() - start) * 1000.0;
if (elapsedMs > mtThreshold) {
#if USE_LWC_LOG == 1
lwc_plog([self getJNIEnv], "performOnMainThread(%s)[time: %.3lf ms]: [%s]", operation, elapsedMs, toCString(caller));
lwc_plog([ThreadUtilities getJNIEnv], "performOnMainThread(%s)[time: %.3lf ms]: [%s]", operation, elapsedMs, toCString(caller));
#else
NSLog(@"performOnMainThread(%s)[time: %.3lf ms]: [%@]", operation, elapsedMs, caller);
#endif
@@ -230,9 +227,9 @@ AWT_ASSERT_APPKIT_THREAD;
}
});
if (invokeDirect) {
[self performSelector:@selector(invokeBlockCopy:) withObject:blockCopy];
[ThreadUtilities performSelector:@selector(invokeBlockCopy:) withObject:blockCopy];
} else {
[self performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:wait modes:javaModes];
[ThreadUtilities performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:wait modes:javaModes];
}
}
}