1/* 2 * Copyright 2024 Autodesk, Inc. 3 * 4 * SPDX-License-Identifier: MIT 5 */ 6 7#include "wsi_common_metal_layer.h" 8 9#import <QuartzCore/CAMetalLayer.h> 10#import <Metal/Metal.h> 11 12void 13wsi_metal_layer_size(const CAMetalLayer *metal_layer, 14 uint32_t *width, uint32_t *height) 15{ 16 @autoreleasepool { 17 CGSize size = [metal_layer drawableSize]; 18 if (width) 19 *width = size.width; 20 if (height) 21 *height = size.height; 22 } 23} 24 25void 26wsi_metal_layer_configure(const CAMetalLayer *metal_layer, 27 uint32_t width, uint32_t height, uint32_t image_count, 28 MTLPixelFormat format, bool enable_opaque, bool enable_immediate) 29{ 30 @autoreleasepool { 31 if (metal_layer.device == nil) { 32 metal_layer.device = metal_layer.preferredDevice; 33 } 34 35 /* So acquire timeout works */ 36 metal_layer.allowsNextDrawableTimeout = YES; 37 /* So we can blit to the drawable */ 38 metal_layer.framebufferOnly = NO; 39 40 metal_layer.maximumDrawableCount = image_count; 41 metal_layer.drawableSize = (CGSize){.width = width, .height = height}; 42 metal_layer.pixelFormat = format; 43 metal_layer.opaque = enable_opaque; 44 metal_layer.displaySyncEnabled = !enable_immediate; 45 } 46} 47 48CAMetalDrawableBridged * 49wsi_metal_layer_acquire_drawable(const CAMetalLayer *metal_layer) 50{ 51 @autoreleasepool { 52 id<CAMetalDrawable> drawable = [metal_layer nextDrawable]; 53 return (__bridge_retained CAMetalDrawableBridged *)drawable; 54 } 55} 56 57struct wsi_metal_layer_blit_context { 58 id<MTLDevice> device; 59 id<MTLCommandQueue> commandQueue; 60}; 61 62struct wsi_metal_layer_blit_context * 63wsi_create_metal_layer_blit_context() 64{ 65 @autoreleasepool { 66 struct wsi_metal_layer_blit_context *context = malloc(sizeof(struct wsi_metal_layer_blit_context)); 67 memset((void*)context, 0, sizeof(*context)); 68 69 context->device = MTLCreateSystemDefaultDevice(); 70 context->commandQueue = [context->device newCommandQueue]; 71 72 return context; 73 } 74} 75 76void 77wsi_destroy_metal_layer_blit_context(struct wsi_metal_layer_blit_context *context) 78{ 79 @autoreleasepool { 80 context->device = nil; 81 context->commandQueue = nil; 82 free(context); 83 } 84} 85 86void 87wsi_metal_layer_blit_and_present(struct wsi_metal_layer_blit_context *context, 88 CAMetalDrawableBridged **drawable_ptr, void *buffer, 89 uint32_t width, uint32_t height, uint32_t row_pitch) 90{ 91 @autoreleasepool { 92 id<CAMetalDrawable> drawable = (__bridge_transfer id<CAMetalDrawable>)*drawable_ptr; 93 94 id<MTLCommandBuffer> commandBuffer = [context->commandQueue commandBuffer]; 95 id<MTLBlitCommandEncoder> commandEncoder = [commandBuffer blitCommandEncoder]; 96 97 NSUInteger image_size = height * row_pitch; 98 id<MTLBuffer> image_buffer = [context->device newBufferWithBytesNoCopy:buffer 99 length:image_size 100 options:MTLResourceStorageModeShared 101 deallocator:nil]; 102 103 [commandEncoder copyFromBuffer:image_buffer 104 sourceOffset:0 105 sourceBytesPerRow:row_pitch 106 sourceBytesPerImage:image_size 107 sourceSize:MTLSizeMake(width, height, 1) 108 toTexture:drawable.texture 109 destinationSlice:0 110 destinationLevel:0 111 destinationOrigin:MTLOriginMake(0, 0, 0)]; 112 [commandEncoder endEncoding]; 113 [commandBuffer presentDrawable:drawable]; 114 [commandBuffer commit]; 115 116 *drawable_ptr = nil; 117 } 118} 119 120void 121wsi_metal_layer_cancel_present(struct wsi_metal_layer_blit_context *context, 122 CAMetalDrawableBridged **drawable_ptr) 123{ 124 @autoreleasepool { 125 id<CAMetalDrawable> drawable = (__bridge_transfer id<CAMetalDrawable>)*drawable_ptr; 126 if (drawable == nil) 127 return; 128 129 /* We need to present the drawable to release it... */ 130 id<MTLCommandBuffer> commandBuffer = [context->commandQueue commandBuffer]; 131 [commandBuffer presentDrawable:drawable]; 132 [commandBuffer commit]; 133 134 *drawable_ptr = nil; 135 } 136} 137