From d0eb9e08769ec45dea76624e449be0dd91647a3b Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Mon, 18 Nov 2024 17:23:14 +0100 Subject: [PATCH] working on Vulkahn compute pipelines --- Examples/Vulkan/.gitignore | 1 + Examples/Vulkan/main.c | 15 +++ Examples/Vulkan/shader.nzsl | 13 +++ Examples/Vulkan/xmake.lua | 92 +++++++++++++++++++ Examples/xmake.lua | 1 - Includes/Pulse.h | 13 +-- Sources/Backends/Vulkan/Vulkan.h | 3 +- Sources/Backends/Vulkan/VulkanCommandList.c | 10 +- .../Backends/Vulkan/VulkanComputePipeline.c | 78 +++++++++++++++- .../Backends/Vulkan/VulkanComputePipeline.h | 5 +- Sources/Backends/Vulkan/VulkanDevice.c | 4 +- Sources/Backends/Vulkan/VulkanFence.c | 15 ++- Sources/PulseCommandList.c | 15 +-- Sources/PulseComputePipeline.c | 45 ++++++++- Sources/PulseDefs.h | 2 +- Sources/PulseEnums.h | 1 - Sources/PulseInternal.h | 5 +- Sources/PulsePFNs.h | 2 +- 18 files changed, 278 insertions(+), 42 deletions(-) create mode 100644 Examples/Vulkan/.gitignore create mode 100644 Examples/Vulkan/shader.nzsl diff --git a/Examples/Vulkan/.gitignore b/Examples/Vulkan/.gitignore new file mode 100644 index 0000000..289a601 --- /dev/null +++ b/Examples/Vulkan/.gitignore @@ -0,0 +1 @@ +*.spv.h diff --git a/Examples/Vulkan/main.c b/Examples/Vulkan/main.c index 691c97b..e40f8fb 100644 --- a/Examples/Vulkan/main.c +++ b/Examples/Vulkan/main.c @@ -31,11 +31,25 @@ int main(void) PulseDevice device = PulseCreateDevice(backend, NULL, 0); CHECK_PULSE_HANDLE_RETVAL(device, 1); + const uint8_t shader_bytecode[] = { + #include "shader.spv.h" + }; + + PulseComputePipelineCreateInfo info = {}; + info.code_size = sizeof(shader_bytecode); + info.code = shader_bytecode; + info.entrypoint = "main"; + info.format = PULSE_SHADER_FORMAT_SPIRV_BIT; + + PulseComputePipeline pipeline = PulseCreateComputePipeline(device, &info); + CHECK_PULSE_HANDLE_RETVAL(pipeline, 1); + PulseFence fence = PulseCreateFence(device); CHECK_PULSE_HANDLE_RETVAL(fence, 1); PulseCommandList cmd = PulseRequestCommandList(device, PULSE_COMMAND_LIST_GENERAL); CHECK_PULSE_HANDLE_RETVAL(cmd, 1); + PulseDispatchComputePipeline(pipeline, cmd, 8, 8, 8); if(!PulseSubmitCommandList(device, cmd, fence)) fprintf(stderr, "Could not submit command list, %s\n", PulseVerbaliseErrorType(PulseGetLastErrorType())); @@ -44,6 +58,7 @@ int main(void) PulseReleaseCommandList(device, cmd); PulseDestroyFence(device, fence); + PulseDestroyComputePipeline(device, pipeline); PulseDestroyDevice(device); PulseUnloadBackend(backend); diff --git a/Examples/Vulkan/shader.nzsl b/Examples/Vulkan/shader.nzsl new file mode 100644 index 0000000..4195d70 --- /dev/null +++ b/Examples/Vulkan/shader.nzsl @@ -0,0 +1,13 @@ +[nzsl_version("1.0")] +module; + +struct Input +{ + [builtin(global_invocation_indices)] indices: vec3[u32] +} + +[entry(compute)] +[workgroup(8, 8, 8)] +fn main(input: Input) +{ +} diff --git a/Examples/Vulkan/xmake.lua b/Examples/Vulkan/xmake.lua index 77dab0a..1a24af4 100644 --- a/Examples/Vulkan/xmake.lua +++ b/Examples/Vulkan/xmake.lua @@ -1,7 +1,99 @@ +add_repositories("nazara-engine-repo https://github.com/NazaraEngine/xmake-repo") + +add_requires("nzsl >=2023.12.31", { configs = { shared = false, nzslc = true } }) + +if is_cross() then + add_requires("nzsl~host", { kind = "binary", host = true }) +end + +-- Yoinked from NZSL xmake repo +rule("find_nzsl") + on_config(function(target) + import("core.project.project") + import("core.tool.toolchain") + import("lib.detect.find_tool") + + local envs + if is_plat("windows") then + local msvc = target:toolchain("msvc") + if msvc and msvc:check() then + envs = msvc:runenvs() + end + elseif is_plat("mingw") then + local mingw = target:toolchain("mingw") + if mingw and mingw:check() then + envs = mingw:runenvs() + end + end + target:data_set("nzsl_envs", envs) + + local nzsl = project.required_package("nzsl~host") or project.required_package("nzsl") + local nzsldir + if nzsl then + nzsldir = path.join(nzsl:installdir(), "bin") + local osenvs = os.getenvs() + envs = envs or {} + for env, values in pairs(nzsl:get("envs")) do + local flatval = path.joinenv(values) + local oldenv = envs[env] or osenvs[env] + if not oldenv or oldenv == "" then + envs[env] = flatval + elseif not oldenv:startswith(flatval) then + envs[env] = flatval .. path.envsep() .. oldenv + end + end + end + + local nzsla = find_tool("nzsla", { version = true, paths = nzsldir, envs = envs }) + local nzslc = find_tool("nzslc", { version = true, paths = nzsldir, envs = envs }) + + target:data_set("nzsla", nzsla) + target:data_set("nzslc", nzslc) + target:data_set("nzsl_runenv", envs) + end) +rule_end() + +rule("compile_shaders") + set_extensions(".nzsl") + add_deps("find_nzsl") + + before_buildcmd_file(function(target, batchcmds, shaderfile, opt) + local outputdir = target:data("nzsl_includedirs") + local nzslc = target:data("nzslc") + local runenvs = target:data("nzsl_runenv") + assert(nzslc, "nzslc not found! please install nzsl package with nzslc enabled") + + batchcmds:show_progress(opt.progress, "${color.build.object}compiling.shader %s", shaderfile) + local argv = { "--compile=spv-header", "--optimize" } + if outputdir then + batchcmds:mkdir(outputdir) + table.insert(argv, "--output=" .. outputdir) + end + + local kind = target:data("plugin.project.kind") or "" + if kind:match("vs") then + table.insert(argv, "--log-format=vs") + end + + table.insert(argv, shaderfile) + + batchcmds:vrunv(nzslc.program, argv, { curdir = ".", envs = runenvs }) + + local outputfile = path.join(outputdir or path.directory(shaderfile), path.basename(shaderfile) .. ".spv.h") + + batchcmds:add_depfiles(shaderfile) + batchcmds:add_depvalues(nzslc.version) + batchcmds:set_depmtime(os.mtime(outputfile)) + batchcmds:set_depcache(target:dependfile(outputfile)) + end) +rule_end() + target("VulkanExample") add_deps("pulse_gpu") if is_plat("linux") then set_extension(".x86_64") end + add_rules("compile_shaders") add_files("*.c") + add_files("*.nzsl") target_end() diff --git a/Examples/xmake.lua b/Examples/xmake.lua index 65bdc48..4465e2c 100644 --- a/Examples/xmake.lua +++ b/Examples/xmake.lua @@ -4,4 +4,3 @@ if has_config("examples") then set_group("Examples") includes("*/xmake.lua") end - diff --git a/Includes/Pulse.h b/Includes/Pulse.h index 909f159..035cc8c 100644 --- a/Includes/Pulse.h +++ b/Includes/Pulse.h @@ -98,6 +98,7 @@ typedef enum PulseErrorType { PULSE_ERROR_NONE, PULSE_ERROR_BACKENDS_CANDIDATES_SHADER_FORMAT_MISMATCH, + PULSE_ERROR_INVALID_BACKEND, PULSE_ERROR_INITIALIZATION_FAILED, PULSE_ERROR_INVALID_HANDLE, PULSE_ERROR_CPU_ALLOCATION_FAILED, @@ -219,18 +220,8 @@ typedef struct PulseComputePipelineCreateInfo uint32_t num_readwrite_storage_images; uint32_t num_readwrite_storage_buffers; uint32_t num_uniform_buffers; - uint32_t threadcount_x; - uint32_t threadcount_y; - uint32_t threadcount_z; } PulseComputePipelineCreateInfo; -typedef struct PulseIndirectDispatchCommand -{ - uint32_t groupcount_x; - uint32_t groupcount_y; - uint32_t groupcount_z; -} PulseIndirectDispatchCommand; - typedef struct PulseImageCreateInfo { PulseImageType type; @@ -287,8 +278,8 @@ PULSE_API bool PulseIsFenceReady(PulseDevice device, PulseFence fence); PULSE_API bool PulseWaitForFences(PulseDevice device, const PulseFence* fences, uint32_t fences_count, bool wait_for_all); PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info); +PULSE_API void PulseDispatchComputePipeline(PulseComputePipeline pipeline, PulseCommandList cmd, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z); PULSE_API void PulseDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline); -PULSE_API void PulseBindComputePipeline(PulseComputePipeline pipeline); PULSE_API PulseErrorType PulseGetLastErrorType(); // /!\ Warning /!\ Call to this function resets the internal last error variable PULSE_API const char* PulseVerbaliseErrorType(PulseErrorType error); diff --git a/Sources/Backends/Vulkan/Vulkan.h b/Sources/Backends/Vulkan/Vulkan.h index 21623d5..b1bea2b 100644 --- a/Sources/Backends/Vulkan/Vulkan.h +++ b/Sources/Backends/Vulkan/Vulkan.h @@ -20,11 +20,10 @@ if((res) != VK_SUCCESS) \ { \ if(backend != PULSE_NULL_HANDLE && PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(backend)) \ - PulseLogErrorFmt(backend, "(Vulkan) Call to Vulkan function failed due to %s", VulkanVerbaliseResult(res)); \ + PulseLogErrorFmt(backend, "(Vulkan) call to Vulkan function failed due to %s", VulkanVerbaliseResult(res)); \ PulseSetInternalError(error); \ return retval; \ } - #define CHECK_VK(backend, res, error) CHECK_VK_RETVAL(backend, res, error, ) typedef struct VulkanGlobal diff --git a/Sources/Backends/Vulkan/VulkanCommandList.c b/Sources/Backends/Vulkan/VulkanCommandList.c index b8e0102..25ccb4b 100644 --- a/Sources/Backends/Vulkan/VulkanCommandList.c +++ b/Sources/Backends/Vulkan/VulkanCommandList.c @@ -78,9 +78,8 @@ PulseCommandList VulkanRequestCommandList(PulseDevice device, PulseCommandListUs } cmd->compute_pipelines_bound_size = 0; - cmd->state = PULSE_COMMAND_LIST_STATE_EMPTY; + cmd->state = PULSE_COMMAND_LIST_STATE_RECORDING; cmd->is_available = false; - cmd->is_compute_pipeline_bound = false; VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(cmd, VulkanCommandList*); @@ -103,7 +102,6 @@ PulseCommandList VulkanRequestCommandList(PulseDevice device, PulseCommandListUs bool VulkanSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence) { - PULSE_UNUSED(device); PULSE_CHECK_HANDLE_RETVAL(cmd, false); VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); @@ -121,8 +119,9 @@ bool VulkanSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFenc VkFence vulkan_fence; if(fence != PULSE_NULL_HANDLE) { - vulkan_fence = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VkFence); - vulkan_device->vkResetFences(vulkan_device->device, 1, &vulkan_fence); + vulkan_fence = VULKAN_RETRIEVE_DRIVER_DATA_AS(fence, VkFence); + CHECK_VK_RETVAL(device->backend, vulkan_device->vkResetFences(vulkan_device->device, 1, &vulkan_fence), PULSE_ERROR_DEVICE_ALLOCATION_FAILED, false); + fence->cmd = cmd; } VulkanQueue* vulkan_queue; @@ -140,6 +139,7 @@ bool VulkanSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFenc submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &vulkan_cmd->cmd; res = vulkan_device->vkQueueSubmit(vulkan_queue->queue, 1, &submit_info, vulkan_fence); + cmd->state = PULSE_COMMAND_LIST_STATE_SENT; switch(res) { case VK_SUCCESS: return true; diff --git a/Sources/Backends/Vulkan/VulkanComputePipeline.c b/Sources/Backends/Vulkan/VulkanComputePipeline.c index 6748cb9..a213842 100644 --- a/Sources/Backends/Vulkan/VulkanComputePipeline.c +++ b/Sources/Backends/Vulkan/VulkanComputePipeline.c @@ -2,16 +2,92 @@ // This file is part of "Pulse" // For conditions of distribution and use, see copyright notice in LICENSE +#include "Vulkan.h" +#include "VulkanDevice.h" +#include "VulkanCommandList.h" #include "VulkanComputePipeline.h" PulseComputePipeline VulkanCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info) { + VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); + + PulseComputePipelineHandler* pipeline = (PulseComputePipelineHandler*)calloc(1, sizeof(PulseComputePipelineHandler)); + PULSE_CHECK_ALLOCATION_RETVAL(pipeline, PULSE_NULL_HANDLE); + + VulkanComputePipeline* vulkan_pipeline = (VulkanComputePipeline*)calloc(1, sizeof(VulkanComputePipeline)); + PULSE_CHECK_ALLOCATION_RETVAL(vulkan_pipeline, PULSE_NULL_HANDLE); + + pipeline->driver_data = vulkan_pipeline; + + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) + { + if(info->code == PULSE_NULLPTR) + PulseLogError(device->backend, "invalid code pointer passed to PulseComputePipelineCreateInfo"); + if(info->entrypoint == PULSE_NULLPTR) + PulseLogError(device->backend, "invalid entrypoint pointer passed to PulseComputePipelineCreateInfo"); + if(info->format == PULSE_SHADER_FORMAT_SPIRV_BIT && (device->backend->supported_shader_formats & PULSE_SHADER_FORMAT_SPIRV_BIT) == 0) + PulseLogError(device->backend, "invalid shader format passed to PulseComputePipelineCreateInfo"); + } + + VkShaderModuleCreateInfo shader_module_create_info = {}; + shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_module_create_info.codeSize = info->code_size; + shader_module_create_info.pCode = (const uint32_t*)info->code; + CHECK_VK_RETVAL(device->backend, vulkan_device->vkCreateShaderModule(vulkan_device->device, &shader_module_create_info, PULSE_NULLPTR, &vulkan_pipeline->module), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULL_HANDLE); + + VkPipelineShaderStageCreateInfo shader_stage_info = {}; + shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stage_info.stage = VK_SHADER_STAGE_COMPUTE_BIT; + shader_stage_info.module = vulkan_pipeline->module; + shader_stage_info.pName = info->entrypoint; + + VkPipelineLayoutCreateInfo pipeline_layout_info = {}; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 0; + pipeline_layout_info.pSetLayouts = PULSE_NULLPTR; // will change + CHECK_VK_RETVAL(device->backend, vulkan_device->vkCreatePipelineLayout(vulkan_device->device, &pipeline_layout_info, PULSE_NULLPTR, &vulkan_pipeline->layout), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULL_HANDLE); + + VkComputePipelineCreateInfo pipeline_info = {}; + pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipeline_info.layout = vulkan_pipeline->layout; + pipeline_info.stage = shader_stage_info; + + CHECK_VK_RETVAL(device->backend, vulkan_device->vkCreateComputePipelines(vulkan_device->device, VK_NULL_HANDLE, 1, &pipeline_info, PULSE_NULLPTR, &vulkan_pipeline->pipeline), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULL_HANDLE); + + if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(device->backend)) + PulseLogInfoFmt(device->backend, "(Vulkan) created new compute pipeline %p", pipeline); + + return pipeline; } -void VulkanBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline) +void VulkanDispatchComputePipeline(PulseComputePipeline pipeline, PulseCommandList cmd, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z) { + VulkanComputePipeline* vulkan_pipeline = VULKAN_RETRIEVE_DRIVER_DATA_AS(pipeline, VulkanComputePipeline*); + VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(cmd->device, VulkanDevice*); + VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(cmd, VulkanCommandList*); + + vulkan_device->vkCmdBindPipeline(vulkan_cmd->cmd, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_pipeline->pipeline); + vulkan_device->vkCmdDispatch(vulkan_cmd->cmd, groupcount_x, groupcount_y, groupcount_z); } void VulkanDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline) { + if(pipeline == PULSE_NULL_HANDLE) + { + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) + PulseLogWarning(device->backend, "compute pipeline is NULL, this may be a bug in your application"); + return; + } + + VulkanComputePipeline* vulkan_pipeline = VULKAN_RETRIEVE_DRIVER_DATA_AS(pipeline, VulkanComputePipeline*); + VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); + vulkan_device->vkDeviceWaitIdle(vulkan_device->device); + vulkan_device->vkDestroyShaderModule(vulkan_device->device, vulkan_pipeline->module, PULSE_NULLPTR); + vulkan_device->vkDestroyPipelineLayout(vulkan_device->device, vulkan_pipeline->layout, PULSE_NULLPTR); + vulkan_device->vkDestroyPipeline(vulkan_device->device, vulkan_pipeline->pipeline, PULSE_NULLPTR); + free(vulkan_pipeline); + free(pipeline); + + if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(device->backend)) + PulseLogInfoFmt(device->backend, "(Vulkan) destroyed compute pipeline %p", pipeline); } diff --git a/Sources/Backends/Vulkan/VulkanComputePipeline.h b/Sources/Backends/Vulkan/VulkanComputePipeline.h index 4eaafc8..a1be7eb 100644 --- a/Sources/Backends/Vulkan/VulkanComputePipeline.h +++ b/Sources/Backends/Vulkan/VulkanComputePipeline.h @@ -13,10 +13,13 @@ typedef struct VulkanComputePipeline { + VkShaderModule module; + VkPipelineLayout layout; + VkPipeline pipeline; } VulkanComputePipeline; PulseComputePipeline VulkanCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info); -void VulkanBindComputePipeline(PulseComputePipeline pipeline); +void VulkanDispatchComputePipeline(PulseComputePipeline pipeline, PulseCommandList cmd, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z); void VulkanDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline); #endif // PULSE_VULKAN_COMPUTE_PIPELINE_H_ diff --git a/Sources/Backends/Vulkan/VulkanDevice.c b/Sources/Backends/Vulkan/VulkanDevice.c index 7cf8dea..e0181ca 100644 --- a/Sources/Backends/Vulkan/VulkanDevice.c +++ b/Sources/Backends/Vulkan/VulkanDevice.c @@ -227,7 +227,7 @@ PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devic PULSE_LOAD_DRIVER_DEVICE(Vulkan); if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(backend)) - PulseLogInfoFmt(backend, "(Vulkan) Created device from %s", device->properties.deviceName); + PulseLogInfoFmt(backend, "(Vulkan) created device from %s", device->properties.deviceName); return pulse_device; } @@ -241,7 +241,7 @@ void VulkanDestroyDevice(PulseDevice device) vmaDestroyAllocator(vulkan_device->allocator); vulkan_device->vkDestroyDevice(vulkan_device->device, PULSE_NULLPTR); if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(device->backend)) - PulseLogInfoFmt(device->backend, "(Vulkan) Destroyed device created from %s", vulkan_device->properties.deviceName); + PulseLogInfoFmt(device->backend, "(Vulkan) destroyed device created from %s", vulkan_device->properties.deviceName); free(vulkan_device->cmd_pools); free(vulkan_device); free(device); diff --git a/Sources/Backends/Vulkan/VulkanFence.c b/Sources/Backends/Vulkan/VulkanFence.c index 3dd6d67..b2d2a52 100644 --- a/Sources/Backends/Vulkan/VulkanFence.c +++ b/Sources/Backends/Vulkan/VulkanFence.c @@ -21,6 +21,7 @@ PulseFence VulkanCreateFence(PulseDevice device) PulseFenceHandler* fence = (PulseFenceHandler*)malloc(sizeof(PulseFenceHandler)); PULSE_CHECK_ALLOCATION_RETVAL(fence, PULSE_NULL_HANDLE); + fence->cmd = PULSE_NULL_HANDLE; fence->driver_data = vulkan_fence; return fence; } @@ -60,7 +61,10 @@ bool VulkanIsFenceReady(PulseDevice device, PulseFence fence) VkResult res = vulkan_device->vkGetFenceStatus(vulkan_device->device, vulkan_fence); switch(res) { - case VK_SUCCESS: return true; + case VK_SUCCESS: + if(fence->cmd != PULSE_NULL_HANDLE) + fence->cmd->state = PULSE_COMMAND_LIST_STATE_READY; + return true; case VK_NOT_READY: return false; case VK_ERROR_DEVICE_LOST: PulseSetInternalError(PULSE_ERROR_DEVICE_LOST); return false; @@ -80,12 +84,19 @@ bool VulkanWaitForFences(PulseDevice device, const PulseFence* fences, uint32_t VkFence* vulkan_fences = (VkFence*)calloc(fences_count, sizeof(VkFence)); PULSE_CHECK_ALLOCATION_RETVAL(vulkan_fences, false); for(uint32_t i = 0; i < fences_count; i++) + { + if(fences[i]->cmd == PULSE_NULL_HANDLE && PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) + PulseLogError(device->backend, "cannot wait on a fence that has no command list attached to it"); vulkan_fences[i] = VULKAN_RETRIEVE_DRIVER_DATA_AS(((PulseFence)fences[i]), VkFence); + } VkResult result = vulkan_device->vkWaitForFences(vulkan_device->device, fences_count, vulkan_fences, wait_for_all, UINT64_MAX); free(vulkan_fences); switch(result) { - case VK_SUCCESS: break; + case VK_SUCCESS: + for(uint32_t i = 0; i < fences_count; i++) + fences[i]->cmd->state = PULSE_COMMAND_LIST_STATE_READY; + break; case VK_TIMEOUT: break; case VK_ERROR_DEVICE_LOST: PulseSetInternalError(PULSE_ERROR_DEVICE_LOST); return false; diff --git a/Sources/PulseCommandList.c b/Sources/PulseCommandList.c index 3005864..5a67bc7 100644 --- a/Sources/PulseCommandList.c +++ b/Sources/PulseCommandList.c @@ -16,7 +16,7 @@ PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd, PULSE_CHECK_HANDLE_RETVAL(device, false); PULSE_CHECK_HANDLE_RETVAL(cmd, false); - if(cmd->state != PULSE_COMMAND_LIST_STATE_READY) + if(cmd->state != PULSE_COMMAND_LIST_STATE_RECORDING) { switch(cmd->state) { @@ -25,14 +25,9 @@ PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseLogError(device->backend, "command list is in invalid state"); return false; - case PULSE_COMMAND_LIST_STATE_EMPTY: + case PULSE_COMMAND_LIST_STATE_READY: if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) - PulseLogWarning(device->backend, "command list is empty"); - return false; - - case PULSE_COMMAND_LIST_STATE_RECORDING: - if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) - PulseLogError(device->backend, "command list is in recording state"); + PulseLogError(device->backend, "command list is empty"); return false; case PULSE_COMMAND_LIST_STATE_SENT: @@ -60,9 +55,9 @@ PULSE_API void PulseReleaseCommandList(PulseDevice device, PulseCommandList cmd) switch(cmd->state) { - case PULSE_COMMAND_LIST_STATE_RECORDING: + case PULSE_COMMAND_LIST_STATE_SENT: if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) - PulseLogWarning(device->backend, "command list is in recording state"); + PulseLogWarning(device->backend, "command list is in pending state"); break; default: break; diff --git a/Sources/PulseComputePipeline.c b/Sources/PulseComputePipeline.c index b188bd5..ed1d888 100644 --- a/Sources/PulseComputePipeline.c +++ b/Sources/PulseComputePipeline.c @@ -2,20 +2,61 @@ // This file is part of "Pulse" // For conditions of distribution and use, see copyright notice in LICENSE -#include +#include "PulseDefs.h" #include "PulseInternal.h" PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info) { PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE); + if(info == PULSE_NULLPTR && PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) + PulseLogError(device->backend, "null infos pointer"); + PULSE_CHECK_PTR_RETVAL(info, PULSE_NULL_HANDLE); + return device->PFN_CreateComputePipeline(device, info); } -PULSE_API void PulseBindComputePipeline(PulseComputePipeline pipeline) +PULSE_API void PulseDispatchComputePipeline(PulseComputePipeline pipeline, PulseCommandList cmd, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z) { PULSE_CHECK_HANDLE(pipeline); + PULSE_CHECK_HANDLE(cmd); + + if(cmd->state != PULSE_COMMAND_LIST_STATE_RECORDING) + { + switch(cmd->state) + { + case PULSE_COMMAND_LIST_STATE_INVALID: + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(cmd->device->backend)) + PulseLogError(cmd->device->backend, "command list is in invalid state"); + return; + + case PULSE_COMMAND_LIST_STATE_READY: + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(cmd->device->backend)) + PulseLogError(cmd->device->backend, "command list is not recording"); + return; + + case PULSE_COMMAND_LIST_STATE_SENT: + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(cmd->device->backend)) + PulseLogWarning(cmd->device->backend, "command list has already been submitted"); + return; + + default: break; + } + } + + cmd->device->PFN_DispatchComputePipeline(pipeline, cmd, groupcount_x, groupcount_y, groupcount_z); + pipeline->cmd = cmd; + + if(cmd->compute_pipelines_bound_size == cmd->compute_pipelines_bound_capacity) + { + cmd->compute_pipelines_bound_capacity += 5; + cmd->compute_pipelines_bound = (PulseComputePipeline*)realloc(cmd->compute_pipelines_bound, cmd->compute_pipelines_bound_capacity * sizeof(PulseComputePipeline)); + PULSE_CHECK_ALLOCATION(cmd->compute_pipelines_bound); + } + cmd->compute_pipelines_bound[cmd->compute_pipelines_bound_size] = pipeline; + cmd->compute_pipelines_bound_size++; } PULSE_API void PulseDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline) { PULSE_CHECK_HANDLE(device); + device->PFN_DestroyComputePipeline(device, pipeline); } diff --git a/Sources/PulseDefs.h b/Sources/PulseDefs.h index 69311d4..c6ae67e 100644 --- a/Sources/PulseDefs.h +++ b/Sources/PulseDefs.h @@ -54,7 +54,7 @@ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(CreateComputePipeline, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyComputePipeline, _namespace) \ - PULSE_LOAD_DRIVER_DEVICE_FUNCTION(BindComputePipeline, _namespace) \ + PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DispatchComputePipeline, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(CreateFence, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyFence, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(IsFenceReady, _namespace) \ diff --git a/Sources/PulseEnums.h b/Sources/PulseEnums.h index dad196e..bae0b45 100644 --- a/Sources/PulseEnums.h +++ b/Sources/PulseEnums.h @@ -8,7 +8,6 @@ typedef enum PulseCommandListState { PULSE_COMMAND_LIST_STATE_INVALID, - PULSE_COMMAND_LIST_STATE_EMPTY, PULSE_COMMAND_LIST_STATE_RECORDING, PULSE_COMMAND_LIST_STATE_READY, PULSE_COMMAND_LIST_STATE_SENT diff --git a/Sources/PulseInternal.h b/Sources/PulseInternal.h index bc91513..1d77bf9 100644 --- a/Sources/PulseInternal.h +++ b/Sources/PulseInternal.h @@ -43,12 +43,12 @@ typedef struct PulseCommandListHandler uint32_t compute_pipelines_bound_size; PulseCommandListState state; PulseCommandListUsage usage; - bool is_compute_pipeline_bound; bool is_available; } PulseCommandListHandler; typedef struct PulseComputePipelineHandler { + PulseCommandList cmd; void* driver_data; } PulseComputePipelineHandler; @@ -57,7 +57,7 @@ typedef struct PulseDeviceHandler // PFNs PulseDestroyDevicePFN PFN_DestroyDevice; PulseCreateComputePipelinePFN PFN_CreateComputePipeline; - PulseBindComputePipelinePFN PFN_BindComputePipeline; + PulseDispatchComputePipelinePFN PFN_DispatchComputePipeline; PulseDestroyComputePipelinePFN PFN_DestroyComputePipeline; PulseCreateFencePFN PFN_CreateFence; PulseDestroyFencePFN PFN_DestroyFence; @@ -74,6 +74,7 @@ typedef struct PulseDeviceHandler typedef struct PulseFenceHandler { + PulseCommandList cmd; void* driver_data; } PulseFenceHandler; diff --git a/Sources/PulsePFNs.h b/Sources/PulsePFNs.h index f67d8f7..fe72536 100644 --- a/Sources/PulsePFNs.h +++ b/Sources/PulsePFNs.h @@ -15,7 +15,7 @@ typedef PulseDevice (*PulseCreateDevicePFN)(PulseBackend, PulseDevice*, uint32_t typedef void (*PulseDestroyDevicePFN)(PulseDevice); typedef PulseComputePipeline (*PulseCreateComputePipelinePFN)(PulseDevice, const PulseComputePipelineCreateInfo*); -typedef void (*PulseBindComputePipelinePFN)(PulseComputePipeline); +typedef void (*PulseDispatchComputePipelinePFN)(PulseComputePipeline, PulseCommandList, uint32_t, uint32_t, uint32_t); typedef void (*PulseDestroyComputePipelinePFN)(PulseDevice, PulseComputePipeline); typedef PulseFence (*PulseCreateFencePFN)(PulseDevice); typedef void (*PulseDestroyFencePFN)(PulseDevice, PulseFence);