From 14b1dc0fdf56ac01e3db334cc07ddd2e6f9deb1b Mon Sep 17 00:00:00 2001 From: Gregory Wells Date: Mon, 4 Aug 2025 09:24:12 -0400 Subject: [PATCH] redo metal presentation --- .../metal/src/devices/metal_output_device.m | 55 +++++++++++++++++++ .../metal/src/devices/metal_output_devices.h | 5 ++ .../apis/metal/src/devices/metal_shader.msl | 26 +++++++++ .../metal_graphics_pipeline.m | 5 +- .../apis/metal/src/present/metal_present.m | 42 +++++++------- 5 files changed, 111 insertions(+), 22 deletions(-) create mode 100644 projects/apis/metal/src/devices/metal_shader.msl diff --git a/projects/apis/metal/src/devices/metal_output_device.m b/projects/apis/metal/src/devices/metal_output_device.m index 329e054..ab369bc 100644 --- a/projects/apis/metal/src/devices/metal_output_device.m +++ b/projects/apis/metal/src/devices/metal_output_device.m @@ -4,12 +4,60 @@ #include "instance/metal_instance.h" #include "instance/gryphn_instance.h" +#include "metal_shader.msl" + gnReturnCode createMetalOutputDevice(gnInstanceHandle instance, gnOutputDeviceHandle outputDevice, gnOutputDeviceInfo deviceInfo) { if (instance == GN_NULL_HANDLE) return GN_INVALID_HANDLE; outputDevice->outputDevice = malloc(sizeof(gnPlatformOutputDevice)); outputDevice->outputDevice->device = deviceInfo.physicalDevice->physicalDevice->device.retain; outputDevice->outputDevice->transferQueue = outputDevice->outputDevice->device.newCommandQueue; + + // create full screen quad + float verticies[] = { + -1.0f, -1.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, + }; + outputDevice->outputDevice->fullScreenQuadBuffer = + [outputDevice->outputDevice->device + newBufferWithBytes:verticies + length:sizeof(verticies) + options:MTLResourceStorageModeShared + ]; + + NSError *error = nil; + outputDevice->outputDevice->fullScreenShader = [outputDevice->outputDevice->device + newLibraryWithSource:@(shader_source) + options:nil + error:&error + ]; + if (!outputDevice->outputDevice->fullScreenShader) { + gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ + .message = gnCombineStrings(gnCreateString("Failed to create shader for output device: \n"), error.localizedDescription.UTF8String) + }); + return GN_DEVICE_LOST; + } + + outputDevice->outputDevice->fullScreenVertex = [outputDevice->outputDevice->fullScreenShader newFunctionWithName:@"fullscreen_vertex"]; + outputDevice->outputDevice->fullScreenFragment = [outputDevice->outputDevice->fullScreenShader newFunctionWithName:@"fullscreen_fragment"]; + + MTLRenderPipelineDescriptor *pipelineDesc = [[MTLRenderPipelineDescriptor alloc] init]; + pipelineDesc.vertexFunction = outputDevice->outputDevice->fullScreenVertex; + pipelineDesc.fragmentFunction = outputDevice->outputDevice->fullScreenFragment; + pipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; + pipelineDesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid; + pipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid; + + outputDevice->outputDevice->fullScreenPipeline = [outputDevice->outputDevice->device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error]; + if (!outputDevice->outputDevice->fullScreenPipeline) { + gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ + .message = gnCombineStrings(gnCreateString("Failed to create graphics pipeline for output device: \n"), error.localizedDescription.UTF8String) + }); + return GN_DEVICE_LOST; + } + return GN_SUCCESS; } @@ -20,6 +68,13 @@ void waitForMetalDevice(gnOutputDeviceHandle device) { void destroyMetalOutputDevice(gnOutputDeviceHandle device) { [device->outputDevice->transferQueue release]; [device->outputDevice->device release]; + + [device->outputDevice->fullScreenQuadBuffer release]; + [device->outputDevice->fullScreenShader release]; + [device->outputDevice->fullScreenVertex release]; + [device->outputDevice->fullScreenFragment release]; + [device->outputDevice->fullScreenPipeline release]; + free(device->outputDevice); } diff --git a/projects/apis/metal/src/devices/metal_output_devices.h b/projects/apis/metal/src/devices/metal_output_devices.h index f1a91f2..8e95e6d 100644 --- a/projects/apis/metal/src/devices/metal_output_devices.h +++ b/projects/apis/metal/src/devices/metal_output_devices.h @@ -14,6 +14,11 @@ struct gnPlatformOutputDevice_t { id executingCommandBuffer; id transferQueue; + + id fullScreenQuadBuffer; + id fullScreenShader; + id fullScreenVertex, fullScreenFragment; + id fullScreenPipeline; } gnPlatformOutputDevice; gnPhysicalDevice* getMetalDevices(gnInstanceHandle instance, uint32_t* deviceCount); diff --git a/projects/apis/metal/src/devices/metal_shader.msl b/projects/apis/metal/src/devices/metal_shader.msl new file mode 100644 index 0000000..f300caf --- /dev/null +++ b/projects/apis/metal/src/devices/metal_shader.msl @@ -0,0 +1,26 @@ +const char* shader_source = +"using namespace metal;" +"" +"struct Vertex {" +" float2 position;" +" float2 texcoord;" +"};" +"" +"struct VertexOut {" +" float4 position [[position]];" +" float2 texcoord;" +"};" +"" +"vertex VertexOut fullscreen_vertex(const device Vertex* vertices [[buffer(0)]]," +" uint vid [[vertex_id]]) {" +" VertexOut out;" +" out.position = float4(vertices[vid].position, 0.0, 1.0);" +" out.texcoord = vertices[vid].texcoord;" +" return out;" +"}" +"" +"fragment float4 fullscreen_fragment(VertexOut in [[stage_in]]," +" texture2d tex [[texture(0)]]) {" +" constexpr sampler s(address::clamp_to_edge, filter::linear);" +" return tex.sample(s, in.texcoord);" +"}"; diff --git a/projects/apis/metal/src/pipelines/graphics_pipeline/metal_graphics_pipeline.m b/projects/apis/metal/src/pipelines/graphics_pipeline/metal_graphics_pipeline.m index 079901d..1f2fe13 100644 --- a/projects/apis/metal/src/pipelines/graphics_pipeline/metal_graphics_pipeline.m +++ b/projects/apis/metal/src/pipelines/graphics_pipeline/metal_graphics_pipeline.m @@ -65,8 +65,9 @@ gnReturnCode createMetalGraphicsPipeline(gnGraphicsPipeline graphicsPipeline, gn if (subpass.depthAttachment != NULL) { descriptor.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8; - descriptor.depthAttachmentPixelFormat = info.renderPassDescriptor->renderPassDescriptor->subpasses[info.subpassIndex].depthAttachment.texture.pixelFormat; - descriptor.stencilAttachmentPixelFormat = info.renderPassDescriptor->renderPassDescriptor->subpasses[info.subpassIndex].stencilAttachment.texture.pixelFormat; + descriptor.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + // descriptor.depthAttachmentPixelFormat = info.renderPassDescriptor->renderPassDescriptor->subpasses[info.subpassIndex].depthAttachment.texture.pixelFormat; + // descriptor.stencilAttachmentPixelFormat = info.renderPassDescriptor->renderPassDescriptor->subpasses[info.subpassIndex].stencilAttachment.texture.pixelFormat; } } diff --git a/projects/apis/metal/src/present/metal_present.m b/projects/apis/metal/src/present/metal_present.m index 06d00a4..1f800a1 100644 --- a/projects/apis/metal/src/present/metal_present.m +++ b/projects/apis/metal/src/present/metal_present.m @@ -53,6 +53,7 @@ gnReturnCode metalPresent(gnOutputDeviceHandle device, gnPresentInfo info) { gnReturnCode metalPresentSync(gnOutputDeviceHandle device, gnPresentSyncInfo info) { for (uint32_t i = 0; i < info.presentationQueueCount; i++) { if (info.presentationQueues[i]->info.surface->windowSurface->layer.device == nil) info.presentationQueues[i]->info.surface->windowSurface->layer.device = device->outputDevice->device; + info.presentationQueues[i]->info.surface->windowSurface->layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; id drawable = [info.presentationQueues[i]->info.surface->windowSurface->layer nextDrawable]; if (drawable == nil) return GN_UNKNOWN_ERROR; @@ -68,29 +69,30 @@ gnReturnCode metalPresentSync(gnOutputDeviceHandle device, gnPresentSyncInfo inf mtlAddImageBackToQueue(presentationQueue, imageIndex); }]; - id blit = [commandBuffer blitCommandEncoder]; - [blit copyFromTexture:info.presentationQueues[i]->images[info.imageIndices[i]]->texture->texture - sourceSlice:0 - sourceLevel:0 - sourceOrigin:(MTLOrigin){0, 0, 0} - sourceSize:(MTLSize){info.presentationQueues[i]->info.imageSize.x, info.presentationQueues[i]->info.imageSize.y, 1} - toTexture:drawable.texture - destinationSlice:0 - destinationLevel:0 - destinationOrigin:(MTLOrigin){0, 0, 0}]; + MTLRenderPassDescriptor *desc = [MTLRenderPassDescriptor renderPassDescriptor]; + desc.colorAttachments[0].texture = drawable.texture; + desc.colorAttachments[0].loadAction = MTLLoadActionClear; + desc.colorAttachments[0].storeAction = MTLStoreActionStore; + desc.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); - [blit endEncoding]; + id enc = [commandBuffer renderCommandEncoderWithDescriptor:desc]; - [commandBuffer presentDrawable:drawable]; - [commandBuffer commit]; - device->outputDevice->executingCommandBuffer = commandBuffer; + [enc setRenderPipelineState:device->outputDevice->fullScreenPipeline]; + [enc setVertexBuffer:device->outputDevice->fullScreenQuadBuffer offset:0 atIndex:0]; + [enc setFragmentTexture:info.presentationQueues[i]->images[info.imageIndices[i]]->texture->texture atIndex:0]; + [enc drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + [enc endEncoding]; + + [commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; + device->outputDevice->executingCommandBuffer = commandBuffer; + } + + for (uint32_t i = 0; i < info.presentationQueueCount; i++) { + if (info.presentationQueues[i]->info.imageSize.x != info.presentationQueues[i]->info.surface->windowSurface->layer.drawableSize.width || + info.presentationQueues[i]->info.imageSize.y != info.presentationQueues[i]->info.surface->windowSurface->layer.drawableSize.height) { + return GN_SUBOPTIMAL_PRESENTATION_QUEUE; } - - for (uint32_t i = 0; i < info.presentationQueueCount; i++) { - if (info.presentationQueues[i]->info.imageSize.x != info.presentationQueues[i]->info.surface->windowSurface->layer.drawableSize.width || - info.presentationQueues[i]->info.imageSize.y != info.presentationQueues[i]->info.surface->windowSurface->layer.drawableSize.height) { - return GN_SUBOPTIMAL_PRESENTATION_QUEUE; - } } return GN_SUCCESS;