redo metal presentation

This commit is contained in:
Gregory Wells
2025-08-04 09:24:12 -04:00
parent 80fbb3e691
commit 14b1dc0fdf
5 changed files with 111 additions and 22 deletions

View File

@@ -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);
}

View File

@@ -14,6 +14,11 @@ struct gnPlatformOutputDevice_t {
id<MTLCommandBuffer> executingCommandBuffer;
id<MTLCommandQueue> transferQueue;
id<MTLBuffer> fullScreenQuadBuffer;
id<MTLLibrary> fullScreenShader;
id<MTLFunction> fullScreenVertex, fullScreenFragment;
id<MTLRenderPipelineState> fullScreenPipeline;
} gnPlatformOutputDevice;
gnPhysicalDevice* getMetalDevices(gnInstanceHandle instance, uint32_t* deviceCount);

View File

@@ -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<float> tex [[texture(0)]]) {"
" constexpr sampler s(address::clamp_to_edge, filter::linear);"
" return tex.sample(s, in.texcoord);"
"}";

View File

@@ -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;
}
}

View File

@@ -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<CAMetalDrawable> 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<MTLBlitCommandEncoder> 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<MTLRenderCommandEncoder> 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;