14# pragma clang diagnostic ignored "-Wdeprecated-declarations"
19#import <Cocoa/Cocoa.h>
20#import <Metal/Metal.h>
21#import <QuartzCore/QuartzCore.h>
31 NSString *message = [NSString stringWithFormat:
@"Error opening window:\n%s", msg];
33 NSAlert *alert = [[NSAlert alloc]
init];
35 alert.messageText =
@"Blender";
36 alert.informativeText = message;
37 alert.alertStyle = NSAlertStyleCritical;
39 [alert addButtonWithTitle:
@"Quit"];
46MTLCommandQueue *GHOST_ContextCGL::s_sharedMetalCommandQueue = nil;
47int GHOST_ContextCGL::s_sharedCount = 0;
51 CAMetalLayer *metalLayer,
54 m_metalView(metalView),
55 m_metalLayer(metalLayer),
56 m_metalRenderPipeline(nil),
61 current_swapchain_index = 0;
62 for (
int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
63 m_defaultFramebufferMetalTexture[i].texture = nil;
64 m_defaultFramebufferMetalTexture[i].index = i;
68 m_ownsMetalDevice =
false;
73 id<MTLDevice>
metalDevice = MTLCreateSystemDefaultDevice();
79 m_ownsMetalDevice =
true;
81 m_metalLayer = [[CAMetalLayer alloc]
init];
82 m_metalLayer.edgeAntialiasingMask = 0;
83 m_metalLayer.masksToBounds = NO;
84 m_metalLayer.opaque = YES;
85 m_metalLayer.framebufferOnly = YES;
86 m_metalLayer.presentsWithTransaction = NO;
87 [m_metalLayer removeAllAnimations];
89 m_metalLayer.allowsNextDrawableTimeout = NO;
97 m_metalLayer.wantsExtendedDynamicRangeContent = YES;
99 const CFStringRef name = kCGColorSpaceExtendedSRGB;
100 CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(name);
101 m_metalLayer.colorspace = colorspace;
102 CGColorSpaceRelease(colorspace);
108 "[ERROR] Failed to create Metal device for offscreen GHOST Context.\n");
113 mtl_SwapInterval = 60;
121 if (m_ownsMetalDevice) {
123 [m_metalLayer release];
127 assert(s_sharedCount);
130 [s_sharedMetalCommandQueue release];
131 if (s_sharedCount == 0) {
132 s_sharedMetalCommandQueue = nil;
146 mtl_SwapInterval = interval;
152 intervalOut = mtl_SwapInterval;
175 metalUpdateFramebuffer();
184 current_swapchain_index = (current_swapchain_index + 1) % METAL_SWAPCHAIN_SIZE;
190 return m_defaultFramebufferMetalTexture[current_swapchain_index].texture;
195 return s_sharedMetalCommandQueue;
199 id<MTLDevice> device = m_metalLayer.device;
200 return (MTLDevice *)device;
204 MTLRenderPassDescriptor *, id<MTLRenderPipelineState>, id<MTLTexture>, id<CAMetalDrawable>))
206 this->contextPresentCallback =
callback;
213 metalInitFramebuffer();
226void GHOST_ContextCGL::metalInit()
229 id<MTLDevice> device = m_metalLayer.device;
234 if (s_sharedMetalCommandQueue == nil) {
235 s_sharedMetalCommandQueue = (MTLCommandQueue *)[device
239 [s_sharedMetalCommandQueue retain];
243 NSString *source = @R
"msl(
244 using namespace metal;
247 float4 position [[position]];
248 float2 texCoord [[attribute(0)]];
251 vertex Vertex vertex_shader(uint v_id [[vertex_id]]) {
254 vtx.position.x = float(v_id & 1) * 4.0 - 1.0;
255 vtx.position.y = float(v_id >> 1) * 4.0 - 1.0;
256 vtx.position.z = 0.0;
257 vtx.position.w = 1.0;
259 vtx.texCoord = vtx.position.xy * 0.5 + 0.5;
264 constexpr sampler s {};
266 fragment float4 fragment_shader(Vertex v [[stage_in]],
267 texture2d<float> t [[texture(0)]]) {
269 /* Final blit should ensure alpha is 1.0. This resolves
270 * rendering artifacts for blitting of final back-buffer. */
271 float4 out_tex = t.sample(s, v.texCoord);
277 MTLCompileOptions *options = [[[MTLCompileOptions alloc] init] autorelease];
278 options.languageVersion = MTLLanguageVersion1_1;
280 NSError *error = nil;
284 "GHOST_ContextCGL::metalInit: newLibraryWithSource:options:error: failed!");
288 MTLRenderPipelineDescriptor *desc = [[[MTLRenderPipelineDescriptor alloc]
init] autorelease];
290 desc.fragmentFunction = [library newFunctionWithName:
@"fragment_shader"];
291 desc.vertexFunction = [library newFunctionWithName:
@"vertex_shader"];
292 [desc.colorAttachments objectAtIndexedSubscript:0].pixelFormat =
296 [library autorelease];
298 m_metalRenderPipeline = (MTLRenderPipelineState *)[device
299 newRenderPipelineStateWithDescriptor:desc
303 "GHOST_ContextCGL::metalInit: newRenderPipelineStateWithDescriptor:error: failed!");
309 desc.label =
@"Metal Overlay";
310 desc.colorAttachments[0].blendingEnabled = YES;
311 desc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
312 desc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
316 "GHOST_ContextCGL::metalInit: newRenderPipelineStateWithDescriptor:error: failed (when "
317 "creating the Metal overlay pipeline)!");
320 [desc.fragmentFunction release];
321 [desc.vertexFunction release];
325void GHOST_ContextCGL::metalFree()
327 if (m_metalRenderPipeline) {
328 [m_metalRenderPipeline release];
329 m_metalRenderPipeline = nil;
332 for (
int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
333 if (m_defaultFramebufferMetalTexture[i].texture) {
334 [m_defaultFramebufferMetalTexture[i].texture release];
335 m_defaultFramebufferMetalTexture[i].texture = nil;
340void GHOST_ContextCGL::metalInitFramebuffer()
345void GHOST_ContextCGL::metalUpdateFramebuffer()
349 const NSSize backingSize = [m_metalView convertSizeToBacking:
bounds.size];
350 const size_t width = size_t(backingSize.width);
351 const size_t height = size_t(backingSize.height);
353 if (m_defaultFramebufferMetalTexture[current_swapchain_index].texture &&
354 m_defaultFramebufferMetalTexture[current_swapchain_index].texture.width == width &&
355 m_defaultFramebufferMetalTexture[current_swapchain_index].texture.height == height)
361 [m_defaultFramebufferMetalTexture[current_swapchain_index].texture release];
363 id<MTLDevice> device = m_metalLayer.device;
364 MTLTextureDescriptor *overlayDesc = [MTLTextureDescriptor
369 overlayDesc.storageMode = MTLStorageModePrivate;
370 overlayDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
372 id<MTLTexture> overlayTex = [device newTextureWithDescriptor:overlayDesc];
375 "GHOST_ContextCGL::metalUpdateFramebuffer: failed to create Metal overlay texture!");
378 overlayTex.label = [NSString
379 stringWithFormat:
@"Metal Overlay for GHOST Context %p",
this];
382 m_defaultFramebufferMetalTexture[current_swapchain_index].texture = overlayTex;
385 id<MTLCommandBuffer> cmdBuffer = [s_sharedMetalCommandQueue commandBuffer];
386 MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
388 auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
389 attachment.texture = m_defaultFramebufferMetalTexture[current_swapchain_index].texture;
390 attachment.loadAction = MTLLoadActionClear;
391 attachment.clearColor = MTLClearColorMake(0.294, 0.294, 0.294, 1.000);
392 attachment.storeAction = MTLStoreActionStore;
395 id<MTLRenderCommandEncoder> enc = [cmdBuffer
396 renderCommandEncoderWithDescriptor:passDescriptor];
401 m_metalLayer.drawableSize = CGSizeMake(CGFloat(width), CGFloat(height));
405void GHOST_ContextCGL::metalSwapBuffers()
410 id<CAMetalDrawable> drawable = [m_metalLayer nextDrawable];
415 MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
417 auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
418 attachment.texture = drawable.texture;
419 attachment.loadAction = MTLLoadActionClear;
420 attachment.clearColor = MTLClearColorMake(1.0, 0.294, 0.294, 1.000);
421 attachment.storeAction = MTLStoreActionStore;
424 assert(contextPresentCallback);
425 assert(m_defaultFramebufferMetalTexture[current_swapchain_index].texture != nil);
426 (*contextPresentCallback)(passDescriptor,
427 (id<MTLRenderPipelineState>)m_metalRenderPipeline,
428 m_defaultFramebufferMetalTexture[current_swapchain_index].texture,
static const MTLPixelFormat METAL_FRAMEBUFFERPIXEL_FORMAT_EDR
static void ghost_fatal_error_dialog(const char *msg)
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
void metalRegisterPresentCallback(void(*callback)(MTLRenderPassDescriptor *, id< MTLRenderPipelineState >, id< MTLTexture >, id< CAMetalDrawable >))
GHOST_TSuccess updateDrawingContext() override
id< MTLTexture > metalOverlayTexture()
GHOST_TSuccess swapBuffers() override
GHOST_TSuccess activateDrawingContext() override
GHOST_TSuccess getSwapInterval(int &intervalOut) override
MTLDevice * metalDevice()
GHOST_ContextCGL(bool stereoVisual, NSView *metalView, CAMetalLayer *metalLayer, int debug)
GHOST_TSuccess releaseDrawingContext() override
static const int max_command_buffer_count
GHOST_TSuccess initializeDrawingContext() override
unsigned int getDefaultFramebuffer() override
MTLCommandQueue * metalCommandQueue()
GHOST_TSuccess releaseNativeHandles() override
GHOST_TSuccess setSwapInterval(int interval) override
~GHOST_ContextCGL() override
CCL_NAMESPACE_BEGIN struct Options options
DEGForeachIDComponentCallback callback
static void error(const char *str)