JBR-7588 Metal: Reuse MTLContext for all GCs of the same GPU

Implemented reference counting for shared MTLContext objects. Supported multiple display links per MTLContext. Also, works for macOS version < 10.13

(cherry picked from commit c6aa7f18e5)
This commit is contained in:
Alexey Ushakov
2024-08-30 12:20:19 +02:00
committed by jbrbot
parent 4e9b0f41f2
commit e94c7c9ab4
6 changed files with 164 additions and 60 deletions

View File

@@ -85,10 +85,13 @@
@property jboolean useMaskColor;
@property (readonly, strong) id<MTLDevice> device;
@property (readonly) NSString* shadersLib;
@property (strong) id<MTLCommandQueue> commandQueue;
@property (strong) id<MTLCommandQueue> blitCommandQueue;
@property (strong) id<MTLBuffer> vertexBuffer;
@property (readonly) NSMutableDictionary<NSNumber*, NSValue*>* displayLinks;
@property (readonly) EncoderManager * encoderManager;
@property (readonly) MTLSamplerManager * samplerManager;
@property (readonly) MTLStencilManager * stencilManager;
@@ -106,10 +109,13 @@
*/
+ (MTLContext*) setSurfacesEnv:(JNIEnv*)env src:(jlong)pSrc dst:(jlong)pDst;
- (id)initWithDevice:(jint)displayID shadersLib:(NSString*)shadersLib;
+ (NSMutableDictionary*) contextStore;
+ (MTLContext*) createContextWithDeviceIfAbsent:(jint)displayID shadersLib:(NSString*)mtlShadersLib;
- (id)initWithDevice:(id<MTLDevice>)device display:(jint) displayID shadersLib:(NSString*)mtlShadersLib;
- (void)dealloc;
- (void)handleDisplayLink: (BOOL)enabled source:(const char*)src;
- (void)handleDisplayLink:(BOOL)enabled display:(jint)display source:(const char*)src;
- (void)createDisplayLinkIfAbsent: (jint)displayID;
/**
* Resets the current clip state (disables both scissor and depth tests).

View File

@@ -72,7 +72,11 @@ static struct TxtVertex verts[PGRAM_VERTEX_COUNT] = {
{{-1.0, 1.0}, {0.0, 0.0}}
};
MTLTransform* tempTransform = nil;
typedef struct {
jint displayID;
CVDisplayLinkRef displayLink;
MTLContext* mtlc;
} DLParams;
@implementation MTLCommandBufferWrapper {
id<MTLCommandBuffer> _commandBuffer;
@@ -134,7 +138,6 @@ MTLTransform* tempTransform = nil;
@implementation MTLContext {
MTLCommandBufferWrapper * _commandBufferWrapper;
CVDisplayLinkRef _displayLink;
NSMutableSet* _layers;
int _displayLinkCount;
CFTimeInterval _lastRedrawTime;
@@ -153,34 +156,101 @@ MTLTransform* tempTransform = nil;
@synthesize textureFunction,
vertexCacheEnabled, aaEnabled, useMaskColor,
device, pipelineStateStorage,
device, shadersLib, pipelineStateStorage,
commandQueue, blitCommandQueue, vertexBuffer,
texturePool, paint=_paint, encoderManager=_encoderManager,
samplerManager=_samplerManager, stencilManager=_stencilManager;
extern void initSamplers(id<MTLDevice> device);
- (id)initWithDevice:(jint)displayID shadersLib:(NSString*)shadersLib {
+ (NSMutableDictionary*) contextStore {
static NSMutableDictionary<NSNumber*, MTLContext*> *_contextStore;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_contextStore = [[NSMutableDictionary alloc] init];
});
return _contextStore;
}
+ (MTLContext*) createContextWithDeviceIfAbsent:(jint)displayID shadersLib:(NSString*)mtlShadersLib {
// Initialization code here.
id<MTLDevice> device = CGDirectDisplayCopyCurrentMetalDevice(displayID);
if (device == nil) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "MTLContext_createContextWithDeviceIfAbsent(): Cannot create device from "
"displayID=%d", displayID)
// Fallback to the default metal device for main display
jint mainDisplayID = CGMainDisplayID();
if (displayID == mainDisplayID) {
device = MTLCreateSystemDefaultDevice();
}
if (device == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_createContextWithDeviceIfAbsent(): Cannot fallback to default "
"metal device")
return nil;
}
}
id<NSCopying> devID = nil;
if (@available(macOS 10.13, *)) {
devID = @(device.registryID);
} else {
devID = device.name;
}
MTLContext* mtlc = MTLContext.contextStore[devID];
if (mtlc == nil) {
mtlc = [[MTLContext alloc] initWithDevice:device display:displayID shadersLib:mtlShadersLib];
if (mtlc != nil) {
MTLContext.contextStore[devID] = mtlc;
[mtlc release];
J2dRlsTraceLn4(J2D_TRACE_INFO,
"MTLContext_createContextWithDeviceIfAbsent: new context(%p) for display=%d device=\"%s\" "
"retainCount=%d", mtlc, displayID, [mtlc.device.name UTF8String], mtlc.retainCount)
}
} else {
if (![mtlc.shadersLib isEqualToString:mtlShadersLib]) {
J2dRlsTraceLn3(J2D_TRACE_ERROR,
"MTLContext_createContextWithDeviceIfAbsent: cannot reuse context(%p) for display=%d "
"device=\"%s\", shaders lib has been changed", mtlc, displayID, [mtlc.device.name UTF8String])
return nil;
}
[mtlc retain];
J2dRlsTraceLn4(J2D_TRACE_INFO,
"MTLContext_createContextWithDeviceIfAbsent: reuse context(%p) for display=%d device=\"%s\" "
"retainCount=%d", mtlc, displayID, [mtlc.device.name UTF8String], mtlc.retainCount)
}
[mtlc createDisplayLinkIfAbsent:displayID];
return mtlc;
}
+ (void) releaseContext:(MTLContext*) mtlc {
id<NSCopying> devID = nil;
if (@available(macOS 10.13, *)) {
devID = @(mtlc.device.registryID);
} else {
devID = mtlc.device.name;
}
MTLContext* ctx = MTLContext.contextStore[devID];
if (mtlc == ctx) {
if (mtlc.retainCount > 1) {
[mtlc release];
J2dRlsTraceLn2(J2D_TRACE_INFO, "MTLContext_releaseContext: release context(%p) retainCount=%d", mtlc, mtlc.retainCount);
} else {
[MTLContext.contextStore removeObjectForKey:devID];
J2dRlsTraceLn1(J2D_TRACE_INFO, "MTLContext_releaseContext: dealloc context(%p)", mtlc);
}
}
}
- (id)initWithDevice:(id<MTLDevice>)mtlDevice display:(jint) displayID shadersLib:(NSString*)mtlShadersLib {
self = [super init];
if (self) {
// Initialization code here.
device = CGDirectDisplayCopyCurrentMetalDevice(displayID);
if (device == nil) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "MTLContext.initWithDevice(): Cannot create device from displayID=%d",
displayID);
// Fallback to the default metal device for main display
jint mainDisplayID = CGMainDisplayID();
if (displayID == mainDisplayID) {
device = MTLCreateSystemDefaultDevice();
}
if (device == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext.initWithDevice(): Cannot fallback to default metal device");
return nil;
}
}
device = mtlDevice;
shadersLib = [[NSString alloc] initWithString:mtlShadersLib];
pipelineStateStorage = [[MTLPipelineStatesStorage alloc] initWithDevice:device shaderLibPath:shadersLib];
if (pipelineStateStorage == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext.initWithDevice(): Failed to initialize MTLPipelineStatesStorage.");
@@ -213,25 +283,8 @@ extern void initSamplers(id<MTLDevice> device);
_displayLinkCount = 0;
_lastRedrawTime = 0.0;
if (isDisplaySyncEnabled()) {
_displayLinks = [[NSMutableDictionary alloc] init];
_layers = [[NSMutableSet alloc] init];
if (TRACE_CVLINK) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkCreateWithCGDisplay: "
"ctx=%p displayID=%d", self, displayID);
}
CHECK_CVLINK("CreateWithCGDisplay",
CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink));
if (_displayLink == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext.initWithDevice(): Failed to initialize CVDisplayLink.");
return nil;
} else {
CHECK_CVLINK("SetOutputCallback", CVDisplayLinkSetOutputCallback(
_displayLink,
&mtlDisplayLinkCallback,
(__bridge void *) self));
}
} else {
_displayLink = nil;
}
_glyphCacheLCD = [[MTLGlyphCache alloc] initWithContext:self];
_glyphCacheAA = [[MTLGlyphCache alloc] initWithContext:self];
@@ -239,13 +292,47 @@ extern void initSamplers(id<MTLDevice> device);
return self;
}
- (void)handleDisplayLink: (BOOL)enabled source:(const char*)src {
if (_displayLink == nil) {
- (void)createDisplayLinkIfAbsent: (jint)displayID {
if (isDisplaySyncEnabled() && _displayLinks[@(displayID)] == nil) {
CVDisplayLinkRef _displayLink;
if (TRACE_CVLINK) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_createDisplayLinkIfAbsent: "
"ctx=%p displayID=%d", self, displayID);
}
CHECK_CVLINK("CreateWithCGDisplay",
CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink));
if (_displayLink == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_createDisplayLinkIfAbsent: Failed to initialize CVDisplayLink.");
} else {
DLParams* dlParams = malloc(sizeof (DLParams ));
dlParams->displayID = displayID;
dlParams->displayLink = _displayLink;
dlParams->mtlc = self;
_displayLinks[@(displayID)] = [NSValue valueWithPointer:dlParams];
CHECK_CVLINK("SetOutputCallback", CVDisplayLinkSetOutputCallback(
_displayLink,
&mtlDisplayLinkCallback,
(__bridge DLParams*) dlParams));
}
}
}
- (void)handleDisplayLink: (BOOL)enabled display:(jint) display source:(const char*)src {
if (_displayLinks == nil) {
if (TRACE_CVLINK) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_handleDisplayLink[%s]: "
"ctx=%p - displayLink = nil", src, self);
"ctx=%p - displayLinks = nil", src, self);
}
} else {
NSValue* dlParamsVal = _displayLinks[@(display)];
if (dlParamsVal == nil) {
J2dRlsTraceLn3(J2D_TRACE_ERROR, "MTLContext_handleDisplayLink[%s]: "
"ctx=%p, no display link for %d ", src, self, display);
return;
}
DLParams *dlParams = [dlParamsVal pointerValue];
CVDisplayLinkRef _displayLink = dlParams->displayLink;
if (enabled) {
if (!CVDisplayLinkIsRunning(_displayLink)) {
CHECK_CVLINK("Start", CVDisplayLinkStart(_displayLink));
@@ -269,10 +356,11 @@ extern void initSamplers(id<MTLDevice> device);
- (void)dealloc {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.dealloc");
if (_displayLink != nil) {
if (_displayLinks != nil) {
[self haltRedraw];
}
[shadersLib release];
// TODO : Check that texturePool is completely released.
// texturePool content is released in MTLCommandBufferWrapper.onComplete()
//self.texturePool = nil;
@@ -627,7 +715,7 @@ extern void initSamplers(id<MTLDevice> device);
}
}
- (void) redraw {
- (void) redraw:(NSNumber*)displayIDNum {
AWT_ASSERT_APPKIT_THREAD;
/*
* Avoid repeated invocations by UIKit Main Thread
@@ -650,7 +738,7 @@ extern void initSamplers(id<MTLDevice> device);
if (_layers.count > 0) {
[_layers removeAllObjects];
}
[self handleDisplayLink:NO source:"redraw"];
[self handleDisplayLink:NO display:[displayIDNum integerValue] source:"redraw"];
}
}
@@ -658,8 +746,8 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
{
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_mtlDisplayLinkCallback: ctx=%p", displayLinkContext);
@autoreleasepool {
MTLContext *ctx = (__bridge MTLContext *)displayLinkContext;
[ThreadUtilities performOnMainThread:@selector(redraw) on:ctx withObject:nil waitUntilDone:NO];
DLParams* dlParams = (__bridge DLParams* *)displayLinkContext;
[ThreadUtilities performOnMainThread:@selector(redraw:) on:dlParams->mtlc withObject:@(dlParams->displayID) waitUntilDone:NO];
}
return kCVReturnSuccess;
}
@@ -674,25 +762,25 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
// Request for redraw before starting display link to avoid rendering problem on M2 processor
[layer setNeedsDisplay];
}
[self handleDisplayLink:YES source:"startRedraw"];
[self handleDisplayLink:YES display:layer.displayID source:"startRedraw"];
}
- (void)stopRedraw:(MTLLayer*) layer {
AWT_ASSERT_APPKIT_THREAD;
J2dTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_stopRedraw: ctx=%p layer=%p", self, layer);
if (_displayLink != nil) {
if (_displayLinks != nil) {
if (--layer.redrawCount <= 0) {
[_layers removeObject:layer];
layer.redrawCount = 0;
}
if ((_layers.count == 0) && (_displayLinkCount == 0)) {
[self handleDisplayLink:NO source:"stopRedraw"];
[self handleDisplayLink:NO display:layer.displayID source:"stopRedraw"];
}
}
}
- (void)haltRedraw {
if (_displayLink != nil) {
if (_displayLinks != nil) {
if (TRACE_CVLINK) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_haltRedraw: ctx=%p", self);
}
@@ -703,10 +791,16 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
[_layers removeAllObjects];
}
_displayLinkCount = 0;
[self handleDisplayLink:NO source:"haltRedraw"];
CVDisplayLinkRelease(_displayLink);
_displayLink = NULL;
NSEnumerator<NSNumber*>* keyEnum = _displayLinks.keyEnumerator;
NSNumber* displayIDVal;
while ((displayIDVal = [keyEnum nextObject])) {
[self handleDisplayLink:NO display:[displayIDVal integerValue] source:"haltRedraw"];
DLParams *dlParams = [(NSValue*)_displayLinks[displayIDVal] pointerValue];
CVDisplayLinkRelease(dlParams->displayLink);
free(dlParams);
}
[_displayLinks release];
_displayLinks = NULL;
}
}

View File

@@ -45,6 +45,7 @@
*/
typedef struct _MTLGraphicsConfigInfo {
MTLContext *context;
jint displayID;
} MTLGraphicsConfigInfo;
#endif /* MTLGraphicsConfig_h_Included */

View File

@@ -48,7 +48,7 @@ MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo)
mtlinfo->context = nil;
[ThreadUtilities performOnMainThreadWaiting:NO block:^() {
if (mtlc != NULL) {
[mtlc release];
[MTLContext releaseContext:mtlc];
}
free(mtlinfo);
}];
@@ -76,7 +76,7 @@ JNI_COCOA_ENTER(env);
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
MTLContext* mtlc = [[MTLContext alloc] initWithDevice:displayID
MTLContext* mtlc = [MTLContext createContextWithDeviceIfAbsent:displayID
shadersLib:path];
if (mtlc != 0L) {
// create the MTLGraphicsConfigInfo record for this context
@@ -84,6 +84,7 @@ JNI_COCOA_ENTER(env);
if (mtlinfo != NULL) {
memset(mtlinfo, 0, sizeof(MTLGraphicsConfigInfo));
mtlinfo->context = mtlc;
mtlinfo->displayID = displayID;
} else {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: could not allocate memory for mtlinfo");
[mtlc release];

View File

@@ -34,6 +34,7 @@
@property (nonatomic) jobject javaLayer;
@property (readwrite, assign) MTLContext* ctx;
@property (readwrite, assign) NSInteger displayID;
@property (readwrite, assign) id<MTLTexture>* buffer;
@property (readwrite, assign) id<MTLTexture>* outBuffer;
@property (readwrite, atomic) int nextDrawableCount;

View File

@@ -554,6 +554,7 @@ Java_sun_java2d_metal_MTLLayer_validate
layer.buffer = &bmtlsdo->pTexture;
layer.outBuffer = &bmtlsdo->pOutTexture;
layer.ctx = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->context;
layer.displayID = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->displayID;
layer.device = layer.ctx.device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;