diff options
-rw-r--r-- | build_files/cmake/platform/platform_apple.cmake | 2 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_ContextCGL.h | 35 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_ContextCGL.mm | 327 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemCocoa.mm | 2 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_WindowCocoa.h | 10 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_WindowCocoa.mm | 78 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_WindowViewCocoa.h | 11 |
7 files changed, 430 insertions, 35 deletions
diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index 3366d49868d..d7c4370cde7 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -181,7 +181,7 @@ endif() set(PLATFORM_CFLAGS "-pipe -funsigned-char") set(PLATFORM_LINKFLAGS - "-fexceptions -framework CoreServices -framework Foundation -framework IOKit -framework AppKit -framework Cocoa -framework Carbon -framework AudioUnit -framework AudioToolbox -framework CoreAudio" + "-fexceptions -framework CoreServices -framework Foundation -framework IOKit -framework AppKit -framework Cocoa -framework Carbon -framework AudioUnit -framework AudioToolbox -framework CoreAudio -framework Metal -framework QuartzCore" ) list(APPEND PLATFORM_LINKLIBS c++) diff --git a/intern/ghost/intern/GHOST_ContextCGL.h b/intern/ghost/intern/GHOST_ContextCGL.h index b808effc795..37c1ac34299 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.h +++ b/intern/ghost/intern/GHOST_ContextCGL.h @@ -26,15 +26,23 @@ #include "GHOST_Context.h" -@class NSOpenGLView; +@class CAMetalLayer; +@class MTLCommandQueue; +@class MTLRenderPipelineState; +@class MTLTexture; @class NSOpenGLContext; +@class NSOpenGLView; +@class NSView; class GHOST_ContextCGL : public GHOST_Context { public: /** * Constructor. */ - GHOST_ContextCGL(bool stereoVisual, NSOpenGLView *openGLView); + GHOST_ContextCGL(bool stereoVisual, + NSView *metalView, + CAMetalLayer *metalLayer, + NSOpenGLView *openglView); /** * Destructor. @@ -59,6 +67,8 @@ class GHOST_ContextCGL : public GHOST_Context { */ GHOST_TSuccess releaseDrawingContext(); + unsigned int getDefaultFramebuffer(); + /** * Call immediately after new to initialize. If this fails then immediately delete the object. * \return Indication as to whether initialization has succeeded. @@ -94,12 +104,24 @@ class GHOST_ContextCGL : public GHOST_Context { GHOST_TSuccess updateDrawingContext(); private: - /** The openGL view */ + /** Metal state */ + NSView *m_metalView; + CAMetalLayer *m_metalLayer; + MTLCommandQueue *m_metalCmdQueue; + MTLRenderPipelineState *m_metalRenderPipeline; + + /** OpenGL state, for GPUs that don't support Metal */ NSOpenGLView *m_openGLView; /** The OpenGL drawing context */ NSOpenGLContext *m_openGLContext; + /** The virtualized default framebuffer */ + unsigned int m_defaultFramebuffer; + + /** The virtualized default framebuffer's texture */ + MTLTexture *m_defaultFramebufferMetalTexture; + bool m_coreProfile; const bool m_debug; @@ -107,6 +129,13 @@ class GHOST_ContextCGL : public GHOST_Context { /** The first created OpenGL context (for sharing display lists) */ static NSOpenGLContext *s_sharedOpenGLContext; static int s_sharedCount; + + /* Metal functions */ + void metalInit(); + void metalFree(); + void metalInitFramebuffer(); + void metalUpdateFramebuffer(); + void metalSwapBuffers(); }; #endif // __GHOST_CONTEXTCGL_H__ diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm index 5fe40cb04a0..12c340ffe97 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.mm +++ b/intern/ghost/intern/GHOST_ContextCGL.mm @@ -26,25 +26,61 @@ #include "GHOST_ContextCGL.h" #include <Cocoa/Cocoa.h> +#include <QuartzCore/QuartzCore.h> +#include <Metal/Metal.h> #include <vector> #include <cassert> +static void ghost_fatal_error_dialog(const char *msg) +{ + @autoreleasepool { + NSString *message = [NSString stringWithFormat:@"Error opening window:\n%s", msg]; + + NSAlert *alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"Quit"]; + [alert setMessageText:@"Blender"]; + [alert setInformativeText:message]; + [alert setAlertStyle:NSAlertStyleCritical]; + [alert runModal]; + } + + exit(1); +} + NSOpenGLContext *GHOST_ContextCGL::s_sharedOpenGLContext = nil; int GHOST_ContextCGL::s_sharedCount = 0; -GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual, NSOpenGLView *openGLView) - : GHOST_Context(stereoVisual), m_openGLView(openGLView), m_openGLContext(nil), m_debug(false) +GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual, + NSView *metalView, + CAMetalLayer *metalLayer, + NSOpenGLView *openGLView) + : GHOST_Context(stereoVisual), + m_metalView(metalView), + m_metalLayer(metalLayer), + m_metalCmdQueue(nil), + m_metalRenderPipeline(nil), + m_openGLView(openGLView), + m_openGLContext(nil), + m_defaultFramebuffer(0), + m_defaultFramebufferMetalTexture(nil), + m_debug(false) { #if defined(WITH_GL_PROFILE_CORE) m_coreProfile = true; #else m_coreProfile = false; #endif + + if (m_metalView) { + metalInit(); + } } GHOST_ContextCGL::~GHOST_ContextCGL() { + metalFree(); + if (m_openGLContext != nil) { if (m_openGLContext == [NSOpenGLContext currentContext]) { [NSOpenGLContext clearCurrentContext]; @@ -70,9 +106,14 @@ GHOST_ContextCGL::~GHOST_ContextCGL() GHOST_TSuccess GHOST_ContextCGL::swapBuffers() { if (m_openGLContext != nil) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [m_openGLContext flushBuffer]; - [pool drain]; + if (m_metalView) { + metalSwapBuffers(); + } + else if (m_openGLView) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [m_openGLContext flushBuffer]; + [pool drain]; + } return GHOST_kSuccess; } else { @@ -139,12 +180,23 @@ GHOST_TSuccess GHOST_ContextCGL::releaseDrawingContext() } } +unsigned int GHOST_ContextCGL::getDefaultFramebuffer() +{ + return m_defaultFramebuffer; +} + GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext() { if (m_openGLContext != nil) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [m_openGLContext update]; - [pool drain]; + if (m_metalView) { + metalUpdateFramebuffer(); + } + else if (m_openGLView) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [m_openGLContext update]; + [pool drain]; + } + return GHOST_kSuccess; } else { @@ -233,7 +285,15 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext() initContextGLEW(); - if (m_openGLView) { + if (m_metalView) { + if (m_defaultFramebuffer == 0) { + // Create a virtual framebuffer + [m_openGLContext makeCurrentContext]; + metalInitFramebuffer(); + initClearGL(); + } + } + else if (m_openGLView) { [m_openGLView setOpenGLContext:m_openGLContext]; [m_openGLContext setView:m_openGLView]; initClearGL(); @@ -261,8 +321,253 @@ error: GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles() { - m_openGLContext = NULL; - m_openGLView = NULL; + m_openGLContext = nil; + m_openGLView = nil; + m_metalView = nil; return GHOST_kSuccess; } + +/* OpenGL on Metal + * + * Use Metal layer to avoid Viewport lagging on macOS, see T60043. */ + +static const MTLPixelFormat METAL_FRAMEBUFFERPIXEL_FORMAT = MTLPixelFormatBGRA8Unorm; +static const OSType METAL_CORE_VIDEO_PIXEL_FORMAT = kCVPixelFormatType_32BGRA; + +void GHOST_ContextCGL::metalInit() +{ + @autoreleasepool { + id<MTLDevice> device = m_metalLayer.device; + + // Create a command queue for blit/present operation + m_metalCmdQueue = (MTLCommandQueue *)[device newCommandQueue]; + [m_metalCmdQueue retain]; + + // Create shaders for blit operation + NSString *source = @R"msl( + using namespace metal; + + struct Vertex { + float4 position [[position]]; + float2 texCoord [[attribute(0)]]; + }; + + vertex Vertex vertex_shader(uint v_id [[vertex_id]]) { + Vertex vtx; + + vtx.position.x = float(v_id & 1) * 4.0 - 1.0; + vtx.position.y = float(v_id >> 1) * 4.0 - 1.0; + vtx.position.z = 0.0; + vtx.position.w = 1.0; + + vtx.texCoord = vtx.position.xy * 0.5 + 0.5; + + return vtx; + } + + constexpr sampler s {}; + + fragment float4 fragment_shader(Vertex v [[stage_in]], + texture2d<float> t [[texture(0)]]) { + return t.sample(s, v.texCoord); + } + + )msl"; + + MTLCompileOptions *options = [[[MTLCompileOptions alloc] init] autorelease]; + options.languageVersion = MTLLanguageVersion1_1; + + NSError *error = nil; + id<MTLLibrary> library = [device newLibraryWithSource:source options:options error:&error]; + if (error) { + ghost_fatal_error_dialog( + "GHOST_ContextCGL::metalInit: newLibraryWithSource:options:error: failed!"); + } + + // Create a render pipeline for blit operation + MTLRenderPipelineDescriptor *desc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease]; + + desc.fragmentFunction = [library newFunctionWithName:@"fragment_shader"]; + desc.vertexFunction = [library newFunctionWithName:@"vertex_shader"]; + + [desc.colorAttachments objectAtIndexedSubscript:0].pixelFormat = METAL_FRAMEBUFFERPIXEL_FORMAT; + + m_metalRenderPipeline = (MTLRenderPipelineState *)[device + newRenderPipelineStateWithDescriptor:desc + error:&error]; + if (error) { + ghost_fatal_error_dialog( + "GHOST_ContextCGL::metalInit: newRenderPipelineStateWithDescriptor:error: failed!"); + } + } +} + +void GHOST_ContextCGL::metalFree() +{ + if (m_metalCmdQueue) { + [m_metalCmdQueue release]; + } + if (m_metalRenderPipeline) { + [m_metalRenderPipeline release]; + } + if (m_defaultFramebufferMetalTexture) { + [m_defaultFramebufferMetalTexture release]; + } +} + +void GHOST_ContextCGL::metalInitFramebuffer() +{ + glGenFramebuffers(1, &m_defaultFramebuffer); + updateDrawingContext(); + glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebuffer); +} + +void GHOST_ContextCGL::metalUpdateFramebuffer() +{ + assert(m_defaultFramebuffer != 0); + + 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; + } + } + + activateDrawingContext(); + + 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!"); + } + + CVOpenGLTextureRef cvGLTex = nil; + 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); + + // 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!"); + } + + 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!"); + } + + MTLTexture *tex = (MTLTexture *)CVMetalTextureGetTexture(cvMetalTex); + + if (!tex) { + ghost_fatal_error_dialog( + "GHOST_ContextCGL::metalUpdateFramebuffer: CVMetalTextureGetTexture failed!"); + } + + [m_defaultFramebufferMetalTexture release]; + m_defaultFramebufferMetalTexture = [tex retain]; + + glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, glTex, 0); + + [m_metalLayer setDrawableSize:CGSizeMake((CGFloat)width, (CGFloat)height)]; + + CVPixelBufferRelease(cvPixelBuffer); + CVOpenGLTextureCacheRelease(cvGLTexCache); + CVOpenGLTextureRelease(cvGLTex); + CFRelease(cvMetalTexCache); + CFRelease(cvMetalTex); +} + +void GHOST_ContextCGL::metalSwapBuffers() +{ + @autoreleasepool { + updateDrawingContext(); + glFlush(); + + assert(m_defaultFramebufferMetalTexture != 0); + + 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.storeAction = MTLStoreActionStore; + } + + id<MTLTexture> srcTexture = (id<MTLTexture>)m_defaultFramebufferMetalTexture; + + { + 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]; + } +} diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index c80424b279b..376ebfa2a21 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -762,7 +762,7 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const STR_String &title, */ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext() { - GHOST_Context *context = new GHOST_ContextCGL(false, NULL); + GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL); if (context->initializeDrawingContext()) return context; else diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h index 41e7191701e..5300b07523c 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.h +++ b/intern/ghost/intern/GHOST_WindowCocoa.h @@ -32,8 +32,10 @@ #include "GHOST_Window.h" #include "STR_String.h" -@class CocoaWindow; +@class CAMetalLayer; +@class CocoaMetalView; @class CocoaOpenGLView; +@class CocoaWindow; @class NSCursor; @class NSScreen; @@ -313,11 +315,13 @@ class GHOST_WindowCocoa : public GHOST_Window { int hotX, int hotY); - /** The window containing the OpenGL view */ + /** The window containing the view */ CocoaWindow *m_window; - /** The openGL view */ + /** The view, either Metal or OpenGL */ CocoaOpenGLView *m_openGLView; + CocoaMetalView *m_metalView; + CAMetalLayer *m_metalLayer; /** The mother SystemCocoa class to send events */ GHOST_SystemCocoa *m_systemCocoa; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index 33d9921cdec..45ea3c85005 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -29,6 +29,8 @@ #endif #include <Cocoa/Cocoa.h> +#include <QuartzCore/QuartzCore.h> +#include <Metal/Metal.h> #include <sys/sysctl.h> @@ -263,7 +265,17 @@ @end /* NSView for handling input and drawing. */ +#define COCOA_VIEW_CLASS CocoaOpenGLView +#define COCOA_VIEW_BASE_CLASS NSOpenGLView #include "GHOST_WindowViewCocoa.h" +#undef COCOA_VIEW_CLASS +#undef COCOA_VIEW_BASE_CLASS + +#define COCOA_VIEW_CLASS CocoaMetalView +#define COCOA_VIEW_BASE_CLASS NSView +#include "GHOST_WindowViewCocoa.h" +#undef COCOA_VIEW_CLASS +#undef COCOA_VIEW_BASE_CLASS #pragma mark initialization / finalization @@ -281,6 +293,8 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, bool is_debug) : GHOST_Window(width, height, state, stereoVisual, false), m_openGLView(nil), + m_metalView(nil), + m_metalLayer(nil), m_systemCocoa(systemCocoa), m_customCursor(0), m_immediateDraw(false), @@ -318,21 +332,44 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, minSize.height = 240; [m_window setContentMinSize:minSize]; - // Creates the OpenGL View inside the window - m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect]; + // Create NSView inside the window + id<MTLDevice> metalDevice = MTLCreateSystemDefaultDevice(); + NSView *view; + + if (metalDevice) { + // Create metal layer and view if supported + 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_metalView = [[CocoaMetalView alloc] initWithFrame:rect]; + [m_metalView setWantsLayer:YES]; + [m_metalView setLayer:m_metalLayer]; + [m_metalView setSystemAndWindowCocoa:systemCocoa windowCocoa:this]; + view = m_metalView; + } + else { + // Fallback to OpenGL view if there is no Metal support + m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect]; + [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this]; + view = m_openGLView; + } if (m_systemCocoa->m_nativePixel) { // Needs to happen early when building with the 10.14 SDK, otherwise // has no effect until resizeing the window. - if ([m_openGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) { - [m_openGLView setWantsBestResolutionOpenGLSurface:YES]; + if ([view respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) { + [view setWantsBestResolutionOpenGLSurface:YES]; } } - [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this]; - - [m_window setContentView:m_openGLView]; - [m_window setInitialFirstResponder:m_openGLView]; + [m_window setContentView:view]; + [m_window setInitialFirstResponder:view]; [m_window makeKeyAndOrderFront:nil]; @@ -381,7 +418,18 @@ GHOST_WindowCocoa::~GHOST_WindowCocoa() releaseNativeHandles(); - [m_openGLView release]; + if (m_openGLView) { + [m_openGLView release]; + m_openGLView = nil; + } + if (m_metalView) { + [m_metalView release]; + m_metalView = nil; + } + if (m_metalLayer) { + [m_metalLayer release]; + m_metalLayer = nil; + } if (m_window) { [m_window close]; @@ -405,7 +453,8 @@ GHOST_WindowCocoa::~GHOST_WindowCocoa() bool GHOST_WindowCocoa::getValid() const { - return GHOST_Window::getValid() && m_window != NULL && m_openGLView != NULL; + NSView *view = (m_openGLView) ? m_openGLView : m_metalView; + return GHOST_Window::getValid() && m_window != NULL && view != NULL; } void *GHOST_WindowCocoa::getOSWindow() const @@ -669,7 +718,8 @@ NSScreen *GHOST_WindowCocoa::getScreen() /* called for event, when window leaves monitor to another */ void GHOST_WindowCocoa::setNativePixelSize(void) { - NSRect backingBounds = [m_openGLView convertRectToBacking:[m_openGLView bounds]]; + NSView *view = (m_openGLView) ? m_openGLView : m_metalView; + NSRect backingBounds = [view convertRectToBacking:[view bounds]]; GHOST_Rect rect; getClientBounds(rect); @@ -763,7 +813,8 @@ GHOST_Context *GHOST_WindowCocoa::newDrawingContext(GHOST_TDrawingContextType ty { if (type == GHOST_kDrawingContextTypeOpenGL) { - GHOST_Context *context = new GHOST_ContextCGL(m_wantStereoVisual, m_openGLView); + GHOST_Context *context = new GHOST_ContextCGL( + m_wantStereoVisual, m_metalView, m_metalLayer, m_openGLView); if (context->initializeDrawingContext()) return context; @@ -780,7 +831,8 @@ GHOST_TSuccess GHOST_WindowCocoa::invalidate() { GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid"); NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [m_openGLView setNeedsDisplay:YES]; + NSView *view = (m_openGLView) ? m_openGLView : m_metalView; + [view setNeedsDisplay:YES]; [pool drain]; return GHOST_kSuccess; } diff --git a/intern/ghost/intern/GHOST_WindowViewCocoa.h b/intern/ghost/intern/GHOST_WindowViewCocoa.h index 601c5fbdead..9ed339c9992 100644 --- a/intern/ghost/intern/GHOST_WindowViewCocoa.h +++ b/intern/ghost/intern/GHOST_WindowViewCocoa.h @@ -17,10 +17,15 @@ * All rights reserved. */ -/* NSView subclass for drawing and handling input. */ +/* NSView subclass for drawing and handling input. + * + * COCOA_VIEW_BASE_CLASS will be either NSView or NSOpenGLView depending if + * we use a Metal or OpenGL layer for drawing in the view. We use macros + * to defined classes for each case, so we don't have to duplicate code as + * Objective-C does not have multiple inheritance. */ // We need to subclass it in order to give Cocoa the feeling key events are trapped -@interface CocoaOpenGLView : NSOpenGLView <NSTextInput> +@interface COCOA_VIEW_CLASS : COCOA_VIEW_BASE_CLASS <NSTextInput> { GHOST_SystemCocoa *systemCocoa; GHOST_WindowCocoa *associatedWindow; @@ -34,7 +39,7 @@ windowCocoa:(GHOST_WindowCocoa *)winCocoa; @end -@implementation CocoaOpenGLView +@implementation COCOA_VIEW_CLASS - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa |