From 102da0f90256a1821ab81105d8d85d60ac6030be Mon Sep 17 00:00:00 2001 From: Greg Wells Date: Wed, 28 May 2025 07:11:30 -0400 Subject: [PATCH] compiling metal shader modules --- .../src/core/devices/metal_output_device.m | 134 +++++++++--------- .../src/core/devices/metal_physical_device.m | 1 + .../metal_presentation_queue.m | 7 + .../core/shader_module/metal_shader_module.h | 7 + .../core/shader_module/metal_shader_module.m | 87 ++++++++++++ .../metal/src/core/surface/metal_surface.m | 4 + src/utils/gryphn_error_code.h | 6 +- 7 files changed, 178 insertions(+), 68 deletions(-) create mode 100644 rendering_api/metal/src/core/shader_module/metal_shader_module.h create mode 100644 rendering_api/metal/src/core/shader_module/metal_shader_module.m diff --git a/rendering_api/metal/src/core/devices/metal_output_device.m b/rendering_api/metal/src/core/devices/metal_output_device.m index 5338df9..da63168 100644 --- a/rendering_api/metal/src/core/devices/metal_output_device.m +++ b/rendering_api/metal/src/core/devices/metal_output_device.m @@ -14,78 +14,78 @@ gnReturnCode gnCreateOutputDeviceFn(gnOutputDevice* outputDevice, gnInstance* in outputDevice->outputDevice->queues[i] = outputDevice->outputDevice->device.newCommandQueue; } - { + // { + // NSError* error = nil; + // MTLCompileOptions* options = nil; + // NSString *shaderSource = @"#include \ + // using namespace metal;\ + // struct VertexOut {\ + // float4 position [[position]];\ + // float2 uv;\ + // };\ + // vertex VertexOut vs_main(uint vertexID [[vertex_id]]) {\ + // float2 positions[4] = {\ + // {-1.0, -1.0},\ + // { 1.0, -1.0},\ + // {-1.0, 1.0},\ + // { 1.0, 1.0}\ + // };\ + // float2 uvs[4] = {\ + // {0.0, 1.0},\ + // {1.0, 1.0},\ + // {0.0, 0.0},\ + // {1.0, 0.0}\ + // };\ + // VertexOut out;\ + // out.position = float4(positions[vertexID], 0.0, 1.0);\ + // out.uv = uvs[vertexID];\ + // return out;\ + // }\ + // fragment float4 fs_main(VertexOut in [[stage_in]],\ + // texture2d colorTex [[texture(0)]],\ + // sampler samp [[sampler(0)]]) {\ + // return colorTex.sample(samp, in.uv);\ + // }\ + // "; + // id library = [outputDevice->outputDevice->device newLibraryWithSource:shaderSource options:nil error:&error]; - NSError* error = nil; - MTLCompileOptions* options = nil; - NSString *shaderSource = @"#include \ - using namespace metal;\ - struct VertexOut {\ - float4 position [[position]];\ - float2 uv;\ - };\ - vertex VertexOut vs_main(uint vertexID [[vertex_id]]) {\ - float2 positions[4] = {\ - {-1.0, -1.0},\ - { 1.0, -1.0},\ - {-1.0, 1.0},\ - { 1.0, 1.0}\ - };\ - float2 uvs[4] = {\ - {0.0, 1.0},\ - {1.0, 1.0},\ - {0.0, 0.0},\ - {1.0, 0.0}\ - };\ - VertexOut out;\ - out.position = float4(positions[vertexID], 0.0, 1.0);\ - out.uv = uvs[vertexID];\ - return out;\ - }\ - fragment float4 fs_main(VertexOut in [[stage_in]],\ - texture2d colorTex [[texture(0)]],\ - sampler samp [[sampler(0)]]) {\ - return colorTex.sample(samp, in.uv);\ - }\ - "; - id library = [outputDevice->outputDevice->device newLibraryWithSource:shaderSource options:nil error:&error]; - if (!library) { - gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ - .message = gnCreateString("Failed to compile framebuffer shader") - }); - return GN_FAILED_TO_CREATE_DEVICE; - } - // id vs = library->newFunction(NS::String::string("vs_main", NS::UTF8StringEncoding)); - id vs = [library newFunctionWithName:@"vs_main"]; - id fs = [library newFunctionWithName:@"fs_main"]; + // if (!library) { + // gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ + // .message = gnCreateString("Failed to compile framebuffer shader") + // }); + // return GN_FAILED_TO_CREATE_DEVICE; + // } + // // id vs = library->newFunction(NS::String::string("vs_main", NS::UTF8StringEncoding)); + // id vs = [library newFunctionWithName:@"vs_main"]; + // id fs = [library newFunctionWithName:@"fs_main"]; - if (vs == nil) { - gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ - .message = gnCreateString("Failed to load frambuffer vertex shader") - }); - return GN_FAILED_TO_CREATE_DEVICE; - } + // if (vs == nil) { + // gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ + // .message = gnCreateString("Failed to compile frambuffer vertex shader") + // }); + // return GN_FAILED_TO_CREATE_DEVICE; + // } - if (fs == nil) { - gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ - .message = gnCreateString("Failed to load frambuffer fragment shader") - }); - return GN_FAILED_TO_CREATE_DEVICE; - } + // if (fs == nil) { + // gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ + // .message = gnCreateString("Failed to compile frambuffer fragment shader") + // }); + // return GN_FAILED_TO_CREATE_DEVICE; + // } - MTLRenderPipelineDescriptor* pipelineDesc =[[MTLRenderPipelineDescriptor alloc] init];; - pipelineDesc.vertexFunction = vs; - pipelineDesc.fragmentFunction = fs; - pipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; + // MTLRenderPipelineDescriptor* pipelineDesc =[[MTLRenderPipelineDescriptor alloc] init];; + // pipelineDesc.vertexFunction = vs; + // pipelineDesc.fragmentFunction = fs; + // pipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; - outputDevice->outputDevice->framebuffer = [outputDevice->outputDevice->device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error]; - if (!outputDevice->outputDevice->framebuffer) { - gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ - .message = gnCreateString("Failed to create frambuffer render pipeline") - }); - return GN_FAILED_TO_CREATE_DEVICE; - } - } + // outputDevice->outputDevice->framebuffer = [outputDevice->outputDevice->device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error]; + // if (!outputDevice->outputDevice->framebuffer) { + // gnDebuggerSetErrorMessage(instance->debugger, (gnMessageData){ + // .message = gnCreateString("Failed to create frambuffer render pipeline") + // }); + // return GN_FAILED_TO_CREATE_DEVICE; + // } + // } return GN_SUCCESS; } diff --git a/rendering_api/metal/src/core/devices/metal_physical_device.m b/rendering_api/metal/src/core/devices/metal_physical_device.m index ae5f26c..d108d88 100644 --- a/rendering_api/metal/src/core/devices/metal_physical_device.m +++ b/rendering_api/metal/src/core/devices/metal_physical_device.m @@ -23,6 +23,7 @@ gnPhysicalDevice* gnGetPhysicalDevicesFn(gnInstance* instance, uint32_t* deviceC devicesList[i].properties.deviceType = GN_EXTERNAL_DEVICE; // below I am going to fake that there is one queue that can support graphics, compute, and transfer queues + devicesList[i].queueProperties.queueCount = 1; devicesList[i].queueProperties.queueProperties = malloc(sizeof(gnQueueProperties)); devicesList[i].queueProperties.queueProperties[0] = (gnQueueProperties){ .queueCount = 1, diff --git a/rendering_api/metal/src/core/presentation_queue/metal_presentation_queue.m b/rendering_api/metal/src/core/presentation_queue/metal_presentation_queue.m index f80f945..442ee9c 100644 --- a/rendering_api/metal/src/core/presentation_queue/metal_presentation_queue.m +++ b/rendering_api/metal/src/core/presentation_queue/metal_presentation_queue.m @@ -40,3 +40,10 @@ gnReturnCode gnCreatePresentationQueueFn(gnPresentationQueue* presentationQueue, return GN_SUCCESS; } + +void gnDestroyPresentationQueueFn(gnPresentationQueue *presentationQueue) { + for (int i = 0; i < presentationQueue->imageCount; i++) { + [presentationQueue->presentationQueue->textures[i] release]; + } + free(presentationQueue->presentationQueue); +} diff --git a/rendering_api/metal/src/core/shader_module/metal_shader_module.h b/rendering_api/metal/src/core/shader_module/metal_shader_module.h new file mode 100644 index 0000000..20691b7 --- /dev/null +++ b/rendering_api/metal/src/core/shader_module/metal_shader_module.h @@ -0,0 +1,7 @@ +#pragma once +#include "core/shader_module/gryphn_shader_module.h" +#import + +typedef struct gnPlatformShaderModule_t { + id function; +} gnPlatformShaderModule; diff --git a/rendering_api/metal/src/core/shader_module/metal_shader_module.m b/rendering_api/metal/src/core/shader_module/metal_shader_module.m new file mode 100644 index 0000000..a58e7da --- /dev/null +++ b/rendering_api/metal/src/core/shader_module/metal_shader_module.m @@ -0,0 +1,87 @@ +#include "metal_shader_module.h" +#include "spirv_cross_c.h" +#include "core/debugger/gryphn_debugger.h" +#include "core/devices/metal_output_devices.h" +#import +#import + +void mtlSpirVErrorCallback(void *userdata, const char *error) { + struct gnDebugger_t* debugger = (struct gnDebugger_t*)userdata; + gnDebuggerSetErrorMessage(debugger, (gnMessageData){ + .message = gnCreateString(error) + }); +} + +gnReturnCode gnCreateShaderModuleFn(struct gnShaderModule_t *module, struct gnOutputDevice_t *device, struct gnShaderModuleInfo_t shaderModuleInfo) { + module->shaderModule = malloc(sizeof(struct gnPlatformShaderModule_t)); + + spvc_context context = NULL; + spvc_parsed_ir ir = NULL; + spvc_compiler compiler = NULL; + const char *result = NULL; + spvc_resources resources = NULL; + const spvc_reflected_resource *list = NULL; + spvc_compiler_options options = NULL; + size_t count; + + spvc_context_create(&context); + spvc_context_set_error_callback(context, mtlSpirVErrorCallback, module->device->instance->debugger); + spvc_context_parse_spirv(context, shaderModuleInfo.code, shaderModuleInfo.size / 4, &ir); + spvc_context_create_compiler(context, SPVC_BACKEND_MSL, ir, SPVC_CAPTURE_MODE_COPY, &compiler); + + spvc_compiler_create_compiler_options(compiler, &options); + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_MSL_VERSION, 200); + spvc_compiler_install_compiler_options(compiler, options); + + SpvExecutionModel executionModel = SpvExecutionModelVertex; + if (shaderModuleInfo.stage == GN_FRAGMENT_SHADER_MODULE) executionModel = SpvExecutionModelFragment; + + spvc_compiler_set_entry_point(compiler, shaderModuleInfo.entryPoint.value, executionModel); + spvc_result res = spvc_compiler_compile(compiler, &result); + if (res != SPVC_SUCCESS) + return GN_FAILED_TO_CONVERT_SHADER_CODE; + // this is where to use result + + NSError* error = nil; + MTLCompileOptions* mtloptions = nil; + NSString* sourceCode = [NSString stringWithCString:result encoding:NSUTF8StringEncoding]; + id shaderLib = [device->outputDevice->device newLibraryWithSource:sourceCode options:mtloptions error:&error]; + if (!shaderLib) { + const char* errorString = error.localizedDescription.UTF8String; + gnDebuggerSetErrorMessage(device->instance->debugger, (gnMessageData){ + .message = gnCombineStrings(gnCreateString("Failed to compile metal library "), errorString) + }); + return GN_FAILED_TO_CREATE_SHADER_MODULE; + } + + const char* name = shaderModuleInfo.entryPoint.value; + if (strcmp(name, "main") == 0) { + name = "main0"; + } + + gnBool foundFunction = false; + for (int i = 0; i < shaderLib.functionNames.count; i++) { + if (strcmp([shaderLib.functionNames objectAtIndex:0].UTF8String, name) == 0) { + foundFunction = true; + } + } + + if (!foundFunction) { + gnDebuggerSetErrorMessage(device->instance->debugger, (gnMessageData){ + .message = gnCombineStrings(gnCreateString("Failed to find specified entry point "), name) + }); + return GN_FAILED_TO_FIND_ENTRY_POINT; + } + + NSString* functionName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; + module->shaderModule->function = [shaderLib newFunctionWithName:functionName]; + + [shaderLib release]; + + spvc_context_destroy(context); + return GN_SUCCESS; +} + +void gnDestroyShaderModuleFn(struct gnShaderModule_t* module) { + +} diff --git a/rendering_api/metal/src/core/surface/metal_surface.m b/rendering_api/metal/src/core/surface/metal_surface.m index c6c7237..07aaadc 100644 --- a/rendering_api/metal/src/core/surface/metal_surface.m +++ b/rendering_api/metal/src/core/surface/metal_surface.m @@ -23,6 +23,10 @@ gnReturnCode gnCreateMacOSWindowSurfaceFn(struct gnWindowSurface_t* windowSurfac return GN_SUCCESS; } +void gnDestroyWindowSurfaceFn(struct gnWindowSurface_t *windowSurface) { + free(windowSurface->windowSurface); +} + struct gnSurfaceDetails_t gnGetSurfaceDetailsFn( // struct gnWindowSurface_t* windowSurface, // struct gnPhysicalDevice_t device, diff --git a/src/utils/gryphn_error_code.h b/src/utils/gryphn_error_code.h index 88b8f0d..f51fa99 100644 --- a/src/utils/gryphn_error_code.h +++ b/src/utils/gryphn_error_code.h @@ -17,7 +17,9 @@ typedef enum gnReturnCode_t { GN_FAILED_TO_CREATE_PRESENTATION_QUEUE, GN_UNSUPPORTED_IMAGE_COUNT, GN_FAILED_TO_CREATE_IMAGE_VIEW, - GN_FAILED_TO_CREATE_SHADER_MODULE + GN_FAILED_TO_CREATE_SHADER_MODULE, + GN_FAILED_TO_CONVERT_SHADER_CODE, + GN_FAILED_TO_FIND_ENTRY_POINT // GN_UNKNOWN_FRAMEBUFFER_ATTACHMENT, // GN_UNKNOWN_FUNCTION, @@ -48,5 +50,7 @@ static const char* gnErrorCodeToCString(enum gnReturnCode_t returnCode) { case GN_UNSUPPORTED_IMAGE_COUNT: return "GN_UNSUPPORTED_IMAGE_COUNT"; case GN_FAILED_TO_CREATE_IMAGE_VIEW: return "GN_FAILED_TO_CREATE_IMAGE_VIEW"; case GN_FAILED_TO_CREATE_SHADER_MODULE: return "GN_FAILED_TO_CREATE_SHADER_MODULE"; + case GN_FAILED_TO_CONVERT_SHADER_CODE: return "GN_FAILED_TO_CONVERT_SHADER_CODE"; + case GN_FAILED_TO_FIND_ENTRY_POINT: return "GN_FAILED_TO_FIND_ENTRY_POINT"; } }