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
path: root/intern
diff options
context:
space:
mode:
authorTomoaki Kawada <i@yvt.jp>2019-05-24 19:38:20 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2019-06-02 13:54:46 +0300
commit99de160340c7ae08ac5abed12a541017e4846d87 (patch)
tree544bfa7cdd985d112e0ae59e369eb3e672421572 /intern
parent1418eee5f5c84d3ec24d326952e57e248e27ef96 (diff)
macOS: fix viewport lagging, by using CAMetalLayer instead of NSOpenGLView
On GPUs that support it, we now present OpenGL contents via CAMetalLayer. This fixes frame skipping issues found in T60043. If the system does not have a Metal capable GPU, NSOpenGLView will continue to be used. Patch by Tomoaki Kawada, with some changes by Brecht Van Lommel. Differential Revision: https://developer.blender.org/D4619
Diffstat (limited to 'intern')
-rw-r--r--intern/ghost/intern/GHOST_ContextCGL.h35
-rw-r--r--intern/ghost/intern/GHOST_ContextCGL.mm327
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.h10
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm78
-rw-r--r--intern/ghost/intern/GHOST_WindowViewCocoa.h11
6 files changed, 429 insertions, 34 deletions
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