From 3661e39ac1de1fd88e095b5c7ee9bf44c71bb83f Mon Sep 17 00:00:00 2001 From: Gregory Wells Date: Wed, 23 Jul 2025 16:36:39 -0400 Subject: [PATCH] support dynamic uniform buffers on metal --- .../src/commands/commands/metal_commands.m | 27 +++++++++++++++-- .../apis/metal/src/uniforms/metal_uniform.h | 14 +++++++-- .../apis/metal/src/uniforms/metal_uniform.m | 29 ++++++++++++------- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/projects/apis/metal/src/commands/commands/metal_commands.m b/projects/apis/metal/src/commands/commands/metal_commands.m index 4fc5344..da2ea2a 100644 --- a/projects/apis/metal/src/commands/commands/metal_commands.m +++ b/projects/apis/metal/src/commands/commands/metal_commands.m @@ -117,8 +117,31 @@ void metalBindUniform(gnCommandBufferHandle buffer, gnUniform uniform, uint32_t [encoder useResources:uniform->uniform->usedResources count:uniform->uniform->usedResourceCount usage:MTLResourceUsageRead stages:MTLRenderStageVertex | MTLRenderStageFragment]; - [encoder setVertexBuffer:uniform->uniform->argumentBuffers[mtlVertex] offset:0 atIndex:(set + 1)]; - [encoder setFragmentBuffer:uniform->uniform->argumentBuffers[mtlFragment] offset:0 atIndex:(set + 1)]; + int startIndex = 0; + for (int i = 0; i < dynamicOffsetCount; i++) { + int c = startIndex; + for (; c < MAX_METAL_BINDINGS; c++) { + if (uniform->uniform->isDynamic[c]) { + gnBufferUniformInfo updateInfo = { + .binding = c, + .dynamic = gnTrue, + .offset = dynamicOffsets[i], + .size = 0 + }; + mtlBufferUniformInfo info = { + .baseInfo = &updateInfo, + .buffer = (id)uniform->uniform->usedResources[uniform->uniform->indexMap[c]] + }; + mtlUpdateMetalBufferUniform(uniform, &info); + + break; + } + } + startIndex = c + 1; + } + + [encoder setVertexBytes:uniform->uniform->argumentBuffers[mtlVertex].contents length:uniform->uniform->encoders[mtlVertex].encodedLength atIndex:(set + 1)]; + [encoder setFragmentBytes:uniform->uniform->argumentBuffers[mtlFragment].contents length:uniform->uniform->encoders[mtlFragment].encodedLength atIndex:(set + 1)]; } void metalBindVertexBytes(gnCommandBufferHandle buffer, gnPushConstantLayout layout, void* data) { diff --git a/projects/apis/metal/src/uniforms/metal_uniform.h b/projects/apis/metal/src/uniforms/metal_uniform.h index 75e5884..1d0f4e9 100644 --- a/projects/apis/metal/src/uniforms/metal_uniform.h +++ b/projects/apis/metal/src/uniforms/metal_uniform.h @@ -8,6 +8,8 @@ typedef struct metalUniformBinding { gnUniformType type; uint32_t binding; void* data; + + gnBool isDynamic; } metalUniformBinding; typedef id mtlResource; @@ -17,13 +19,21 @@ typedef struct gnPlatformUniform_t { gnShaderModuleStage stageUsed[MAX_METAL_BINDINGS]; id encoders[mtlMaxStage]; id argumentBuffers[mtlMaxStage]; - - mtlResource usedResources[MAX_METAL_BINDINGS]; int indexMap[MAX_METAL_BINDINGS]; uint32_t usedResourceCount; + + gnBool isDynamic[MAX_METAL_BINDINGS]; } gnPlatformUniform; void updateMetalBufferUniform(gnUniform uniform, gnBufferUniformInfo* info); void updateMetalStorageUniform(gnUniform uniform, gnStorageUniformInfo* info); void updateMetalImageUniform(gnUniform uniform, gnImageUniformInfo* info); + + +typedef struct mtlBufferUniformInfo { + gnBufferUniformInfo* baseInfo; + id buffer; +} mtlBufferUniformInfo; + +void mtlUpdateMetalBufferUniform(gnUniformHandle uniform, mtlBufferUniformInfo* info); diff --git a/projects/apis/metal/src/uniforms/metal_uniform.m b/projects/apis/metal/src/uniforms/metal_uniform.m index 955d54e..24c6c40 100644 --- a/projects/apis/metal/src/uniforms/metal_uniform.m +++ b/projects/apis/metal/src/uniforms/metal_uniform.m @@ -4,16 +4,25 @@ #include "texture/metal_texture.h" #include -void updateMetalBufferUniform(gnUniform uniform, gnBufferUniformInfo* info) { - if (uniform->uniform->indexMap[info->binding] == -1) { - uniform->uniform->indexMap[info->binding] = uniform->uniform->usedResourceCount; +void mtlUpdateMetalBufferUniform(gnUniformHandle uniform, mtlBufferUniformInfo* info) { + if (uniform->uniform->indexMap[info->baseInfo->binding] == -1) { + uniform->uniform->indexMap[info->baseInfo->binding] = uniform->uniform->usedResourceCount; uniform->uniform->usedResourceCount++; } - uniform->uniform->usedResources[uniform->uniform->indexMap[info->binding]] = info->buffer->buffer->buffer; - if ((uniform->uniform->stageUsed[info->binding] & GN_VERTEX_SHADER_MODULE) == GN_VERTEX_SHADER_MODULE) - [uniform->uniform->encoders[mtlVertex] setBuffer:info->buffer->buffer->buffer offset:0 atIndex:uniform->uniform->indexMap[info->binding]]; - if ((uniform->uniform->stageUsed[info->binding] & GN_FRAGMENT_SHADER_MODULE) == GN_FRAGMENT_SHADER_MODULE) - [uniform->uniform->encoders[mtlFragment] setBuffer:info->buffer->buffer->buffer offset:0 atIndex:uniform->uniform->index[info->binding]]; + uniform->uniform->isDynamic[info->baseInfo->binding] = info->baseInfo->dynamic; + uniform->uniform->usedResources[uniform->uniform->indexMap[info->baseInfo->binding]] = info->buffer; + if ((uniform->uniform->stageUsed[info->baseInfo->binding] & GN_VERTEX_SHADER_MODULE) == GN_VERTEX_SHADER_MODULE) + [uniform->uniform->encoders[mtlVertex] setBuffer:info->buffer offset:info->baseInfo->offset atIndex:uniform->uniform->indexMap[info->baseInfo->binding]]; + if ((uniform->uniform->stageUsed[info->baseInfo->binding] & GN_FRAGMENT_SHADER_MODULE) == GN_FRAGMENT_SHADER_MODULE) + [uniform->uniform->encoders[mtlFragment] setBuffer:info->buffer offset:info->baseInfo->offset atIndex:uniform->uniform->index[info->baseInfo->binding]]; +} + +void updateMetalBufferUniform(gnUniform uniform, gnBufferUniformInfo* info) { + mtlBufferUniformInfo mtlInfo = { + .baseInfo = info, + .buffer = info->buffer->buffer->buffer + }; + mtlUpdateMetalBufferUniform(uniform, &mtlInfo); } void updateMetalStorageUniform(gnUniform uniform, gnStorageUniformInfo* info) { if (uniform->uniform->indexMap[info->binding] == -1) { @@ -22,9 +31,9 @@ void updateMetalStorageUniform(gnUniform uniform, gnStorageUniformInfo* info) { } uniform->uniform->usedResources[uniform->uniform->indexMap[info->binding]] = info->buffer->buffer->buffer; if ((uniform->uniform->stageUsed[info->binding] & GN_VERTEX_SHADER_MODULE) == GN_VERTEX_SHADER_MODULE) - [uniform->uniform->encoders[mtlVertex] setBuffer:info->buffer->buffer->buffer offset:0 atIndex:uniform->uniform->indexMap[info->binding]]; + [uniform->uniform->encoders[mtlVertex] setBuffer:info->buffer->buffer->buffer offset:info->offset atIndex:uniform->uniform->indexMap[info->binding]]; if ((uniform->uniform->stageUsed[info->binding] & GN_FRAGMENT_SHADER_MODULE) == GN_FRAGMENT_SHADER_MODULE) - [uniform->uniform->encoders[mtlFragment] setBuffer:info->buffer->buffer->buffer offset:0 atIndex:uniform->uniform->index[info->binding]]; + [uniform->uniform->encoders[mtlFragment] setBuffer:info->buffer->buffer->buffer offset:info->offset atIndex:uniform->uniform->index[info->binding]]; } void updateMetalImageUniform(gnUniform uniform, gnImageUniformInfo* info) {