xref: /aosp_15_r20/external/mesa3d/src/vulkan/wsi/wsi_common_metal_layer.m (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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