Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/ghost/intern/GHOST_ContextCGL.mm')
-rw-r--r--intern/ghost/intern/GHOST_ContextCGL.mm809
1 files changed, 557 insertions, 252 deletions
diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm
index 488aa58aa59..9dad337a5d6 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.mm
+++ b/intern/ghost/intern/GHOST_ContextCGL.mm
@@ -46,8 +46,10 @@ int GHOST_ContextCGL::s_sharedCount = 0;
GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual,
NSView *metalView,
CAMetalLayer *metalLayer,
- NSOpenGLView *openGLView)
+ NSOpenGLView *openGLView,
+ GHOST_TDrawingContextType type)
: GHOST_Context(stereoVisual),
+ m_useMetalForRendering(type == GHOST_kDrawingContextTypeMetal),
m_metalView(metalView),
m_metalLayer(metalLayer),
m_metalCmdQueue(nil),
@@ -55,139 +57,277 @@ GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual,
m_openGLView(openGLView),
m_openGLContext(nil),
m_defaultFramebuffer(0),
- m_defaultFramebufferMetalTexture(nil),
m_debug(false)
{
+ /* Initialize Metal Swap-chain. */
+ current_swapchain_index = 0;
+ for (int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
+ m_defaultFramebufferMetalTexture[i].texture = nil;
+ m_defaultFramebufferMetalTexture[i].index = i;
+ }
if (m_metalView) {
+ m_ownsMetalDevice = false;
metalInit();
}
+ else {
+ /* Prepare offscreen GHOST Context Metal device. */
+ id<MTLDevice> metalDevice = MTLCreateSystemDefaultDevice();
+
+ if (m_debug) {
+ printf("Selected Metal Device: %s\n", [metalDevice.name UTF8String]);
+ }
+
+ m_ownsMetalDevice = true;
+ if (metalDevice) {
+ m_metalLayer = [[CAMetalLayer alloc] init];
+ [m_metalLayer setEdgeAntialiasingMask:0];
+ [m_metalLayer setMasksToBounds:NO];
+ [m_metalLayer setOpaque:YES];
+ [m_metalLayer setFramebufferOnly:YES];
+ [m_metalLayer setPresentsWithTransaction:NO];
+ [m_metalLayer removeAllAnimations];
+ [m_metalLayer setDevice:metalDevice];
+ m_metalLayer.allowsNextDrawableTimeout = NO;
+ metalInit();
+ }
+ else {
+ ghost_fatal_error_dialog(
+ "[ERROR] Failed to create Metal device for offscreen GHOST Context.\n");
+ }
+ }
+
+ /* Initialize swap-interval. */
+ mtl_SwapInterval = 60;
}
GHOST_ContextCGL::~GHOST_ContextCGL()
{
metalFree();
- if (m_openGLContext != nil) {
- if (m_openGLContext == [NSOpenGLContext currentContext]) {
- [NSOpenGLContext clearCurrentContext];
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ if (m_openGLContext != nil) {
+ if (m_openGLContext == [NSOpenGLContext currentContext]) {
+ [NSOpenGLContext clearCurrentContext];
- if (m_openGLView) {
- [m_openGLView clearGLContext];
+ if (m_openGLView) {
+ [m_openGLView clearGLContext];
+ }
}
- }
- if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) {
- assert(s_sharedCount > 0);
+ if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) {
+ assert(s_sharedCount > 0);
- s_sharedCount--;
+ s_sharedCount--;
- if (s_sharedCount == 0)
- s_sharedOpenGLContext = nil;
+ if (s_sharedCount == 0)
+ s_sharedOpenGLContext = nil;
- [m_openGLContext release];
+ [m_openGLContext release];
+ }
+ }
+#endif
+ }
+
+ if (m_ownsMetalDevice) {
+ if (m_metalLayer) {
+ [m_metalLayer release];
+ m_metalLayer = nil;
}
}
}
GHOST_TSuccess GHOST_ContextCGL::swapBuffers()
{
- if (m_openGLContext != nil) {
- if (m_metalView) {
- metalSwapBuffers();
+ GHOST_TSuccess return_value = GHOST_kFailure;
+
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ if (m_openGLContext != nil) {
+ if (m_metalView) {
+ metalSwapBuffers();
+ }
+ else if (m_openGLView) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [m_openGLContext flushBuffer];
+ [pool drain];
+ }
+ return_value = GHOST_kSuccess;
}
- else if (m_openGLView) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [m_openGLContext flushBuffer];
- [pool drain];
+ else {
+ return_value = GHOST_kFailure;
}
- return GHOST_kSuccess;
+#endif
}
else {
- return GHOST_kFailure;
+ if (m_metalView) {
+ metalSwapBuffers();
+ }
+ return_value = GHOST_kSuccess;
}
+ return return_value;
}
GHOST_TSuccess GHOST_ContextCGL::setSwapInterval(int interval)
{
- if (m_openGLContext != nil) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval];
- [pool drain];
- return GHOST_kSuccess;
+
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ if (m_openGLContext != nil) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval];
+ [pool drain];
+ return GHOST_kSuccess;
+ }
+ else {
+ return GHOST_kFailure;
+ }
+#endif
}
else {
- return GHOST_kFailure;
+ mtl_SwapInterval = interval;
+ return GHOST_kSuccess;
}
}
GHOST_TSuccess GHOST_ContextCGL::getSwapInterval(int &intervalOut)
{
- if (m_openGLContext != nil) {
- GLint interval;
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ if (m_openGLContext != nil) {
+ GLint interval;
- [m_openGLContext getValues:&interval forParameter:NSOpenGLCPSwapInterval];
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [pool drain];
+ [m_openGLContext getValues:&interval forParameter:NSOpenGLCPSwapInterval];
- intervalOut = static_cast<int>(interval);
+ [pool drain];
- return GHOST_kSuccess;
+ intervalOut = static_cast<int>(interval);
+
+ return GHOST_kSuccess;
+ }
+ else {
+ return GHOST_kFailure;
+ }
+#endif
}
else {
- return GHOST_kFailure;
+ intervalOut = mtl_SwapInterval;
+ return GHOST_kSuccess;
}
}
GHOST_TSuccess GHOST_ContextCGL::activateDrawingContext()
{
- if (m_openGLContext != nil) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [m_openGLContext makeCurrentContext];
- [pool drain];
- return GHOST_kSuccess;
+
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ if (m_openGLContext != nil) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [m_openGLContext makeCurrentContext];
+ [pool drain];
+ return GHOST_kSuccess;
+ }
+ else {
+ return GHOST_kFailure;
+ }
+#endif
}
else {
- return GHOST_kFailure;
+ return GHOST_kSuccess;
}
}
GHOST_TSuccess GHOST_ContextCGL::releaseDrawingContext()
{
- if (m_openGLContext != nil) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [NSOpenGLContext clearCurrentContext];
- [pool drain];
- return GHOST_kSuccess;
+
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ if (m_openGLContext != nil) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSOpenGLContext clearCurrentContext];
+ [pool drain];
+ return GHOST_kSuccess;
+ }
+ else {
+ return GHOST_kFailure;
+ }
+#endif
}
else {
- return GHOST_kFailure;
+ return GHOST_kSuccess;
}
}
unsigned int GHOST_ContextCGL::getDefaultFramebuffer()
{
- return m_defaultFramebuffer;
+
+ if (!m_useMetalForRendering) {
+ return m_defaultFramebuffer;
+ }
+ /* NOTE(Metal): This is not valid. */
+ return 0;
}
GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext()
{
- if (m_openGLContext != nil) {
- if (m_metalView) {
- metalUpdateFramebuffer();
+
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ if (m_openGLContext != nil) {
+ if (m_metalView) {
+ metalUpdateFramebuffer();
+ }
+ else if (m_openGLView) {
+ @autoreleasepool {
+ [m_openGLContext update];
+ }
+ }
+
+ return GHOST_kSuccess;
}
- else if (m_openGLView) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [m_openGLContext update];
- [pool drain];
+ else {
+ return GHOST_kFailure;
}
-
- return GHOST_kSuccess;
+#endif
}
else {
- return GHOST_kFailure;
+ if (m_metalView) {
+ metalUpdateFramebuffer();
+ return GHOST_kSuccess;
+ }
}
+ return GHOST_kFailure;
+}
+
+id<MTLTexture> GHOST_ContextCGL::metalOverlayTexture()
+{
+ /* Increment Swap-chain - Only needed if context is requesting a new texture */
+ current_swapchain_index = (current_swapchain_index + 1) % METAL_SWAPCHAIN_SIZE;
+
+ /* Ensure backing texture is ready for current swapchain index */
+ updateDrawingContext();
+
+ /* Return texture. */
+ return m_defaultFramebufferMetalTexture[current_swapchain_index].texture;
+}
+
+MTLCommandQueue *GHOST_ContextCGL::metalCommandQueue()
+{
+ return m_metalCmdQueue;
+}
+MTLDevice *GHOST_ContextCGL::metalDevice()
+{
+ id<MTLDevice> device = m_metalLayer.device;
+ return (MTLDevice *)device;
+}
+
+void GHOST_ContextCGL::metalRegisterPresentCallback(void (*callback)(
+ MTLRenderPassDescriptor *, id<MTLRenderPipelineState>, id<MTLTexture>, id<CAMetalDrawable>))
+{
+ this->contextPresentCallback = callback;
}
static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs,
@@ -241,120 +381,134 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
#endif
/* Command-line argument would be better. */
- static bool softwareGL = getenv("BLENDER_SOFTWAREGL");
-
- NSOpenGLPixelFormat *pixelFormat = nil;
- std::vector<NSOpenGLPixelFormatAttribute> attribs;
- bool increasedSamplerLimit = false;
-
- /* Attempt to initialize device with increased sampler limit.
- * If this is unsupported and initialization fails, initialize GL Context as normal.
- *
- * NOTE: This is not available when using the SoftwareGL path, or for Intel-based
- * platforms. */
- if (!softwareGL) {
- if (@available(macos 11.0, *)) {
- increasedSamplerLimit = true;
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ /* Command-line argument would be better. */
+ static bool softwareGL = getenv("BLENDER_SOFTWAREGL");
+
+ NSOpenGLPixelFormat *pixelFormat = nil;
+ std::vector<NSOpenGLPixelFormatAttribute> attribs;
+ bool increasedSamplerLimit = false;
+
+ /* Attempt to initialize device with increased sampler limit.
+ * If this is unsupported and initialization fails, initialize GL Context as normal.
+ *
+ * NOTE: This is not available when using the SoftwareGL path, or for Intel-based
+ * platforms. */
+ if (!softwareGL) {
+ if (@available(macos 11.0, *)) {
+ increasedSamplerLimit = true;
+ }
}
- }
- const int max_ctx_attempts = increasedSamplerLimit ? 2 : 1;
- for (int ctx_create_attempt = 0; ctx_create_attempt < max_ctx_attempts; ctx_create_attempt++) {
-
- attribs.clear();
- attribs.reserve(40);
- makeAttribList(attribs, m_stereoVisual, needAlpha, softwareGL, increasedSamplerLimit);
+ const int max_ctx_attempts = increasedSamplerLimit ? 2 : 1;
+ for (int ctx_create_attempt = 0; ctx_create_attempt < max_ctx_attempts;
+ ctx_create_attempt++) {
+
+ attribs.clear();
+ attribs.reserve(40);
+ makeAttribList(attribs, m_stereoVisual, needAlpha, softwareGL, increasedSamplerLimit);
+
+ pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
+ if (pixelFormat == nil) {
+ /* If pixel format creation fails when testing increased sampler limit,
+ * attempt initialization again with feature disabled, otherwise, fail. */
+ if (increasedSamplerLimit) {
+ increasedSamplerLimit = false;
+ continue;
+ }
+ return GHOST_kFailure;
+ }
- pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
- if (pixelFormat == nil) {
- /* If pixel format creation fails when testing increased sampler limit,
- * attempt initialization again with feature disabled, otherwise, fail. */
- if (increasedSamplerLimit) {
- increasedSamplerLimit = false;
- continue;
+ /* Attempt to create context. */
+ m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
+ shareContext:s_sharedOpenGLContext];
+ [pixelFormat release];
+
+ if (m_openGLContext == nil) {
+ /* If context creation fails when testing increased sampler limit,
+ * attempt re-creation with feature disabled. Otherwise, error. */
+ if (increasedSamplerLimit) {
+ increasedSamplerLimit = false;
+ continue;
+ }
+
+ /* Default context creation attempt failed. */
+ return GHOST_kFailure;
}
- return GHOST_kFailure;
- }
- /* Attempt to create context. */
- m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
- shareContext:s_sharedOpenGLContext];
- [pixelFormat release];
+ /* Created GL context successfully, activate. */
+ [m_openGLContext makeCurrentContext];
- if (m_openGLContext == nil) {
- /* If context creation fails when testing increased sampler limit,
- * attempt re-creation with feature disabled. Otherwise, error. */
+ /* When increasing sampler limit, verify created context is a supported configuration. */
if (increasedSamplerLimit) {
- increasedSamplerLimit = false;
- continue;
+ const char *vendor = (const char *)glGetString(GL_VENDOR);
+ const char *renderer = (const char *)glGetString(GL_RENDERER);
+
+ /* If generated context type is unsupported, release existing context and
+ * fallback to creating a normal context below. */
+ if (strstr(vendor, "Intel") || strstr(renderer, "Software")) {
+ [m_openGLContext release];
+ m_openGLContext = nil;
+ increasedSamplerLimit = false;
+ continue;
+ }
}
-
- /* Default context creation attempt failed. */
- return GHOST_kFailure;
}
- /* Created GL context successfully, activate. */
- [m_openGLContext makeCurrentContext];
+ if (m_debug) {
+ GLint major = 0, minor = 0;
+ glGetIntegerv(GL_MAJOR_VERSION, &major);
+ glGetIntegerv(GL_MINOR_VERSION, &minor);
+ fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : "");
+ fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER));
+ }
- /* When increasing sampler limit, verify created context is a supported configuration. */
- if (increasedSamplerLimit) {
- const char *vendor = (const char *)glGetString(GL_VENDOR);
- const char *renderer = (const char *)glGetString(GL_RENDERER);
-
- /* If generated context type is unsupported, release existing context and
- * fallback to creating a normal context below. */
- if (strstr(vendor, "Intel") || strstr(renderer, "Software")) {
- [m_openGLContext release];
- m_openGLContext = nil;
- increasedSamplerLimit = false;
- continue;
+# ifdef GHOST_WAIT_FOR_VSYNC
+ {
+ GLint swapInt = 1;
+ /* Wait for vertical-sync, to avoid tearing artifacts. */
+ [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+ }
+# endif
+
+ if (m_metalView) {
+ if (m_defaultFramebuffer == 0) {
+ /* Create a virtual frame-buffer. */
+ [m_openGLContext makeCurrentContext];
+ metalInitFramebuffer();
+ initClearGL();
}
}
- }
+ else if (m_openGLView) {
+ [m_openGLView setOpenGLContext:m_openGLContext];
+ [m_openGLContext setView:m_openGLView];
+ initClearGL();
+ }
- if (m_debug) {
- GLint major = 0, minor = 0;
- glGetIntegerv(GL_MAJOR_VERSION, &major);
- glGetIntegerv(GL_MINOR_VERSION, &minor);
- fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : "");
- fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER));
- }
+ [m_openGLContext flushBuffer];
-#ifdef GHOST_WAIT_FOR_VSYNC
- {
- GLint swapInt = 1;
- /* Wait for vertical-sync, to avoid tearing artifacts. */
- [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
- }
-#endif
+ if (s_sharedCount == 0)
+ s_sharedOpenGLContext = m_openGLContext;
- if (m_metalView) {
- if (m_defaultFramebuffer == 0) {
- /* Create a virtual frame-buffer. */
- [m_openGLContext makeCurrentContext];
+ s_sharedCount++;
+#endif
+ }
+ else {
+ /* NOTE(Metal): Metal-only path. */
+ if (m_metalView) {
metalInitFramebuffer();
- initClearGL();
}
}
- else if (m_openGLView) {
- [m_openGLView setOpenGLContext:m_openGLContext];
- [m_openGLContext setView:m_openGLView];
- initClearGL();
- }
-
- [m_openGLContext flushBuffer];
-
- if (s_sharedCount == 0)
- s_sharedOpenGLContext = m_openGLContext;
-
- s_sharedCount++;
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles()
{
+#if WITH_OPENGL
m_openGLContext = nil;
m_openGLView = nil;
+#endif
m_metalView = nil;
return GHOST_kSuccess;
@@ -404,10 +558,14 @@ void GHOST_ContextCGL::metalInit()
fragment float4 fragment_shader(Vertex v [[stage_in]],
texture2d<float> t [[texture(0)]]) {
- return t.sample(s, v.texCoord);
- }
- )msl";
+ /* Final blit should ensure alpha is 1.0. This resolves
+ * rendering artifacts for blitting of final backbuffer. */
+ float4 out_tex = t.sample(s, v.texCoord);
+ out_tex.a = 1.0;
+ return out_tex;
+ }
+ )msl";
MTLCompileOptions *options = [[[MTLCompileOptions alloc] init] autorelease];
options.languageVersion = MTLLanguageVersion1_1;
@@ -424,6 +582,8 @@ void GHOST_ContextCGL::metalInit()
desc.fragmentFunction = [library newFunctionWithName:@"fragment_shader"];
desc.vertexFunction = [library newFunctionWithName:@"vertex_shader"];
+ /* Ensure library is released. */
+ [library autorelease];
[desc.colorAttachments objectAtIndexedSubscript:0].pixelFormat = METAL_FRAMEBUFFERPIXEL_FORMAT;
@@ -434,6 +594,20 @@ void GHOST_ContextCGL::metalInit()
ghost_fatal_error_dialog(
"GHOST_ContextCGL::metalInit: newRenderPipelineStateWithDescriptor:error: failed!");
}
+
+ /* Create a render pipeline to composite things rendered with Metal on top
+ * of the frame-buffer contents. Uses the same vertex and fragment shader
+ * as the blit above, but with alpha blending enabled. */
+ desc.label = @"Metal Overlay";
+ desc.colorAttachments[0].blendingEnabled = YES;
+ desc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
+ desc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+
+ if (error) {
+ ghost_fatal_error_dialog(
+ "GHOST_ContextCGL::metalInit: newRenderPipelineStateWithDescriptor:error: failed (when "
+ "creating the Metal overlay pipeline)!");
+ }
}
}
@@ -445,123 +619,206 @@ void GHOST_ContextCGL::metalFree()
if (m_metalRenderPipeline) {
[m_metalRenderPipeline release];
}
- if (m_defaultFramebufferMetalTexture) {
- [m_defaultFramebufferMetalTexture release];
+
+ for (int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
+ if (m_defaultFramebufferMetalTexture[i].texture) {
+ [m_defaultFramebufferMetalTexture[i].texture release];
+ }
}
}
void GHOST_ContextCGL::metalInitFramebuffer()
{
- glGenFramebuffers(1, &m_defaultFramebuffer);
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ glGenFramebuffers(1, &m_defaultFramebuffer);
+#endif
+ }
updateDrawingContext();
- glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebuffer);
+
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebuffer);
+#endif
+ }
}
void GHOST_ContextCGL::metalUpdateFramebuffer()
{
- assert(m_defaultFramebuffer != 0);
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ assert(m_defaultFramebuffer != 0);
+#endif
+ }
NSRect bounds = [m_metalView bounds];
NSSize backingSize = [m_metalView convertSizeToBacking:bounds.size];
size_t width = (size_t)backingSize.width;
size_t height = (size_t)backingSize.height;
- {
- /* Test if there is anything to update */
- id<MTLTexture> tex = (id<MTLTexture>)m_defaultFramebufferMetalTexture;
- if (tex && tex.width == width && tex.height == height) {
- return;
+#if WITH_OPENGL
+ unsigned int glTex;
+ CVPixelBufferRef cvPixelBuffer = nil;
+ CVOpenGLTextureCacheRef cvGLTexCache = nil;
+ CVOpenGLTextureRef cvGLTex = nil;
+ CVMetalTextureCacheRef cvMetalTexCache = nil;
+ CVMetalTextureRef cvMetalTex = nil;
+#endif
+
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ /* OPENGL path */
+ {
+ /* Test if there is anything to update */
+ id<MTLTexture> tex = m_defaultFramebufferMetalTexture[current_swapchain_index].texture;
+ if (tex && tex.width == width && tex.height == height) {
+ return;
+ }
}
- }
- activateDrawingContext();
+ activateDrawingContext();
+
+ NSDictionary *cvPixelBufferProps = @{
+ (__bridge NSString *)kCVPixelBufferOpenGLCompatibilityKey : @YES,
+ (__bridge NSString *)kCVPixelBufferMetalCompatibilityKey : @YES,
+ };
+ CVReturn cvret = CVPixelBufferCreate(kCFAllocatorDefault,
+ width,
+ height,
+ METAL_CORE_VIDEO_PIXEL_FORMAT,
+ (__bridge CFDictionaryRef)cvPixelBufferProps,
+ &cvPixelBuffer);
+ if (cvret != kCVReturnSuccess) {
+ ghost_fatal_error_dialog(
+ "GHOST_ContextCGL::metalUpdateFramebuffer: CVPixelBufferCreate failed!");
+ }
- NSDictionary *cvPixelBufferProps = @{
- (__bridge NSString *)kCVPixelBufferOpenGLCompatibilityKey : @YES,
- (__bridge NSString *)kCVPixelBufferMetalCompatibilityKey : @YES,
- };
- CVPixelBufferRef cvPixelBuffer = nil;
- CVReturn cvret = CVPixelBufferCreate(kCFAllocatorDefault,
- width,
- height,
- METAL_CORE_VIDEO_PIXEL_FORMAT,
- (__bridge CFDictionaryRef)cvPixelBufferProps,
- &cvPixelBuffer);
- if (cvret != kCVReturnSuccess) {
- ghost_fatal_error_dialog(
- "GHOST_ContextCGL::metalUpdateFramebuffer: CVPixelBufferCreate failed!");
- }
-
- /* Create an OpenGL texture. */
- CVOpenGLTextureCacheRef cvGLTexCache = nil;
- cvret = CVOpenGLTextureCacheCreate(kCFAllocatorDefault,
- nil,
- m_openGLContext.CGLContextObj,
- m_openGLContext.pixelFormat.CGLPixelFormatObj,
- nil,
- &cvGLTexCache);
- if (cvret != kCVReturnSuccess) {
- ghost_fatal_error_dialog(
- "GHOST_ContextCGL::metalUpdateFramebuffer: CVOpenGLTextureCacheCreate failed!");
- }
+ /* Create an OpenGL texture. */
+ cvret = CVOpenGLTextureCacheCreate(kCFAllocatorDefault,
+ nil,
+ m_openGLContext.CGLContextObj,
+ m_openGLContext.pixelFormat.CGLPixelFormatObj,
+ nil,
+ &cvGLTexCache);
+ if (cvret != kCVReturnSuccess) {
+ ghost_fatal_error_dialog(
+ "GHOST_ContextCGL::metalUpdateFramebuffer: CVOpenGLTextureCacheCreate failed!");
+ }
- CVOpenGLTextureRef cvGLTex = nil;
- cvret = CVOpenGLTextureCacheCreateTextureFromImage(
- kCFAllocatorDefault, cvGLTexCache, cvPixelBuffer, nil, &cvGLTex);
- if (cvret != kCVReturnSuccess) {
- ghost_fatal_error_dialog(
- "GHOST_ContextCGL::metalUpdateFramebuffer: "
- "CVOpenGLTextureCacheCreateTextureFromImage failed!");
- }
+ cvret = CVOpenGLTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault, cvGLTexCache, cvPixelBuffer, nil, &cvGLTex);
+ if (cvret != kCVReturnSuccess) {
+ ghost_fatal_error_dialog(
+ "GHOST_ContextCGL::metalUpdateFramebuffer: "
+ "CVOpenGLTextureCacheCreateTextureFromImage failed!");
+ }
- unsigned int glTex;
- glTex = CVOpenGLTextureGetName(cvGLTex);
+ glTex = CVOpenGLTextureGetName(cvGLTex);
- /* Create a Metal texture. */
- CVMetalTextureCacheRef cvMetalTexCache = nil;
- cvret = CVMetalTextureCacheCreate(
- kCFAllocatorDefault, nil, m_metalLayer.device, nil, &cvMetalTexCache);
- if (cvret != kCVReturnSuccess) {
- ghost_fatal_error_dialog(
- "GHOST_ContextCGL::metalUpdateFramebuffer: CVMetalTextureCacheCreate failed!");
- }
+ /* Create a Metal texture. */
+ cvret = CVMetalTextureCacheCreate(
+ kCFAllocatorDefault, nil, m_metalLayer.device, nil, &cvMetalTexCache);
+ if (cvret != kCVReturnSuccess) {
+ ghost_fatal_error_dialog(
+ "GHOST_ContextCGL::metalUpdateFramebuffer: CVMetalTextureCacheCreate failed!");
+ }
- CVMetalTextureRef cvMetalTex = nil;
- cvret = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
- cvMetalTexCache,
- cvPixelBuffer,
- nil,
- METAL_FRAMEBUFFERPIXEL_FORMAT,
- width,
- height,
- 0,
- &cvMetalTex);
- if (cvret != kCVReturnSuccess) {
- ghost_fatal_error_dialog(
- "GHOST_ContextCGL::metalUpdateFramebuffer: "
- "CVMetalTextureCacheCreateTextureFromImage failed!");
- }
+ cvret = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
+ cvMetalTexCache,
+ cvPixelBuffer,
+ nil,
+ METAL_FRAMEBUFFERPIXEL_FORMAT,
+ width,
+ height,
+ 0,
+ &cvMetalTex);
+ if (cvret != kCVReturnSuccess) {
+ ghost_fatal_error_dialog(
+ "GHOST_ContextCGL::metalUpdateFramebuffer: "
+ "CVMetalTextureCacheCreateTextureFromImage failed!");
+ }
- MTLTexture *tex = (MTLTexture *)CVMetalTextureGetTexture(cvMetalTex);
+ id<MTLTexture> tex = CVMetalTextureGetTexture(cvMetalTex);
- if (!tex) {
- ghost_fatal_error_dialog(
- "GHOST_ContextCGL::metalUpdateFramebuffer: CVMetalTextureGetTexture failed!");
+ if (!tex) {
+ ghost_fatal_error_dialog(
+ "GHOST_ContextCGL::metalUpdateFramebuffer: CVMetalTextureGetTexture failed!");
+ }
+
+ [m_defaultFramebufferMetalTexture[current_swapchain_index].texture release];
+ m_defaultFramebufferMetalTexture[current_swapchain_index].texture = [tex retain];
+#endif
}
+ else {
+ /* NOTE(Metal): Metal API Path. */
+ if (m_defaultFramebufferMetalTexture[current_swapchain_index].texture &&
+ m_defaultFramebufferMetalTexture[current_swapchain_index].texture.width == width &&
+ m_defaultFramebufferMetalTexture[current_swapchain_index].texture.height == height) {
+ return;
+ }
- [m_defaultFramebufferMetalTexture release];
- m_defaultFramebufferMetalTexture = [tex retain];
+ /* Free old texture */
+ [m_defaultFramebufferMetalTexture[current_swapchain_index].texture release];
- glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebuffer);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, glTex, 0);
+ id<MTLDevice> device = m_metalLayer.device;
+ MTLTextureDescriptor *overlayDesc = [MTLTextureDescriptor
+ texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA16Float
+ width:width
+ height:height
+ mipmapped:NO];
+ overlayDesc.storageMode = MTLStorageModePrivate;
+ overlayDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
+
+ id<MTLTexture> overlayTex = [device newTextureWithDescriptor:overlayDesc];
+ if (!overlayTex) {
+ ghost_fatal_error_dialog(
+ "GHOST_ContextCGL::metalUpdateFramebuffer: failed to create Metal overlay texture!");
+ }
+ else {
+ overlayTex.label = [NSString
+ stringWithFormat:@"Metal Overlay for GHOST Context %p", this]; //@"";
- [m_metalLayer setDrawableSize:CGSizeMake((CGFloat)width, (CGFloat)height)];
+ // NSLog(@"Created new Metal Overlay (backbuffer) for context %p\n", this);
+ }
+
+ m_defaultFramebufferMetalTexture[current_swapchain_index].texture =
+ overlayTex; //[(MTLTexture *)overlayTex retain];
+
+ /* Clear texture on create */
+ id<MTLCommandBuffer> cmdBuffer = [m_metalCmdQueue commandBuffer];
+ MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
+ {
+ auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
+ attachment.texture = m_defaultFramebufferMetalTexture[current_swapchain_index].texture;
+ attachment.loadAction = MTLLoadActionClear;
+ attachment.clearColor = MTLClearColorMake(0.294, 0.294, 0.294, 1.000);
+ attachment.storeAction = MTLStoreActionStore;
+ }
+ {
+ id<MTLRenderCommandEncoder> enc = [cmdBuffer
+ renderCommandEncoderWithDescriptor:passDescriptor];
+ [enc endEncoding];
+ }
+ [cmdBuffer commit];
+ }
- CVPixelBufferRelease(cvPixelBuffer);
- CVOpenGLTextureCacheRelease(cvGLTexCache);
- CVOpenGLTextureRelease(cvGLTex);
- CFRelease(cvMetalTexCache);
- CFRelease(cvMetalTex);
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebuffer);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, glTex, 0);
+#endif
+ }
+
+ [m_metalLayer setDrawableSize:CGSizeMake((CGFloat)width, (CGFloat)height)];
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ CVPixelBufferRelease(cvPixelBuffer);
+ CVOpenGLTextureCacheRelease(cvGLTexCache);
+ CVOpenGLTextureRelease(cvGLTex);
+ CFRelease(cvMetalTexCache);
+ CFRelease(cvMetalTex);
+#endif
+ }
}
void GHOST_ContextCGL::metalSwapBuffers()
@@ -570,40 +827,88 @@ void GHOST_ContextCGL::metalSwapBuffers()
@autoreleasepool {
/* clang-format on */
updateDrawingContext();
- glFlush();
- assert(m_defaultFramebufferMetalTexture != 0);
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ glFlush();
+ assert(m_defaultFramebufferMetalTexture[current_swapchain_index].texture != nil);
+#endif
+ }
id<CAMetalDrawable> drawable = [m_metalLayer nextDrawable];
if (!drawable) {
return;
}
- id<MTLCommandBuffer> cmdBuffer = [m_metalCmdQueue commandBuffer];
-
MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
{
auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
attachment.texture = drawable.texture;
- attachment.loadAction = MTLLoadActionDontCare;
+ attachment.loadAction = MTLLoadActionClear;
+ attachment.clearColor = MTLClearColorMake(1.0, 0.294, 0.294, 1.000);
attachment.storeAction = MTLStoreActionStore;
}
- id<MTLTexture> srcTexture = (id<MTLTexture>)m_defaultFramebufferMetalTexture;
+ if (!m_useMetalForRendering) {
+ id<MTLCommandBuffer> cmdBuffer = [m_metalCmdQueue commandBuffer];
+ {
+ assert(m_defaultFramebufferMetalTexture[current_swapchain_index].texture != nil);
+ id<MTLRenderCommandEncoder> enc = [cmdBuffer
+ renderCommandEncoderWithDescriptor:passDescriptor];
+ [enc setRenderPipelineState:(id<MTLRenderPipelineState>)m_metalRenderPipeline];
+ [enc setFragmentTexture:m_defaultFramebufferMetalTexture[current_swapchain_index].texture
+ atIndex:0];
+ [enc drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
+ [enc endEncoding];
+ }
+
+ [cmdBuffer presentDrawable:drawable];
+ /* Submit command buffer */
+ [cmdBuffer commit];
+ }
+ else {
+ assert(contextPresentCallback);
+ assert(m_defaultFramebufferMetalTexture[current_swapchain_index].texture != nil);
+ (*contextPresentCallback)(passDescriptor,
+ (id<MTLRenderPipelineState>)m_metalRenderPipeline,
+ m_defaultFramebufferMetalTexture[current_swapchain_index].texture,
+ drawable);
+ }
+ }
+}
+
+void GHOST_ContextCGL::initClear()
+{
+
+ if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+ glClearColor(0.294, 0.294, 0.294, 0.000);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glClearColor(0.000, 0.000, 0.000, 0.000);
+#endif
+ }
+ else {
+#if WITH_METAL
+ // TODO (mg_gpusw_apple) this path is never taken, this is legacy left from inital integration
+ // of metal and gl, the whole file should be cleaned up and stripped of the legacy path
+ id<MTLCommandBuffer> cmdBuffer = [m_metalCmdQueue commandBuffer];
+ MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
+ {
+ auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
+ attachment.texture = m_defaultFramebufferMetalTexture[current_swapchain_index].texture;
+ attachment.loadAction = MTLLoadActionClear;
+ attachment.clearColor = MTLClearColorMake(0.294, 0.294, 0.294, 1.000);
+ attachment.storeAction = MTLStoreActionStore;
+ }
+
+ // encoding
{
id<MTLRenderCommandEncoder> enc = [cmdBuffer
renderCommandEncoderWithDescriptor:passDescriptor];
-
- [enc setRenderPipelineState:(id<MTLRenderPipelineState>)m_metalRenderPipeline];
- [enc setFragmentTexture:srcTexture atIndex:0];
- [enc drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
-
[enc endEncoding];
}
-
- [cmdBuffer presentDrawable:drawable];
-
[cmdBuffer commit];
+#endif
}
}