From 7d22da040a38ae6f88769336ec86bfb38f1fb38d Mon Sep 17 00:00:00 2001 From: Greg Wells Date: Wed, 9 Jul 2025 21:37:22 -0400 Subject: [PATCH] metal sync extension --- projects/apis/metal/CMakeLists.txt | 2 + .../apis/metal/loader/metal_device_loader.m | 8 ---- projects/apis/metal/loader/metal_loader.h | 2 + .../apis/metal/loader/metal_sync_loader.m | 23 +++++++++ .../metal/src/devices/metal_physical_device.m | 13 ++--- .../apis/metal/src/present/metal_present.h | 1 + .../apis/metal/src/present/metal_present.m | 47 +++++++++++++++++++ .../metal_presentation_queue.h | 3 +- .../metal_presentation_queue.m | 9 +++- projects/apis/metal/src/submit/metal_submit.h | 3 +- projects/apis/metal/src/submit/metal_submit.m | 13 ++++- .../apis/metal/src/sync/fence/metal_fence.h | 2 +- .../src/sync/semaphore/metal_semaphore.h | 2 +- .../function_loader/src/instance_functions.c | 2 +- 14 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 projects/apis/metal/loader/metal_sync_loader.m diff --git a/projects/apis/metal/CMakeLists.txt b/projects/apis/metal/CMakeLists.txt index c7de4e3..df9aa1b 100644 --- a/projects/apis/metal/CMakeLists.txt +++ b/projects/apis/metal/CMakeLists.txt @@ -11,7 +11,9 @@ file(GLOB_RECURSE LOADER_FILES CONFIGURE_DEPENDS add_library(GryphnMetalImpl STATIC ${SOURCE_FILES} ${LOADER_FILES}) target_include_directories(GryphnMetalImpl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../core/src/ + ${CMAKE_CURRENT_SOURCE_DIR}/../../core/ ${CMAKE_CURRENT_SOURCE_DIR}/../../utils/ + ${CMAKE_CURRENT_SOURCE_DIR}/../../extensions/ ${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../platform/ diff --git a/projects/apis/metal/loader/metal_device_loader.m b/projects/apis/metal/loader/metal_device_loader.m index fedc1c7..57f8fe1 100644 --- a/projects/apis/metal/loader/metal_device_loader.m +++ b/projects/apis/metal/loader/metal_device_loader.m @@ -35,9 +35,6 @@ gnDeviceFunctions loadMetalDeviceFunctions() { ._gnCreateCommandPool = createMetalCommandPool, ._gnDestroyCommandPool = destroyMetalCommandPool, - ._gnCreateSemaphore = createMetalSemaphore, - ._gnDestroySemaphore = destroyMetalSemaphore, - ._gnCreateBuffer = createMetalBuffer, ._gnBufferData = metalBufferData, ._gnMapBuffer = mapMetalBuffer, @@ -55,11 +52,6 @@ gnDeviceFunctions loadMetalDeviceFunctions() { ._gnTextureData = metalTextureData, ._gnDestroyTexture = metalDestroyTexture, - ._gnCreateFence = createMetalFence, - ._gnWaitForFence = waitForMetalFence, - ._gnResetFence = resetMetalFence, - ._gnDestroyFence = destroyMetalFence, - ._gnSubmit = metalSubmit, ._gnPresent = metalPresent, diff --git a/projects/apis/metal/loader/metal_loader.h b/projects/apis/metal/loader/metal_loader.h index a4bd81f..7373e1b 100644 --- a/projects/apis/metal/loader/metal_loader.h +++ b/projects/apis/metal/loader/metal_loader.h @@ -2,7 +2,9 @@ #include "loader/src/gryphn_instance_functions.h" #include "loader/src/gryphn_device_functions.h" #include "loader/src/gryphn_command_functions.h" +#include "extensions/synchronization/loader/sync_functions.h" gnInstanceFunctions loadMetalInstanceFunctions(); gnDeviceFunctions loadMetalDeviceFunctions(); gnCommandFunctions loadMetalCommandFunctions(); +gnSyncExtFunctions loadMetalSyncFunctions(); diff --git a/projects/apis/metal/loader/metal_sync_loader.m b/projects/apis/metal/loader/metal_sync_loader.m new file mode 100644 index 0000000..f46c3f9 --- /dev/null +++ b/projects/apis/metal/loader/metal_sync_loader.m @@ -0,0 +1,23 @@ +#include "metal_loader.h" +#include +#include +#include "presentation_queue/metal_presentation_queue.h" +#include "submit/metal_submit.h" +#include "present/metal_present.h" + +gnSyncExtFunctions loadMetalSyncFunctions() { + return (gnSyncExtFunctions){ + ._gnPresentationQueueGetImageAsync = getMetalPresentQueueImageAsync, + + ._gnCreateSemaphore = createMetalSemaphore, + ._gnDestroySemaphore = destroyMetalSemaphore, + + ._gnCreateFence = createMetalFence, + ._gnWaitForFence = waitForMetalFence, + ._gnResetFence = resetMetalFence, + ._gnDestroyFence = destroyMetalFence, + + ._gnSubmitSync = metalSyncSubmit, + ._gnPresentSync = metalPresentSync + }; +} diff --git a/projects/apis/metal/src/devices/metal_physical_device.m b/projects/apis/metal/src/devices/metal_physical_device.m index e71b36b..84255eb 100644 --- a/projects/apis/metal/src/devices/metal_physical_device.m +++ b/projects/apis/metal/src/devices/metal_physical_device.m @@ -30,12 +30,13 @@ gnPhysicalDevice* getMetalDevices(gnInstanceHandle instance, uint32_t* deviceCou .queueType = GN_QUEUE_GRAPHICS | GN_QUEUE_COMPUTE | GN_QUEUE_TRANSFER }; - if ([device supportsTextureSampleCount:1]) { devicesList[i].features.avaliableSamples |= GN_SAMPLE_BIT_1; } - if ([device supportsTextureSampleCount:2]) { devicesList[i].features.avaliableSamples |= GN_SAMPLE_BIT_2; } - if ([device supportsTextureSampleCount:4]) { devicesList[i].features.avaliableSamples |= GN_SAMPLE_BIT_4; } - if ([device supportsTextureSampleCount:8]) { devicesList[i].features.avaliableSamples |= GN_SAMPLE_BIT_8; } - if ([device supportsTextureSampleCount:16]) { devicesList[i].features.avaliableSamples |= GN_SAMPLE_BIT_16; } - if ([device supportsTextureSampleCount:32]) { devicesList[i].features.avaliableSamples |= GN_SAMPLE_BIT_32; } + if ([device supportsTextureSampleCount:1]) { devicesList[i].features.maxColorSamples |= GN_SAMPLE_BIT_1; } + if ([device supportsTextureSampleCount:2]) { devicesList[i].features.maxColorSamples |= GN_SAMPLE_BIT_2; } + if ([device supportsTextureSampleCount:4]) { devicesList[i].features.maxColorSamples |= GN_SAMPLE_BIT_4; } + if ([device supportsTextureSampleCount:8]) { devicesList[i].features.maxColorSamples |= GN_SAMPLE_BIT_8; } + if ([device supportsTextureSampleCount:16]) { devicesList[i].features.maxColorSamples |= GN_SAMPLE_BIT_16; } + if ([device supportsTextureSampleCount:32]) { devicesList[i].features.maxColorSamples |= GN_SAMPLE_BIT_32; } + devicesList[i].features.maxDepthSamples = devicesList[i].features.maxColorSamples; devicesList[i].features.maxMemoryAllocations = 0x40000000; devicesList[i].features.maxPushConstantSize = 4096; diff --git a/projects/apis/metal/src/present/metal_present.h b/projects/apis/metal/src/present/metal_present.h index 82b9912..84e93e3 100644 --- a/projects/apis/metal/src/present/metal_present.h +++ b/projects/apis/metal/src/present/metal_present.h @@ -8,4 +8,5 @@ #include "texture/metal_texture.h" #import +gnReturnCode metalPresentSync(gnOutputDeviceHandle device, gnPresentSyncInfo info); gnReturnCode metalPresent(gnOutputDeviceHandle device, gnPresentInfo info); diff --git a/projects/apis/metal/src/present/metal_present.m b/projects/apis/metal/src/present/metal_present.m index 71a4066..c6561cf 100644 --- a/projects/apis/metal/src/present/metal_present.m +++ b/projects/apis/metal/src/present/metal_present.m @@ -1,6 +1,53 @@ #include "metal_present.h" +#include gnReturnCode metalPresent(gnOutputDeviceHandle device, gnPresentInfo info) { + + for (int i =0 ; i < info.presentationQueueCount; i++) { + info.presentationQueues[i]->info.surface->windowSurface->layer.device = device->outputDevice->device; + id drawable = [info.presentationQueues[i]->info.surface->windowSurface->layer nextDrawable]; + if (drawable == nil) return GN_FAILED_TO_CREATE_FRAMEBUFFER; + + __block gnPresentationQueue presentationQueue = info.presentationQueues[i]; + __block uint32_t imageIndex = info.imageIndices[i]; + + id commandBuffer = [device->outputDevice->transferQueue commandBuffer]; + [commandBuffer addCompletedHandler:^(id buffer) { + uint32_tArrayListAdd(&presentationQueue->presentationQueue->avaliableTextures, 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}]; + + [blit endEncoding]; + + [commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; + device->outputDevice->executingCommandBuffer = commandBuffer; + } + + [device->outputDevice->executingCommandBuffer waitUntilScheduled]; + + for (int 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; +} + + +gnReturnCode metalPresentSync(gnOutputDeviceHandle device, gnPresentSyncInfo info) { for (int i = 0; i < info.waitCount; i++) { while (!info.waitSemaphores[i]->semaphore->eventTriggered) {} } diff --git a/projects/apis/metal/src/presentation_queue/metal_presentation_queue.h b/projects/apis/metal/src/presentation_queue/metal_presentation_queue.h index c17ecfc..e38301f 100644 --- a/projects/apis/metal/src/presentation_queue/metal_presentation_queue.h +++ b/projects/apis/metal/src/presentation_queue/metal_presentation_queue.h @@ -11,5 +11,6 @@ typedef struct gnPlatformPresentationQueue_t { } gnPlatformPresentationQueue; gnReturnCode createMetalPresentationQueue(gnPresentationQueueHandle presentationQueue, const gnDevice device, gnPresentationQueueInfo presentationInfo); -gnReturnCode getMetalPresentQueueImage(gnPresentationQueueHandle presentationQueue, uint64_t timeout, gnSemaphore semaphore, uint32_t* imageIndex); +gnReturnCode getMetalPresentQueueImageAsync(gnPresentationQueueHandle presentationQueue, uint64_t timeout, gnSemaphore semaphore, uint32_t* imageIndex); +gnReturnCode getMetalPresentQueueImage(gnPresentationQueueHandle presentationQueue, uint32_t* imageIndex); void destroyMetalPresentationQueue(gnPresentationQueueHandle presentationQueue); diff --git a/projects/apis/metal/src/presentation_queue/metal_presentation_queue.m b/projects/apis/metal/src/presentation_queue/metal_presentation_queue.m index 7f5fae8..464de7b 100644 --- a/projects/apis/metal/src/presentation_queue/metal_presentation_queue.m +++ b/projects/apis/metal/src/presentation_queue/metal_presentation_queue.m @@ -48,7 +48,7 @@ gnReturnCode createMetalPresentationQueue(gnPresentationQueueHandle presentation return GN_SUCCESS; } -gnReturnCode getMetalPresentQueueImage(gnPresentationQueueHandle presentationQueue, uint64_t timeout, gnSemaphore semaphore, uint32_t* imageIndex) { +gnReturnCode getMetalPresentQueueImageAsync(gnPresentationQueueHandle presentationQueue, uint64_t timeout, gnSemaphore semaphore, uint32_t* imageIndex) { while (presentationQueue->presentationQueue->avaliableTextures.count == 0) {} *imageIndex = presentationQueue->presentationQueue->avaliableTextures.data[0]; uint32_tArrayListPopHead(&presentationQueue->presentationQueue->avaliableTextures); @@ -56,6 +56,13 @@ gnReturnCode getMetalPresentQueueImage(gnPresentationQueueHandle presentationQue return GN_SUCCESS; } +gnReturnCode getMetalPresentQueueImage(gnPresentationQueueHandle presentationQueue, uint32_t* imageIndex) { + while (presentationQueue->presentationQueue->avaliableTextures.count == 0) {} + *imageIndex = presentationQueue->presentationQueue->avaliableTextures.data[0]; + uint32_tArrayListPopHead(&presentationQueue->presentationQueue->avaliableTextures); + return GN_SUCCESS; +} + void destroyMetalPresentationQueue(gnPresentationQueueHandle presentationQueue) { free(presentationQueue->presentationQueue->avaliableTextures.data); presentationQueue->presentationQueue->avaliableTextures.count = 0; diff --git a/projects/apis/metal/src/submit/metal_submit.h b/projects/apis/metal/src/submit/metal_submit.h index ac9e4fe..cfaed85 100644 --- a/projects/apis/metal/src/submit/metal_submit.h +++ b/projects/apis/metal/src/submit/metal_submit.h @@ -3,6 +3,7 @@ #include "commands/command_buffer/metal_command_buffer.h" #include "debugger/gryphn_debugger.h" #include "commands/command_pool/metal_command_pool.h" -#include "sync/fence/gryphn_fence.h" +#include "synchronization/commands/gryphn_sync_submit.h" +gnReturnCode metalSyncSubmit(gnOutputDevice device, gnSubmitSyncInfo info); gnReturnCode metalSubmit(gnOutputDevice device, gnSubmitInfo info); diff --git a/projects/apis/metal/src/submit/metal_submit.m b/projects/apis/metal/src/submit/metal_submit.m index 057a761..9532405 100644 --- a/projects/apis/metal/src/submit/metal_submit.m +++ b/projects/apis/metal/src/submit/metal_submit.m @@ -1,6 +1,7 @@ #include "metal_submit.h" +#include "synchronization/fence/gryphn_fence.h" -gnReturnCode metalSubmit(gnOutputDevice device, gnSubmitInfo info) { +gnReturnCode metalSyncSubmit(gnOutputDevice device, gnSubmitSyncInfo info) { for (int i = 0; i < info.waitCount; i++) { while (!info.waitSemaphores[i]->semaphore->eventTriggered) {} } @@ -23,3 +24,13 @@ gnReturnCode metalSubmit(gnOutputDevice device, gnSubmitInfo info) { return GN_SUCCESS; } + +gnReturnCode metalSubmit(gnOutputDevice device, gnSubmitInfo info) { + for (int i = 0; i < info.commandBufferCount; i++) { + id commandBuffer = info.commandBuffers[i]->commandBuffer->commandBuffer; + [commandBuffer commit]; + } + + [info.commandBuffers[info.commandBufferCount - 1]->commandBuffer->commandBuffer waitUntilCompleted]; + return GN_SUCCESS; +} diff --git a/projects/apis/metal/src/sync/fence/metal_fence.h b/projects/apis/metal/src/sync/fence/metal_fence.h index 13c5a27..fda644c 100644 --- a/projects/apis/metal/src/sync/fence/metal_fence.h +++ b/projects/apis/metal/src/sync/fence/metal_fence.h @@ -1,5 +1,5 @@ #pragma once -#include "sync/fence/gryphn_fence.h" +#include "synchronization/fence/gryphn_fence.h" #import #import diff --git a/projects/apis/metal/src/sync/semaphore/metal_semaphore.h b/projects/apis/metal/src/sync/semaphore/metal_semaphore.h index b5ffaf0..6d587e4 100644 --- a/projects/apis/metal/src/sync/semaphore/metal_semaphore.h +++ b/projects/apis/metal/src/sync/semaphore/metal_semaphore.h @@ -1,5 +1,5 @@ #pragma once -#include "sync/semaphore/gryphn_semaphore.h" +#include "synchronization/semaphore/gryphn_semaphore.h" #import typedef struct gnPlatformSemaphore_t { diff --git a/projects/validation_layers/function_loader/src/instance_functions.c b/projects/validation_layers/function_loader/src/instance_functions.c index 922ca54..b3f3459 100644 --- a/projects/validation_layers/function_loader/src/instance_functions.c +++ b/projects/validation_layers/function_loader/src/instance_functions.c @@ -28,7 +28,7 @@ void checkDestroyOutputDevice(gnOutputDeviceHandle device) { #ifdef GN_PLATFORM_MACOS gnReturnCode checkCreateSurfaceMacOS(gnWindowSurfaceHandle windowSurface, gnInstanceHandle instance, gnMacOSWindowSurfaceInfo createInfo) { - CHECK_FUNCTION_WITH_RETURN_CODE(instance, _gnCreateMacOSWindowSurface, instanceFunctions, surface, instance, info); + CHECK_FUNCTION_WITH_RETURN_CODE(instance, _gnCreateMacOSWindowSurface, instanceFunctions, windowSurface, instance, createInfo); } #endif #ifdef GN_PLATFORM_LINUX