diff --git a/include/Nazara/VulkanRenderer/VulkanDevice.hpp b/include/Nazara/VulkanRenderer/VulkanDevice.hpp index 753488bf4..d8dc546b6 100644 --- a/include/Nazara/VulkanRenderer/VulkanDevice.hpp +++ b/include/Nazara/VulkanRenderer/VulkanDevice.hpp @@ -29,8 +29,8 @@ namespace Nz std::shared_ptr InstantiateRenderPass(std::vector attachments, std::vector subpassDescriptions, std::vector subpassDependencies) override; std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override; - std::shared_ptr InstantiateShaderModule(const ShaderAst& shaderAst, const ShaderWriter::States& states) override; - std::shared_ptr InstantiateShaderModule(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) override; + std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states) override; + std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderLanguage lang, const void* source, std::size_t sourceSize) override; std::shared_ptr InstantiateTexture(const TextureInfo& params) override; std::shared_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; diff --git a/include/Nazara/VulkanRenderer/VulkanShaderModule.hpp b/include/Nazara/VulkanRenderer/VulkanShaderModule.hpp new file mode 100644 index 000000000..8813eac54 --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanShaderModule.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan Renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKANRENDERER_VULKANSHADERSTAGE_HPP +#define NAZARA_VULKANRENDERER_VULKANSHADERSTAGE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_VULKANRENDERER_API VulkanShaderModule : public ShaderModule + { + public: + struct Stage; + + VulkanShaderModule() = default; + VulkanShaderModule(const VulkanShaderModule&) = delete; + VulkanShaderModule(VulkanShaderModule&&) = delete; + ~VulkanShaderModule() = default; + + bool Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states); + bool Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize); + + inline const Vk::ShaderModule& GetHandle() const; + inline const std::vector& GetStages() const; + + VulkanShaderModule& operator=(const VulkanShaderModule&) = delete; + VulkanShaderModule& operator=(VulkanShaderModule&&) = delete; + + struct Stage + { + ShaderStageType stage; + std::string name; + }; + + private: + Vk::ShaderModule m_shaderModule; + std::vector m_stages; + }; +} + +#include + +#endif // NAZARA_VULKANRENDERER_VULKANSHADERSTAGE_HPP diff --git a/include/Nazara/VulkanRenderer/VulkanShaderStage.inl b/include/Nazara/VulkanRenderer/VulkanShaderModule.inl similarity index 59% rename from include/Nazara/VulkanRenderer/VulkanShaderStage.inl rename to include/Nazara/VulkanRenderer/VulkanShaderModule.inl index 04aae8d74..8c3bded08 100644 --- a/include/Nazara/VulkanRenderer/VulkanShaderStage.inl +++ b/include/Nazara/VulkanRenderer/VulkanShaderModule.inl @@ -2,19 +2,19 @@ // This file is part of the "Nazara Engine - Vulkan Renderer" // For conditions of distribution and use, see copyright notice in Config.hpp -#include +#include #include namespace Nz { - inline const Vk::ShaderModule& VulkanShaderStage::GetHandle() const + inline const Vk::ShaderModule& VulkanShaderModule::GetHandle() const { return m_shaderModule; } - inline ShaderStageType VulkanShaderStage::GetStageType() const + inline auto VulkanShaderModule::GetStages() const -> const std::vector& { - return m_stage; + return m_stages; } } diff --git a/include/Nazara/VulkanRenderer/VulkanShaderStage.hpp b/include/Nazara/VulkanRenderer/VulkanShaderStage.hpp deleted file mode 100644 index b019fb90c..000000000 --- a/include/Nazara/VulkanRenderer/VulkanShaderStage.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2020 Jérôme Leclercq -// This file is part of the "Nazara Engine - Vulkan Renderer" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_VULKANRENDERER_VULKANSHADERSTAGE_HPP -#define NAZARA_VULKANRENDERER_VULKANSHADERSTAGE_HPP - -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - class NAZARA_VULKANRENDERER_API VulkanShaderStage : public ShaderModule - { - public: - VulkanShaderStage() = default; - VulkanShaderStage(const VulkanShaderStage&) = delete; - VulkanShaderStage(VulkanShaderStage&&) = delete; - ~VulkanShaderStage() = default; - - bool Create(Vk::Device& device, const ShaderAst& shader, const ShaderWriter::States& states); - bool Create(Vk::Device& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize); - - inline const Vk::ShaderModule& GetHandle() const; - inline ShaderStageType GetStageType() const; - - VulkanShaderStage& operator=(const VulkanShaderStage&) = delete; - VulkanShaderStage& operator=(VulkanShaderStage&&) = delete; - - private: - Vk::ShaderModule m_shaderModule; - ShaderStageType m_stage; - }; -} - -#include - -#endif // NAZARA_VULKANRENDERER_VULKANSHADERSTAGE_HPP diff --git a/src/Nazara/VulkanRenderer/VulkanDevice.cpp b/src/Nazara/VulkanRenderer/VulkanDevice.cpp index 1311398af..e3e2fdd2a 100644 --- a/src/Nazara/VulkanRenderer/VulkanDevice.cpp +++ b/src/Nazara/VulkanRenderer/VulkanDevice.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -46,25 +46,25 @@ namespace Nz { auto pipelineLayout = std::make_shared(); if (!pipelineLayout->Create(*this, std::move(pipelineLayoutInfo))) - return {}; + throw std::runtime_error("failed to instanciate vulkan render pipeline layout"); return pipelineLayout; } - std::shared_ptr VulkanDevice::InstantiateShaderModule(const ShaderAst& shaderAst, const ShaderWriter::States& states) + std::shared_ptr VulkanDevice::InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states) { - auto stage = std::make_shared(); - if (!stage->Create(*this, shaderAst, states)) - return {}; + auto stage = std::make_shared(); + if (!stage->Create(*this, stages, shaderAst, states)) + throw std::runtime_error("failed to instanciate vulkan shader module"); return stage; } - std::shared_ptr VulkanDevice::InstantiateShaderModule(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) + std::shared_ptr VulkanDevice::InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderLanguage lang, const void* source, std::size_t sourceSize) { - auto stage = std::make_shared(); - if (!stage->Create(*this, type, lang, source, sourceSize)) - return {}; + auto stage = std::make_shared(); + if (!stage->Create(*this, stages, lang, source, sourceSize)) + throw std::runtime_error("failed to instanciate vulkan shader module"); return stage; } diff --git a/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp b/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp index ec7295c19..0f8b7098e 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -166,13 +166,15 @@ namespace Nz for (auto&& stagePtr : pipelineInfo.shaderModules) { - Nz::VulkanShaderStage& vulkanStage = *static_cast(stagePtr.get()); - - VkPipelineShaderStageCreateInfo& createInfo = shaderStageCreateInfos.emplace_back(); - createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - createInfo.module = vulkanStage.GetHandle(); - createInfo.pName = "main"; - createInfo.stage = ToVulkan(vulkanStage.GetStageType()); + Nz::VulkanShaderModule& vulkanModule = *static_cast(stagePtr.get()); + for (auto& stage : vulkanModule.GetStages()) + { + VkPipelineShaderStageCreateInfo& createInfo = shaderStageCreateInfos.emplace_back(); + createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + createInfo.module = vulkanModule.GetHandle(); + createInfo.pName = stage.name.data(); + createInfo.stage = ToVulkan(stage.stage); + } } return shaderStageCreateInfos; diff --git a/src/Nazara/VulkanRenderer/VulkanShaderModule.cpp b/src/Nazara/VulkanRenderer/VulkanShaderModule.cpp new file mode 100644 index 000000000..dda59dee1 --- /dev/null +++ b/src/Nazara/VulkanRenderer/VulkanShaderModule.cpp @@ -0,0 +1,144 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan Renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace + { + struct SpirvEntryPointExtractor : SpirvDecoder + { + struct EntryPoint + { + SpirvExecutionModel executionModel; + std::string name; + }; + + std::vector entryPoints; + + bool HandleOpcode(const SpirvInstruction& instruction, UInt32 wordCount) override + { + switch (instruction.op) + { + // All instructions that can appear before OpEntryPoint + case SpirvOp::OpCapability: + case SpirvOp::OpExtension: + case SpirvOp::OpExtInstImport: + case SpirvOp::OpMemoryModel: + return true; + + case SpirvOp::OpEntryPoint: + { + SpirvExecutionModel executionModel = static_cast(ReadWord()); + std::string name = ReadString(); + + entryPoints.push_back({ + executionModel, + std::move(name) + }); + + return true; + } + + // Return false for other instructions (which means OpEntryPoint will no longer appear from here) + default: + return false; + } + } + }; + } + + bool VulkanShaderModule::Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states) + { + SpirvWriter::Environment env; + + SpirvWriter writer; + writer.SetEnv(env); + + std::vector code = writer.Generate(shaderAst, states); + return Create(device, shaderStages, ShaderLanguage::SpirV, code.data(), code.size() * sizeof(UInt32)); + } + + bool VulkanShaderModule::Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize) + { + switch (lang) + { + case ShaderLanguage::GLSL: + case ShaderLanguage::HLSL: + case ShaderLanguage::MSL: + break; + + case ShaderLanguage::NazaraBinary: + { + auto shader = ShaderAst::UnserializeShader(source, sourceSize); + return Create(device, shaderStages, shader, {}); + } + + case ShaderLanguage::NazaraShader: + { + std::vector tokens = Nz::ShaderLang::Tokenize(std::string_view(static_cast(source), sourceSize)); + + Nz::ShaderLang::Parser parser; + Nz::ShaderAst::StatementPtr shaderAst = parser.Parse(tokens); + return Create(device, shaderStages, shaderAst, {}); + } + + case ShaderLanguage::SpirV: + { + SpirvEntryPointExtractor extractor; + extractor.Decode(reinterpret_cast(source), sourceSize); + + ShaderStageTypeFlags remainingStages = shaderStages; + for (auto& entryPoint : extractor.entryPoints) + { + ShaderStageType stageType; + switch (entryPoint.executionModel) + { + case SpirvExecutionModel::Fragment: + stageType = ShaderStageType::Fragment; + break; + + case SpirvExecutionModel::Vertex: + stageType = ShaderStageType::Vertex; + break; + + default: + continue; //< Ignore + } + + m_stages.push_back({ + stageType, + std::move(entryPoint.name) + }); + + remainingStages.Clear(stageType); + } + + if (remainingStages != 0) + { + NazaraError("Vulkan shader module does not handle all requested stage types"); + return false; + } + + if (!m_shaderModule.Create(device, reinterpret_cast(source), sourceSize)) + { + NazaraError("failed to create shader module"); + return false; + } + + return true; + } + } + + NazaraError("this language is not supported"); + return false; + } +} diff --git a/src/Nazara/VulkanRenderer/VulkanShaderStage.cpp b/src/Nazara/VulkanRenderer/VulkanShaderStage.cpp deleted file mode 100644 index 403699798..000000000 --- a/src/Nazara/VulkanRenderer/VulkanShaderStage.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2020 Jérôme Leclercq -// This file is part of the "Nazara Engine - Vulkan Renderer" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include - -namespace Nz -{ - bool VulkanShaderStage::Create(Vk::Device& device, const ShaderAst& shader, const ShaderWriter::States& states) - { - m_stage = shader.GetStage(); - - SpirvWriter::Environment env; - - SpirvWriter writer; - writer.SetEnv(env); - - std::vector code = writer.Generate(shader, states); - return Create(device, m_stage, ShaderLanguage::SpirV, code.data(), code.size() * sizeof(UInt32)); - } - - bool VulkanShaderStage::Create(Vk::Device& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) - { - m_stage = type; - - switch (lang) - { - case ShaderLanguage::NazaraBinary: - { - auto shader = UnserializeShader(source, sourceSize); - if (shader.GetStage() != type) - throw std::runtime_error("incompatible shader stage"); - - if (!Create(device, shader, {})) - return false; - - break; - } - - case ShaderLanguage::SpirV: - { - if (!m_shaderModule.Create(device, reinterpret_cast(source), sourceSize)) - { - NazaraError("Failed to create shader module"); - return false; - } - - break; - } - - default: - { - NazaraError("this language is not supported"); - return false; - } - } - - return true; - } -}