working on Vulkahn compute pipelines

This commit is contained in:
2024-11-18 17:23:14 +01:00
parent d8b14d1a7e
commit d0eb9e0876
18 changed files with 278 additions and 42 deletions

1
Examples/Vulkan/.gitignore vendored git.filemode.normal_file
View File

@@ -0,0 +1 @@
*.spv.h

View File

@@ -31,11 +31,25 @@ int main(void)
PulseDevice device = PulseCreateDevice(backend, NULL, 0); PulseDevice device = PulseCreateDevice(backend, NULL, 0);
CHECK_PULSE_HANDLE_RETVAL(device, 1); 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); PulseFence fence = PulseCreateFence(device);
CHECK_PULSE_HANDLE_RETVAL(fence, 1); CHECK_PULSE_HANDLE_RETVAL(fence, 1);
PulseCommandList cmd = PulseRequestCommandList(device, PULSE_COMMAND_LIST_GENERAL); PulseCommandList cmd = PulseRequestCommandList(device, PULSE_COMMAND_LIST_GENERAL);
CHECK_PULSE_HANDLE_RETVAL(cmd, 1); CHECK_PULSE_HANDLE_RETVAL(cmd, 1);
PulseDispatchComputePipeline(pipeline, cmd, 8, 8, 8);
if(!PulseSubmitCommandList(device, cmd, fence)) if(!PulseSubmitCommandList(device, cmd, fence))
fprintf(stderr, "Could not submit command list, %s\n", PulseVerbaliseErrorType(PulseGetLastErrorType())); fprintf(stderr, "Could not submit command list, %s\n", PulseVerbaliseErrorType(PulseGetLastErrorType()));
@@ -44,6 +58,7 @@ int main(void)
PulseReleaseCommandList(device, cmd); PulseReleaseCommandList(device, cmd);
PulseDestroyFence(device, fence); PulseDestroyFence(device, fence);
PulseDestroyComputePipeline(device, pipeline);
PulseDestroyDevice(device); PulseDestroyDevice(device);
PulseUnloadBackend(backend); PulseUnloadBackend(backend);

13
Examples/Vulkan/shader.nzsl git.filemode.normal_file
View File

@@ -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)
{
}

View File

@@ -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") target("VulkanExample")
add_deps("pulse_gpu") add_deps("pulse_gpu")
if is_plat("linux") then if is_plat("linux") then
set_extension(".x86_64") set_extension(".x86_64")
end end
add_rules("compile_shaders")
add_files("*.c") add_files("*.c")
add_files("*.nzsl")
target_end() target_end()

View File

@@ -4,4 +4,3 @@ if has_config("examples") then
set_group("Examples") set_group("Examples")
includes("*/xmake.lua") includes("*/xmake.lua")
end end

View File

@@ -98,6 +98,7 @@ typedef enum PulseErrorType
{ {
PULSE_ERROR_NONE, PULSE_ERROR_NONE,
PULSE_ERROR_BACKENDS_CANDIDATES_SHADER_FORMAT_MISMATCH, PULSE_ERROR_BACKENDS_CANDIDATES_SHADER_FORMAT_MISMATCH,
PULSE_ERROR_INVALID_BACKEND,
PULSE_ERROR_INITIALIZATION_FAILED, PULSE_ERROR_INITIALIZATION_FAILED,
PULSE_ERROR_INVALID_HANDLE, PULSE_ERROR_INVALID_HANDLE,
PULSE_ERROR_CPU_ALLOCATION_FAILED, PULSE_ERROR_CPU_ALLOCATION_FAILED,
@@ -219,18 +220,8 @@ typedef struct PulseComputePipelineCreateInfo
uint32_t num_readwrite_storage_images; uint32_t num_readwrite_storage_images;
uint32_t num_readwrite_storage_buffers; uint32_t num_readwrite_storage_buffers;
uint32_t num_uniform_buffers; uint32_t num_uniform_buffers;
uint32_t threadcount_x;
uint32_t threadcount_y;
uint32_t threadcount_z;
} PulseComputePipelineCreateInfo; } PulseComputePipelineCreateInfo;
typedef struct PulseIndirectDispatchCommand
{
uint32_t groupcount_x;
uint32_t groupcount_y;
uint32_t groupcount_z;
} PulseIndirectDispatchCommand;
typedef struct PulseImageCreateInfo typedef struct PulseImageCreateInfo
{ {
PulseImageType type; 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 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 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 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 PulseErrorType PulseGetLastErrorType(); // /!\ Warning /!\ Call to this function resets the internal last error variable
PULSE_API const char* PulseVerbaliseErrorType(PulseErrorType error); PULSE_API const char* PulseVerbaliseErrorType(PulseErrorType error);

View File

@@ -20,11 +20,10 @@
if((res) != VK_SUCCESS) \ if((res) != VK_SUCCESS) \
{ \ { \
if(backend != PULSE_NULL_HANDLE && PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(backend)) \ 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); \ PulseSetInternalError(error); \
return retval; \ return retval; \
} }
#define CHECK_VK(backend, res, error) CHECK_VK_RETVAL(backend, res, error, ) #define CHECK_VK(backend, res, error) CHECK_VK_RETVAL(backend, res, error, )
typedef struct VulkanGlobal typedef struct VulkanGlobal

View File

@@ -78,9 +78,8 @@ PulseCommandList VulkanRequestCommandList(PulseDevice device, PulseCommandListUs
} }
cmd->compute_pipelines_bound_size = 0; 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_available = false;
cmd->is_compute_pipeline_bound = false;
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(cmd, VulkanCommandList*); 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) bool VulkanSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence)
{ {
PULSE_UNUSED(device);
PULSE_CHECK_HANDLE_RETVAL(cmd, false); PULSE_CHECK_HANDLE_RETVAL(cmd, false);
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
@@ -121,8 +119,9 @@ bool VulkanSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFenc
VkFence vulkan_fence; VkFence vulkan_fence;
if(fence != PULSE_NULL_HANDLE) if(fence != PULSE_NULL_HANDLE)
{ {
vulkan_fence = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VkFence); vulkan_fence = VULKAN_RETRIEVE_DRIVER_DATA_AS(fence, VkFence);
vulkan_device->vkResetFences(vulkan_device->device, 1, &vulkan_fence); 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; VulkanQueue* vulkan_queue;
@@ -140,6 +139,7 @@ bool VulkanSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFenc
submit_info.commandBufferCount = 1; submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &vulkan_cmd->cmd; submit_info.pCommandBuffers = &vulkan_cmd->cmd;
res = vulkan_device->vkQueueSubmit(vulkan_queue->queue, 1, &submit_info, vulkan_fence); res = vulkan_device->vkQueueSubmit(vulkan_queue->queue, 1, &submit_info, vulkan_fence);
cmd->state = PULSE_COMMAND_LIST_STATE_SENT;
switch(res) switch(res)
{ {
case VK_SUCCESS: return true; case VK_SUCCESS: return true;

View File

@@ -2,16 +2,92 @@
// This file is part of "Pulse" // This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE // For conditions of distribution and use, see copyright notice in LICENSE
#include "Vulkan.h"
#include "VulkanDevice.h"
#include "VulkanCommandList.h"
#include "VulkanComputePipeline.h" #include "VulkanComputePipeline.h"
PulseComputePipeline VulkanCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info) 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) 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);
} }

View File

@@ -13,10 +13,13 @@
typedef struct VulkanComputePipeline typedef struct VulkanComputePipeline
{ {
VkShaderModule module;
VkPipelineLayout layout;
VkPipeline pipeline;
} VulkanComputePipeline; } VulkanComputePipeline;
PulseComputePipeline VulkanCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info); 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); void VulkanDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline);
#endif // PULSE_VULKAN_COMPUTE_PIPELINE_H_ #endif // PULSE_VULKAN_COMPUTE_PIPELINE_H_

View File

@@ -227,7 +227,7 @@ PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devic
PULSE_LOAD_DRIVER_DEVICE(Vulkan); PULSE_LOAD_DRIVER_DEVICE(Vulkan);
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(backend)) 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; return pulse_device;
} }
@@ -241,7 +241,7 @@ void VulkanDestroyDevice(PulseDevice device)
vmaDestroyAllocator(vulkan_device->allocator); vmaDestroyAllocator(vulkan_device->allocator);
vulkan_device->vkDestroyDevice(vulkan_device->device, PULSE_NULLPTR); vulkan_device->vkDestroyDevice(vulkan_device->device, PULSE_NULLPTR);
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(device->backend)) 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->cmd_pools);
free(vulkan_device); free(vulkan_device);
free(device); free(device);

View File

@@ -21,6 +21,7 @@ PulseFence VulkanCreateFence(PulseDevice device)
PulseFenceHandler* fence = (PulseFenceHandler*)malloc(sizeof(PulseFenceHandler)); PulseFenceHandler* fence = (PulseFenceHandler*)malloc(sizeof(PulseFenceHandler));
PULSE_CHECK_ALLOCATION_RETVAL(fence, PULSE_NULL_HANDLE); PULSE_CHECK_ALLOCATION_RETVAL(fence, PULSE_NULL_HANDLE);
fence->cmd = PULSE_NULL_HANDLE;
fence->driver_data = vulkan_fence; fence->driver_data = vulkan_fence;
return fence; return fence;
} }
@@ -60,7 +61,10 @@ bool VulkanIsFenceReady(PulseDevice device, PulseFence fence)
VkResult res = vulkan_device->vkGetFenceStatus(vulkan_device->device, vulkan_fence); VkResult res = vulkan_device->vkGetFenceStatus(vulkan_device->device, vulkan_fence);
switch(res) 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_NOT_READY: return false;
case VK_ERROR_DEVICE_LOST: PulseSetInternalError(PULSE_ERROR_DEVICE_LOST); 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)); VkFence* vulkan_fences = (VkFence*)calloc(fences_count, sizeof(VkFence));
PULSE_CHECK_ALLOCATION_RETVAL(vulkan_fences, false); PULSE_CHECK_ALLOCATION_RETVAL(vulkan_fences, false);
for(uint32_t i = 0; i < fences_count; i++) 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); 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); VkResult result = vulkan_device->vkWaitForFences(vulkan_device->device, fences_count, vulkan_fences, wait_for_all, UINT64_MAX);
free(vulkan_fences); free(vulkan_fences);
switch(result) 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_TIMEOUT: break;
case VK_ERROR_DEVICE_LOST: PulseSetInternalError(PULSE_ERROR_DEVICE_LOST); return false; case VK_ERROR_DEVICE_LOST: PulseSetInternalError(PULSE_ERROR_DEVICE_LOST); return false;

View File

@@ -16,7 +16,7 @@ PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd,
PULSE_CHECK_HANDLE_RETVAL(device, false); PULSE_CHECK_HANDLE_RETVAL(device, false);
PULSE_CHECK_HANDLE_RETVAL(cmd, 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) switch(cmd->state)
{ {
@@ -25,14 +25,9 @@ PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd,
PulseLogError(device->backend, "command list is in invalid state"); PulseLogError(device->backend, "command list is in invalid state");
return false; return false;
case PULSE_COMMAND_LIST_STATE_EMPTY: case PULSE_COMMAND_LIST_STATE_READY:
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
PulseLogWarning(device->backend, "command list is empty"); PulseLogError(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");
return false; return false;
case PULSE_COMMAND_LIST_STATE_SENT: case PULSE_COMMAND_LIST_STATE_SENT:
@@ -60,9 +55,9 @@ PULSE_API void PulseReleaseCommandList(PulseDevice device, PulseCommandList cmd)
switch(cmd->state) switch(cmd->state)
{ {
case PULSE_COMMAND_LIST_STATE_RECORDING: case PULSE_COMMAND_LIST_STATE_SENT:
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) 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; break;
default: break; default: break;

View File

@@ -2,20 +2,61 @@
// This file is part of "Pulse" // This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE // For conditions of distribution and use, see copyright notice in LICENSE
#include <Pulse.h> #include "PulseDefs.h"
#include "PulseInternal.h" #include "PulseInternal.h"
PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info) PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info)
{ {
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE); 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(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_API void PulseDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline)
{ {
PULSE_CHECK_HANDLE(device); PULSE_CHECK_HANDLE(device);
device->PFN_DestroyComputePipeline(device, pipeline);
} }

View File

@@ -54,7 +54,7 @@
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(CreateComputePipeline, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(CreateComputePipeline, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyComputePipeline, _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(CreateFence, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyFence, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyFence, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(IsFenceReady, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(IsFenceReady, _namespace) \

View File

@@ -8,7 +8,6 @@
typedef enum PulseCommandListState typedef enum PulseCommandListState
{ {
PULSE_COMMAND_LIST_STATE_INVALID, PULSE_COMMAND_LIST_STATE_INVALID,
PULSE_COMMAND_LIST_STATE_EMPTY,
PULSE_COMMAND_LIST_STATE_RECORDING, PULSE_COMMAND_LIST_STATE_RECORDING,
PULSE_COMMAND_LIST_STATE_READY, PULSE_COMMAND_LIST_STATE_READY,
PULSE_COMMAND_LIST_STATE_SENT PULSE_COMMAND_LIST_STATE_SENT

View File

@@ -43,12 +43,12 @@ typedef struct PulseCommandListHandler
uint32_t compute_pipelines_bound_size; uint32_t compute_pipelines_bound_size;
PulseCommandListState state; PulseCommandListState state;
PulseCommandListUsage usage; PulseCommandListUsage usage;
bool is_compute_pipeline_bound;
bool is_available; bool is_available;
} PulseCommandListHandler; } PulseCommandListHandler;
typedef struct PulseComputePipelineHandler typedef struct PulseComputePipelineHandler
{ {
PulseCommandList cmd;
void* driver_data; void* driver_data;
} PulseComputePipelineHandler; } PulseComputePipelineHandler;
@@ -57,7 +57,7 @@ typedef struct PulseDeviceHandler
// PFNs // PFNs
PulseDestroyDevicePFN PFN_DestroyDevice; PulseDestroyDevicePFN PFN_DestroyDevice;
PulseCreateComputePipelinePFN PFN_CreateComputePipeline; PulseCreateComputePipelinePFN PFN_CreateComputePipeline;
PulseBindComputePipelinePFN PFN_BindComputePipeline; PulseDispatchComputePipelinePFN PFN_DispatchComputePipeline;
PulseDestroyComputePipelinePFN PFN_DestroyComputePipeline; PulseDestroyComputePipelinePFN PFN_DestroyComputePipeline;
PulseCreateFencePFN PFN_CreateFence; PulseCreateFencePFN PFN_CreateFence;
PulseDestroyFencePFN PFN_DestroyFence; PulseDestroyFencePFN PFN_DestroyFence;
@@ -74,6 +74,7 @@ typedef struct PulseDeviceHandler
typedef struct PulseFenceHandler typedef struct PulseFenceHandler
{ {
PulseCommandList cmd;
void* driver_data; void* driver_data;
} PulseFenceHandler; } PulseFenceHandler;

View File

@@ -15,7 +15,7 @@ typedef PulseDevice (*PulseCreateDevicePFN)(PulseBackend, PulseDevice*, uint32_t
typedef void (*PulseDestroyDevicePFN)(PulseDevice); typedef void (*PulseDestroyDevicePFN)(PulseDevice);
typedef PulseComputePipeline (*PulseCreateComputePipelinePFN)(PulseDevice, const PulseComputePipelineCreateInfo*); 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 void (*PulseDestroyComputePipelinePFN)(PulseDevice, PulseComputePipeline);
typedef PulseFence (*PulseCreateFencePFN)(PulseDevice); typedef PulseFence (*PulseCreateFencePFN)(PulseDevice);
typedef void (*PulseDestroyFencePFN)(PulseDevice, PulseFence); typedef void (*PulseDestroyFencePFN)(PulseDevice, PulseFence);