diff --git a/Examples/Vulkan/main.c b/Examples/Vulkan/main.c index 7db5179..2d46e07 100644 --- a/Examples/Vulkan/main.c +++ b/Examples/Vulkan/main.c @@ -60,7 +60,7 @@ int main(void) CHECK_PULSE_HANDLE_RETVAL(pass, 1); PulseBindStorageBuffers(pass, 0, &buffer, 1); PulseBindComputePipeline(pass, pipeline); - PulseDispatchComputations(pass, 8, 8, 8); + PulseDispatchComputations(pass, 32, 32, 1); PulseEndComputePass(pass); if(!PulseSubmitCommandList(device, cmd, fence)) @@ -70,9 +70,10 @@ int main(void) PulseReleaseCommandList(device, cmd); PulseDestroyFence(device, fence); - PulseDestroyBuffer(device, buffer); PulseDestroyComputePipeline(device, pipeline); + PulseDestroyBuffer(device, buffer); + PulseDestroyDevice(device); PulseUnloadBackend(backend); puts("Successfully loaded Pulse using Vulkan !"); diff --git a/Examples/Vulkan/shader.nzsl b/Examples/Vulkan/shader.nzsl index 4195d70..875b41a 100644 --- a/Examples/Vulkan/shader.nzsl +++ b/Examples/Vulkan/shader.nzsl @@ -3,11 +3,23 @@ module; struct Input { - [builtin(global_invocation_indices)] indices: vec3[u32] + [builtin(global_invocation_indices)] indices: vec3[u32] +} + +[layout(std430)] +struct SSBO +{ + data: dyn_array[i32] +} + +external +{ + [set(0), binding(0)] ssbo: storage[SSBO], } [entry(compute)] -[workgroup(8, 8, 8)] +[workgroup(32, 32, 1)] fn main(input: Input) { + ssbo.data[input.indices.x * input.indices.y] = 1; } diff --git a/Includes/Pulse.h b/Includes/Pulse.h index 58e3afb..940adcf 100644 --- a/Includes/Pulse.h +++ b/Includes/Pulse.h @@ -1,4 +1,4 @@ -// Copyright (C) 2024 kanel +// Copyright (C) 2025 kanel // This file is part of "Pulse" // For conditions of distribution and use, see copyright notice in LICENSE @@ -278,11 +278,12 @@ PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, co PULSE_API void PulseDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline); PULSE_API PulseComputePass PulseBeginComputePass(PulseCommandList cmd); -PULSE_API void PulseBindStorageBuffers(PulseComputePass pass, uint32_t starting_slot, PulseBuffer* const* buffers, uint32_t num_buffers); +PULSE_API void PulseBindStorageBuffers(PulseComputePass pass, uint32_t starting_slot, const PulseBuffer* buffers, uint32_t num_buffers); PULSE_API void PulseBindUniformData(PulseComputePass pass, uint32_t slot, const void* data, uint32_t data_size); -PULSE_API void PulseBindStorageImages(PulseComputePass pass, uint32_t starting_slot, PulseImage* const* images, uint32_t num_images); +PULSE_API void PulseBindStorageImages(PulseComputePass pass, uint32_t starting_slot, const PulseImage* images, uint32_t num_images); PULSE_API void PulseBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline); PULSE_API void PulseDispatchComputations(PulseComputePass pass, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z); +PULSE_API void PulseDispatchComputationsIndirect(PulseComputePass pass, PulseBuffer buffer, uint32_t offset); PULSE_API void PulseEndComputePass(PulseComputePass pass); PULSE_API PulseErrorType PulseGetLastErrorType(); // Call to this function resets the internal last error variable diff --git a/Includes/PulseProfile.h b/Includes/PulseProfile.h index 04db4a5..bcbdd44 100644 --- a/Includes/PulseProfile.h +++ b/Includes/PulseProfile.h @@ -1,4 +1,4 @@ -// Copyright (C) 2024 kanel +// Copyright (C) 2025 kanel // This file is part of "Pulse" // For conditions of distribution and use, see copyright notice in LICENSE diff --git a/Sources/Backends/Vulkan/VulkanComputePass.c b/Sources/Backends/Vulkan/VulkanComputePass.c index 7234f24..3a0442a 100644 --- a/Sources/Backends/Vulkan/VulkanComputePass.c +++ b/Sources/Backends/Vulkan/VulkanComputePass.c @@ -34,16 +34,46 @@ void VulkanDestroyComputePass(PulseDevice device, PulseComputePass pass) free(pass); } -void VulkanBindStorageBuffers(PulseComputePass pass, uint32_t starting_slot, PulseBuffer* const* buffers, uint32_t num_buffers) +void VulkanBindStorageBuffers(PulseComputePass pass, uint32_t starting_slot, const PulseBuffer* buffers, uint32_t num_buffers) { + PulseBufferUsageFlags usage = buffers[0]->usage; + PulseBuffer* array = ((usage & PULSE_BUFFER_USAGE_STORAGE_WRITE) == 1) ? pass->readwrite_storage_buffers : pass->readonly_storage_buffers; + VulkanComputePass* vulkan_pass = VULKAN_RETRIEVE_DRIVER_DATA_AS(pass, VulkanComputePass*); + + for(uint32_t i = 0; i < num_buffers; i++) + { + if(array[starting_slot + i] == buffers[i]) + continue; + array[starting_slot + i] = buffers[i]; + + if((usage & PULSE_BUFFER_USAGE_STORAGE_WRITE) == 1) + vulkan_pass->should_recreate_write_descriptor_sets = true; + else + vulkan_pass->should_recreate_read_only_descriptor_sets = true; + } } void VulkanBindUniformData(PulseComputePass pass, uint32_t slot, const void* data, uint32_t data_size) { } -void VulkanBindStorageImages(PulseComputePass pass, uint32_t starting_slot, PulseImage* const* images, uint32_t num_images) +void VulkanBindStorageImages(PulseComputePass pass, uint32_t starting_slot, const PulseImage* images, uint32_t num_images) { + PulseImageUsageFlags usage = images[0]->usage; + PulseImage* array = ((usage & PULSE_IMAGE_USAGE_STORAGE_WRITE) == 1) ? pass->readwrite_images : pass->readonly_images; + VulkanComputePass* vulkan_pass = VULKAN_RETRIEVE_DRIVER_DATA_AS(pass, VulkanComputePass*); + + for(uint32_t i = 0; i < num_images; i++) + { + if(array[starting_slot + i] == images[i]) + continue; + array[starting_slot + i] = images[i]; + + if((usage & PULSE_IMAGE_USAGE_STORAGE_WRITE) == 1) + vulkan_pass->should_recreate_write_descriptor_sets = true; + else + vulkan_pass->should_recreate_read_only_descriptor_sets = true; + } } void VulkanBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline) @@ -51,8 +81,13 @@ void VulkanBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipel VulkanComputePipeline* vulkan_pipeline = VULKAN_RETRIEVE_DRIVER_DATA_AS(pipeline, VulkanComputePipeline*); VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(pass->cmd->device, VulkanDevice*); VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(pass->cmd, VulkanCommandList*); + VulkanComputePass* vulkan_pass = VULKAN_RETRIEVE_DRIVER_DATA_AS(pass, VulkanComputePass*); vulkan_device->vkCmdBindPipeline(vulkan_cmd->cmd, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_pipeline->pipeline); + + vulkan_pass->should_recreate_read_only_descriptor_sets = true; + vulkan_pass->should_recreate_write_descriptor_sets = true; + vulkan_pass->should_recreate_uniform_descriptor_sets = true; } void VulkanDispatchComputations(PulseComputePass pass, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z) @@ -60,6 +95,8 @@ void VulkanDispatchComputations(PulseComputePass pass, uint32_t groupcount_x, ui VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(pass->cmd->device, VulkanDevice*); VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(pass->cmd, VulkanCommandList*); + VulkanBindDescriptorSets(pass); + vulkan_device->vkCmdDispatch(vulkan_cmd->cmd, groupcount_x, groupcount_y, groupcount_z); } diff --git a/Sources/Backends/Vulkan/VulkanComputePass.h b/Sources/Backends/Vulkan/VulkanComputePass.h index 36afc96..178f7e7 100644 --- a/Sources/Backends/Vulkan/VulkanComputePass.h +++ b/Sources/Backends/Vulkan/VulkanComputePass.h @@ -31,9 +31,9 @@ void VulkanDestroyComputePass(PulseDevice device, PulseComputePass pass); PulseComputePass VulkanBeginComputePass(PulseCommandList cmd); void VulkanEndComputePass(PulseComputePass pass); -void VulkanBindStorageBuffers(PulseComputePass pass, uint32_t starting_slot, PulseBuffer* const* buffers, uint32_t num_buffers); +void VulkanBindStorageBuffers(PulseComputePass pass, uint32_t starting_slot, const PulseBuffer* buffers, uint32_t num_buffers); void VulkanBindUniformData(PulseComputePass pass, uint32_t slot, const void* data, uint32_t data_size); -void VulkanBindStorageImages(PulseComputePass pass, uint32_t starting_slot, PulseImage* const* images, uint32_t num_images); +void VulkanBindStorageImages(PulseComputePass pass, uint32_t starting_slot, const PulseImage* images, uint32_t num_images); void VulkanBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline); void VulkanDispatchComputations(PulseComputePass pass, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z); diff --git a/Sources/Backends/Vulkan/VulkanDescriptor.c b/Sources/Backends/Vulkan/VulkanDescriptor.c index 30f1fea..c62f3ae 100644 --- a/Sources/Backends/Vulkan/VulkanDescriptor.c +++ b/Sources/Backends/Vulkan/VulkanDescriptor.c @@ -9,9 +9,7 @@ #include "Vulkan.h" #include "VulkanDevice.h" #include "VulkanDescriptor.h" - -#undef NDEBUG -#include +#include "VulkanComputePass.h" void VulkanInitDescriptorSetLayoutManager(VulkanDescriptorSetLayoutManager* manager, PulseDevice device) { @@ -235,6 +233,37 @@ void VulkanReturnDescriptorSetToPool(VulkanDescriptorSetPool* pool, const Vulkan } } +void VulkanBindDescriptorSets(PulseComputePass pass) +{ + VulkanComputePass* vulkan_pass = VULKAN_RETRIEVE_DRIVER_DATA_AS(pass, VulkanComputePass*); + if(!vulkan_pass->should_recreate_read_only_descriptor_sets && !vulkan_pass->should_recreate_write_descriptor_sets && !vulkan_pass->should_recreate_uniform_descriptor_sets) + return; + + VkWriteDescriptorSet writes[ + PULSE_MAX_READ_TEXTURES_BOUND + + PULSE_MAX_READ_BUFFERS_BOUND + + PULSE_MAX_WRITE_TEXTURES_BOUND + + PULSE_MAX_WRITE_BUFFERS_BOUND + + PULSE_MAX_UNIFORM_BUFFERS_BOUND]; + VkDescriptorBufferInfo buffer_infos[PULSE_MAX_UNIFORM_BUFFERS_BOUND + PULSE_MAX_WRITE_BUFFERS_BOUND + PULSE_MAX_READ_BUFFERS_BOUND]; + VkDescriptorImageInfo image_infos[PULSE_MAX_READ_TEXTURES_BOUND + PULSE_MAX_WRITE_TEXTURES_BOUND]; + uint32_t write_count = 0; + uint32_t buffer_info_count = 0; + uint32_t image_info_count = 0; + + if(vulkan_pass->should_recreate_read_only_descriptor_sets) + { + } + + if(vulkan_pass->should_recreate_write_descriptor_sets) + { + } + + if(vulkan_pass->should_recreate_uniform_descriptor_sets) + { + } +} + void VulkanDestroyDescriptorSetPool(VulkanDescriptorSetPool* pool) { if(pool->pool == VK_NULL_HANDLE) diff --git a/Sources/Backends/Vulkan/VulkanDescriptor.h b/Sources/Backends/Vulkan/VulkanDescriptor.h index 40e9c58..eb29eba 100644 --- a/Sources/Backends/Vulkan/VulkanDescriptor.h +++ b/Sources/Backends/Vulkan/VulkanDescriptor.h @@ -87,6 +87,8 @@ void VulkanDestroyDescriptorSetLayoutManager(VulkanDescriptorSetLayoutManager* m VulkanDescriptorSet* VulkanRequestDescriptorSetFromPool(VulkanDescriptorSetPool* pool, const VulkanDescriptorSetLayout* layout); void VulkanReturnDescriptorSetToPool(VulkanDescriptorSetPool* pool, const VulkanDescriptorSet* set); +void VulkanBindDescriptorSets(PulseComputePass pass); + void VulkanInitDescriptorSetPoolManager(VulkanDescriptorSetPoolManager* manager, PulseDevice device); VulkanDescriptorSetPool* VulkanGetAvailableDescriptorSetPool(VulkanDescriptorSetPoolManager* manager); void VulkanDestroyDescriptorSetPoolManager(VulkanDescriptorSetPoolManager* manager); diff --git a/Sources/PulseComputePass.c b/Sources/PulseComputePass.c index efc2446..67fd82f 100644 --- a/Sources/PulseComputePass.c +++ b/Sources/PulseComputePass.c @@ -6,7 +6,24 @@ #include "PulseDefs.h" #include "PulseInternal.h" -PULSE_API void PulseBindStorageBuffers(PulseComputePass pass, uint32_t starting_slot, PulseBuffer* const* buffers, uint32_t num_buffers) +PULSE_API PulseComputePass PulseBeginComputePass(PulseCommandList cmd) +{ + PULSE_CHECK_HANDLE_RETVAL(cmd, PULSE_NULL_HANDLE); + PULSE_CHECK_HANDLE_RETVAL(cmd->device, PULSE_NULL_HANDLE); + + PULSE_CHECK_COMMAND_LIST_STATE_RETVAL(cmd, PULSE_NULL_HANDLE); + PulseComputePass pass = cmd->device->PFN_BeginComputePass(cmd); + if(pass->is_recording == true) + { + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(cmd->device->backend)) + PulseLogWarning(cmd->device->backend, "a compute pass is already recording in this command buffer, please call PulseEndComputePass before beginning a new one"); + return PULSE_NULL_HANDLE; + } + pass->is_recording = true; + return pass; +} + +PULSE_API void PulseBindStorageBuffers(PulseComputePass pass, uint32_t starting_slot, const PulseBuffer* buffers, uint32_t num_buffers) { PULSE_CHECK_HANDLE(pass); @@ -24,7 +41,7 @@ PULSE_API void PulseBindUniformData(PulseComputePass pass, uint32_t slot, const pass->cmd->device->PFN_BindUniformData(pass, slot, data, data_size); } -PULSE_API void PulseBindStorageImages(PulseComputePass pass, uint32_t starting_slot, PulseImage* const* images, uint32_t num_images) +PULSE_API void PulseBindStorageImages(PulseComputePass pass, uint32_t starting_slot, const PulseImage* images, uint32_t num_images) { PULSE_CHECK_HANDLE(pass); @@ -55,24 +72,14 @@ PULSE_API void PulseDispatchComputations(PulseComputePass pass, uint32_t groupco PULSE_CHECK_COMMAND_LIST_STATE(pass->cmd); - pass->cmd->device->PFN_DispatchComputations(pass, groupcount_x, groupcount_y, groupcount_z); -} - -PULSE_API PulseComputePass PulseBeginComputePass(PulseCommandList cmd) -{ - PULSE_CHECK_HANDLE_RETVAL(cmd, PULSE_NULL_HANDLE); - PULSE_CHECK_HANDLE_RETVAL(cmd->device, PULSE_NULL_HANDLE); - - PULSE_CHECK_COMMAND_LIST_STATE_RETVAL(cmd, PULSE_NULL_HANDLE); - PulseComputePass pass = cmd->device->PFN_BeginComputePass(cmd); - if(pass->is_recording == true) + if(pass->current_pipeline == PULSE_NULL_HANDLE) { - if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(cmd->device->backend)) - PulseLogWarning(cmd->device->backend, "a compute pass is already recording in this command buffer, please call PulseEndComputePass before beginning a new one"); - return PULSE_NULL_HANDLE; + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(pass->cmd->device->backend)) + PulseLogWarning(pass->cmd->device->backend, "cannot dispatch computations, no pipeline bound"); + PulseSetInternalError(PULSE_ERROR_INVALID_HANDLE); } - pass->is_recording = true; - return pass; + + pass->cmd->device->PFN_DispatchComputations(pass, groupcount_x, groupcount_y, groupcount_z); } PULSE_API void PulseEndComputePass(PulseComputePass pass) diff --git a/Sources/PulsePFNs.h b/Sources/PulsePFNs.h index 5e496b5..ead02b9 100644 --- a/Sources/PulsePFNs.h +++ b/Sources/PulsePFNs.h @@ -36,9 +36,9 @@ typedef bool (*PulseCopyBufferToImageFN)(PulseCommandList, const PulseBufferRegi typedef bool (*PulseCopyImageToBufferPFN)(PulseCommandList, const PulseImageRegion*, const PulseBufferRegion*); typedef bool (*PulseBlitImagePFN)(PulseCommandList, const PulseImageRegion*, const PulseImageRegion*); typedef PulseComputePass (*PulseBeginComputePassPFN)(PulseCommandList); -typedef void (*PulseBindStorageBuffersPFN)(PulseComputePass, uint32_t, PulseBuffer* const*, uint32_t); +typedef void (*PulseBindStorageBuffersPFN)(PulseComputePass, uint32_t, const PulseBuffer*, uint32_t); typedef void (*PulseBindUniformDataPFN)(PulseComputePass, uint32_t, const void*, uint32_t); -typedef void (*PulseBindStorageImagesPFN)(PulseComputePass, uint32_t, PulseImage* const*, uint32_t); +typedef void (*PulseBindStorageImagesPFN)(PulseComputePass, uint32_t, const PulseImage*, uint32_t); typedef void (*PulseBindComputePipelinePFN)(PulseComputePass, PulseComputePipeline); typedef void (*PulseEndComputePassPFN)(PulseComputePass); diff --git a/Xmake/Actions/CheckFiles.lua b/Xmake/Actions/CheckFiles.lua index cfdeb42..b81f7c0 100644 --- a/Xmake/Actions/CheckFiles.lua +++ b/Xmake/Actions/CheckFiles.lua @@ -45,6 +45,7 @@ on_run(function() Name = "empty lines", Check = function() local files = table.join( + os.files("Includes/**.h"), os.files("Sources/**.h"), os.files("Sources/**.inl"), os.files("Sources/**.c"), @@ -88,6 +89,7 @@ on_run(function() Name = "copyright", Check = function() local files = table.join( + os.files("Includes/**.h"), os.files("Sources/**.h"), os.files("Sources/**.inl"), os.files("Sources/**.c"), diff --git a/xmake.lua b/xmake.lua index 892d938..0825e53 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,4 +1,4 @@ --- Copyright (C) 2024 kanel +-- Copyright (C) 2025 kanel -- This file is part of "Pulse" -- For conditions of distribution and use, see copyright notice in LICENSE