diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 8a165adf43561b1204490a12afa00d2a3fabdbf4..4a51b24f5c978daebc5116e20b527252c8063d61 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -157,6 +157,19 @@ namespace vkcv [[nodiscard]] PipelineHandle createGraphicsPipeline(const PipelineConfig &config); + /** + * Creates a basic vulkan compute pipeline using @p shader program and returns it using the @p handle. + * Fixed Functions for pipeline are set with standard values. + * + * @param shader program that hold the compiles compute shader + * @param handle a handle to return the created vulkan handle + * @return True if pipeline creation was successful, False if not + */ + [[nodiscard]] + PipelineHandle createComputePipeline( + const ShaderProgram &config, + const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts); + /** * Creates a basic vulkan render pass using @p config from the render pass config class and returns it using the @p handle. * Fixed Functions for pipeline are set with standard values. @@ -211,7 +224,7 @@ namespace vkcv */ [[nodiscard]] DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &bindings); - void writeResourceDescription(DescriptorSetHandle handle, size_t setIndex, const DescriptorWrites& writes); + void writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites& writes); DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const; @@ -228,6 +241,13 @@ namespace vkcv const std::vector<DrawcallInfo> &drawcalls, const std::vector<ImageHandle> &renderTargets); + void recordComputeDispatchToCmdStream( + CommandStreamHandle cmdStream, + PipelineHandle computePipeline, + const uint32_t dispatchCount[3], + const std::vector<DescriptorSetUsage> &descriptorSetUsages, + const PushConstantData& pushConstantData); + /** * @brief end recording and present image */ diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp index 3886a53d4127c983e7525c0f6e4ad467129f9ed0..2494793f3b4aff3dfa412ff9bbe27e4550ca8fe0 100644 --- a/projects/cmd_sync_test/src/main.cpp +++ b/projects/cmd_sync_test/src/main.cpp @@ -207,7 +207,7 @@ int main(int argc, const char** argv) { vkcv::SamplerDescriptorWrite(1, sampler), vkcv::SamplerDescriptorWrite(4, shadowSampler) }; setWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(2, lightBuffer.getHandle()) }; - core.writeResourceDescription(descriptorSet, 0, setWrites); + core.writeDescriptorSet(descriptorSet, setWrites); auto start = std::chrono::system_clock::now(); const auto appStartTime = start; diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index 00a77c761e8fe3d67b4724cd0f1ddfa28f3edcb3..15bb872ce21d9042a6e606e791daab54344d128d 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -130,9 +130,9 @@ int main(int argc, const char** argv) { }; vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; - setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; - core.writeResourceDescription(descriptorSet, 0, setWrites); + setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; + setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; + core.writeDescriptorSet(descriptorSet, setWrites); vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle(); diff --git a/projects/first_triangle/shaders/comp.spv b/projects/first_triangle/shaders/comp.spv new file mode 100644 index 0000000000000000000000000000000000000000..b414e36b2bea66dab00746298e536d029091e0fd Binary files /dev/null and b/projects/first_triangle/shaders/comp.spv differ diff --git a/projects/first_triangle/shaders/compile.bat b/projects/first_triangle/shaders/compile.bat index b4521235c40fe5fb163bab874560c2f219b7517f..17743a7c49cdfc6e091c43a42a0adb755a731682 100644 --- a/projects/first_triangle/shaders/compile.bat +++ b/projects/first_triangle/shaders/compile.bat @@ -1,3 +1,4 @@ %VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv %VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv +%VULKAN_SDK%\Bin32\glslc.exe shader.comp -o comp.spv pause \ No newline at end of file diff --git a/projects/first_triangle/shaders/shader.comp b/projects/first_triangle/shaders/shader.comp new file mode 100644 index 0000000000000000000000000000000000000000..fad6cd0815f2f09bf92dcc3171e2e3723f5466df --- /dev/null +++ b/projects/first_triangle/shaders/shader.comp @@ -0,0 +1,25 @@ +#version 440 + +layout(std430, binding = 0) buffer testBuffer +{ + float test1[10]; + float test2[10]; + float test3[10]; +}; + +layout( push_constant ) uniform constants{ + float pushConstant; +}; + +layout(local_size_x = 5) in; + +void main(){ + + if(gl_GlobalInvocationID.x >= 10){ + return; + } + + test1[gl_GlobalInvocationID.x] = gl_GlobalInvocationID.x; + test2[gl_GlobalInvocationID.x] = 69; // nice! + test3[gl_GlobalInvocationID.x] = pushConstant; +} \ No newline at end of file diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 2ede653ff98e19159e0155b282cab1b309a13816..ca10434bb9e486649411bcaac54c432744a914bb 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -92,6 +92,7 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } + // Graphics Pipeline vkcv::ShaderProgram triangleShaderProgram{}; triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/vert.spv")); triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/frag.spv")); @@ -115,6 +116,30 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } + // Compute Pipeline + vkcv::ShaderProgram computeShaderProgram{}; + computeShaderProgram.addShader(vkcv::ShaderStage::COMPUTE, std::filesystem::path("shaders/comp.spv")); + computeShaderProgram.reflectShader(vkcv::ShaderStage::COMPUTE); + + // take care, assuming shader has exactly one descriptor set + vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeShaderProgram.getReflectedDescriptors()[0]); + + vkcv::PipelineHandle computePipeline = core.createComputePipeline( + computeShaderProgram, + { core.getDescriptorSet(computeDescriptorSet).layout }); + + struct ComputeTestBuffer { + float test1[10]; + float test2[10]; + float test3[10]; + }; + + vkcv::Buffer computeTestBuffer = core.createBuffer<ComputeTestBuffer>(vkcv::BufferType::STORAGE, 1); + + vkcv::DescriptorWrites computeDescriptorWrites; + computeDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, computeTestBuffer.getHandle()) }; + core.writeDescriptorSet(computeDescriptorSet, computeDescriptorWrites); + /* * BufferHandle triangleVertices = core.createBuffer(vertices); * BufferHandle triangleIndices = core.createBuffer(indices); @@ -164,6 +189,17 @@ int main(int argc, const char** argv) { pushConstantData, { drawcall }, { swapchainInput }); + + const uint32_t dispatchSize[3] = { 2, 1, 1 }; + const float theMeaningOfLife = 42; + + core.recordComputeDispatchToCmdStream( + cmdStream, + computePipeline, + dispatchSize, + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(computeDescriptorSet).vulkanHandle) }, + vkcv::PushConstantData((void*)&theMeaningOfLife, sizeof(theMeaningOfLife))); + core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index cccf51946ba41ed17b4479d151892e946ff17844..44e7111e1f4941ef2f0f8114ac788d7db4a13b5a 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -103,6 +103,13 @@ namespace vkcv return m_PipelineManager->createPipeline(config, *m_PassManager); } + PipelineHandle Core::createComputePipeline( + const ShaderProgram &shaderProgram, + const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts) + { + return m_PipelineManager->createComputePipeline(shaderProgram, descriptorSetLayouts); + } + PassHandle Core::createPass(const PassConfig &config) { @@ -300,6 +307,40 @@ namespace vkcv recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction); } + void Core::recordComputeDispatchToCmdStream( + CommandStreamHandle cmdStreamHandle, + PipelineHandle computePipeline, + const uint32_t dispatchCount[3], + const std::vector<DescriptorSetUsage>& descriptorSetUsages, + const PushConstantData& pushConstantData) { + + auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { + + const auto pipelineLayout = m_PipelineManager->getVkPipelineLayout(computePipeline); + + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_PipelineManager->getVkPipeline(computePipeline)); + for (const auto& usage : descriptorSetUsages) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eCompute, + pipelineLayout, + usage.setLocation, + { usage.vulkanHandle }, + {}); + } + if (pushConstantData.sizePerDrawcall > 0) { + cmdBuffer.pushConstants( + pipelineLayout, + vk::ShaderStageFlagBits::eCompute, + 0, + pushConstantData.sizePerDrawcall, + pushConstantData.data); + } + cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]); + }; + + recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr); + } + void Core::endFrame() { if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { return; @@ -404,10 +445,9 @@ namespace vkcv return m_DescriptorManager->createDescriptorSet(bindings); } - void Core::writeResourceDescription(DescriptorSetHandle handle, size_t setIndex, const DescriptorWrites &writes) { - m_DescriptorManager->writeResourceDescription( - handle, - setIndex, + void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) { + m_DescriptorManager->writeDescriptorSet( + handle, writes, *m_ImageManager, *m_BufferManager, diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp index b805e1367d334d6a2b02a0a19a5a351a5e2da1bf..f591daf90b47b57a758b2b24c7fa87b5c33e3c46 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -92,9 +92,8 @@ namespace vkcv vk::DescriptorType type; }; - void DescriptorManager::writeResourceDescription( + void DescriptorManager::writeDescriptorSet( const DescriptorSetHandle &handle, - size_t setIndex, const DescriptorWrites &writes, const ImageManager &imageManager, const BufferManager &bufferManager, diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp index d8607b9312b25e71c7eb4af009efd92b834b40ec..d18be64f3b069af68cecce68f6fa623c81f8dfa4 100644 --- a/src/vkcv/DescriptorManager.hpp +++ b/src/vkcv/DescriptorManager.hpp @@ -23,9 +23,8 @@ namespace vkcv DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &descriptorBindings); - void writeResourceDescription( + void writeDescriptorSet( const DescriptorSetHandle &handle, - size_t setIndex, const DescriptorWrites &writes, const ImageManager &imageManager, const BufferManager &bufferManager, diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 6572145d34492204b2df1e55028d22c330802ca1..24ac7970d739d46ab7cecca7bb783bb0037c43cb 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -193,6 +193,7 @@ namespace vkcv {}, (config.m_DescriptorLayouts), (pushConstantRange)); + vk::PipelineLayout vkPipelineLayout{}; if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) { @@ -325,4 +326,65 @@ namespace vkcv return m_Configs.at(id); } + PipelineHandle PipelineManager::createComputePipeline( + const ShaderProgram &shaderProgram, + const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts) { + + // Temporally handing over the Shader Program instead of a pipeline config + vk::ShaderModule computeModule{}; + if (createShaderModule(computeModule, shaderProgram, ShaderStage::COMPUTE) != vk::Result::eSuccess) + return PipelineHandle(); + + vk::PipelineShaderStageCreateInfo pipelineComputeShaderStageInfo( + {}, + vk::ShaderStageFlagBits::eCompute, + computeModule, + "main", + nullptr + ); + + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, descriptorSetLayouts); + + const size_t pushConstantSize = shaderProgram.getPushConstantSize(); + vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eCompute, 0, pushConstantSize); + if (pushConstantSize > 0) { + pipelineLayoutCreateInfo.setPushConstantRangeCount(1); + pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstantRange); + } + + vk::PipelineLayout vkPipelineLayout{}; + if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) + { + m_Device.destroy(computeModule); + return PipelineHandle(); + } + + vk::ComputePipelineCreateInfo computePipelineCreateInfo{}; + computePipelineCreateInfo.stage = pipelineComputeShaderStageInfo; + computePipelineCreateInfo.layout = vkPipelineLayout; + + vk::Pipeline vkPipeline; + if (m_Device.createComputePipelines(nullptr, 1, &computePipelineCreateInfo, nullptr, &vkPipeline)!= vk::Result::eSuccess) + { + m_Device.destroy(computeModule); + return PipelineHandle(); + } + + m_Device.destroy(computeModule); + + const uint64_t id = m_Pipelines.size(); + m_Pipelines.push_back({ vkPipeline, vkPipelineLayout }); + + return PipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); }); + } + + // There is an issue for refactoring the Pipeline Manager. + // While including Compute Pipeline Creation, some private helper functions where introduced: + + vk::Result PipelineManager::createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, const ShaderStage stage) + { + std::vector<char> code = shaderProgram.getShader(stage).shaderCode; + vk::ShaderModuleCreateInfo moduleInfo({}, code.size(), reinterpret_cast<uint32_t*>(code.data())); + return m_Device.createShaderModule(&moduleInfo, nullptr, &module); + } } \ No newline at end of file diff --git a/src/vkcv/PipelineManager.hpp b/src/vkcv/PipelineManager.hpp index e243151f7248c07fa0287bb2eaf698e5080f7f61..634f5f4e6464532306e35fd10d9a1623df6ace16 100644 --- a/src/vkcv/PipelineManager.hpp +++ b/src/vkcv/PipelineManager.hpp @@ -21,7 +21,9 @@ namespace vkcv std::vector<PipelineConfig> m_Configs; void destroyPipelineById(uint64_t id); - + + vk::Result createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, ShaderStage stage); + public: PipelineManager() = delete; // no default ctor explicit PipelineManager(vk::Device device) noexcept; // ctor @@ -35,6 +37,10 @@ namespace vkcv PipelineHandle createPipeline(const PipelineConfig &config, PassManager& passManager); + PipelineHandle createComputePipeline( + const ShaderProgram& shaderProgram, + const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts); + [[nodiscard]] vk::Pipeline getVkPipeline(const PipelineHandle &handle) const;