diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h index a7e3993e..a629fce0 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include @@ -14,6 +17,8 @@ class BuiltinObjectIdPass final : public ObjectIdPass { public: ~BuiltinObjectIdPass() override; + static RHI::InputLayoutDesc BuildInputLayout(); + bool Render( const RenderContext& context, const RenderSurface& surface, @@ -49,6 +54,7 @@ private: RHI::RHIType m_backendType = RHI::RHIType::D3D12; RHI::RHIPipelineLayout* m_pipelineLayout = nullptr; RHI::RHIPipelineState* m_pipelineState = nullptr; + Resources::ResourceHandle m_builtinObjectIdShader; RenderResourceCache m_resourceCache; std::unordered_map m_perObjectSets; }; diff --git a/engine/include/XCEngine/Resources/BuiltinResources.h b/engine/include/XCEngine/Resources/BuiltinResources.h index f2596136..59853b6b 100644 --- a/engine/include/XCEngine/Resources/BuiltinResources.h +++ b/engine/include/XCEngine/Resources/BuiltinResources.h @@ -24,6 +24,7 @@ const char* GetBuiltinPrimitiveDisplayName(BuiltinPrimitiveType primitiveType); Containers::String GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType primitiveType); Containers::String GetBuiltinDefaultPrimitiveMaterialPath(); Containers::String GetBuiltinForwardLitShaderPath(); +Containers::String GetBuiltinObjectIdShaderPath(); Containers::String GetBuiltinDefaultPrimitiveTexturePath(); bool TryParseBuiltinPrimitiveType(const Containers::String& path, BuiltinPrimitiveType& outPrimitiveType); diff --git a/engine/src/Rendering/Detail/ShaderVariantUtils.h b/engine/src/Rendering/Detail/ShaderVariantUtils.h new file mode 100644 index 00000000..ac52f80b --- /dev/null +++ b/engine/src/Rendering/Detail/ShaderVariantUtils.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace XCEngine { +namespace Rendering { +namespace Detail { + +inline Resources::ShaderBackend ToShaderBackend(RHI::RHIType backendType) { + switch (backendType) { + case RHI::RHIType::D3D12: + return Resources::ShaderBackend::D3D12; + case RHI::RHIType::Vulkan: + return Resources::ShaderBackend::Vulkan; + case RHI::RHIType::OpenGL: + default: + return Resources::ShaderBackend::OpenGL; + } +} + +inline RHI::ShaderLanguage ToRHIShaderLanguage(Resources::ShaderLanguage language) { + switch (language) { + case Resources::ShaderLanguage::HLSL: + return RHI::ShaderLanguage::HLSL; + case Resources::ShaderLanguage::SPIRV: + return RHI::ShaderLanguage::SPIRV; + case Resources::ShaderLanguage::GLSL: + default: + return RHI::ShaderLanguage::GLSL; + } +} + +inline std::wstring ToWideAscii(const Containers::String& value) { + std::wstring wide; + wide.reserve(value.Length()); + for (size_t index = 0; index < value.Length(); ++index) { + wide.push_back(static_cast(value[index])); + } + return wide; +} + +inline void ApplyShaderStageVariant( + const Resources::ShaderStageVariant& variant, + RHI::ShaderCompileDesc& compileDesc) { + compileDesc.source.assign( + variant.sourceCode.CStr(), + variant.sourceCode.CStr() + variant.sourceCode.Length()); + compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language); + compileDesc.entryPoint = ToWideAscii(variant.entryPoint); + compileDesc.profile = ToWideAscii(variant.profile); +} + +inline bool ShaderPassHasGraphicsVariants( + const Resources::Shader& shader, + const Containers::String& passName, + Resources::ShaderBackend backend) { + return shader.FindVariant(passName, Resources::ShaderType::Vertex, backend) != nullptr && + shader.FindVariant(passName, Resources::ShaderType::Fragment, backend) != nullptr; +} + +} // namespace Detail +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp b/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp index 81f34bd8..ad199585 100644 --- a/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp @@ -1,13 +1,17 @@ #include "Rendering/Passes/BuiltinObjectIdPass.h" #include "Components/GameObject.h" +#include "Core/Asset/ResourceManager.h" +#include "Debug/Logger.h" #include "RHI/RHICommandList.h" #include "RHI/RHIPipelineLayout.h" #include "RHI/RHIPipelineState.h" +#include "Rendering/Detail/ShaderVariantUtils.h" +#include "Rendering/RenderMaterialUtility.h" +#include "Resources/BuiltinResources.h" #include "Resources/Mesh/Mesh.h" #include -#include namespace XCEngine { namespace Rendering { @@ -15,83 +19,41 @@ namespace Passes { namespace { -const char kBuiltinObjectIdHlsl[] = R"( -cbuffer PerObjectConstants : register(b0) { - float4x4 gProjectionMatrix; - float4x4 gViewMatrix; - float4x4 gModelMatrix; - float4 gObjectIdColor; -}; +const Resources::ShaderPass* FindObjectIdCompatiblePass( + const Resources::Shader& shader, + Resources::ShaderBackend backend) { + for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) { + if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::ObjectId) && + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shaderPass.name, backend)) { + return &shaderPass; + } + } -struct VSInput { - float3 position : POSITION; -}; + const Resources::ShaderPass* objectIdPass = shader.FindPass("ObjectId"); + if (objectIdPass != nullptr && + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, objectIdPass->name, backend)) { + return objectIdPass; + } -struct PSInput { - float4 position : SV_POSITION; -}; + const Resources::ShaderPass* editorObjectIdPass = shader.FindPass("EditorObjectId"); + if (editorObjectIdPass != nullptr && + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, editorObjectIdPass->name, backend)) { + return editorObjectIdPass; + } -PSInput MainVS(VSInput input) { - PSInput output; - float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0)); - float4 positionVS = mul(gViewMatrix, positionWS); - output.position = mul(gProjectionMatrix, positionVS); - return output; -} + if (shader.GetPassCount() > 0 && + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) { + return &shader.GetPasses()[0]; + } -float4 MainPS(PSInput input) : SV_TARGET { - return gObjectIdColor; -} -)"; - -const char kBuiltinObjectIdVertexShader[] = R"(#version 430 -layout(location = 0) in vec3 aPosition; - -layout(std140, binding = 0) uniform PerObjectConstants { - mat4 gProjectionMatrix; - mat4 gViewMatrix; - mat4 gModelMatrix; - vec4 gObjectIdColor; -}; - -void main() { - vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); - vec4 positionVS = gViewMatrix * positionWS; - gl_Position = gProjectionMatrix * positionVS; -} -)"; - -const char kBuiltinObjectIdFragmentShader[] = R"(#version 430 -layout(std140, binding = 0) uniform PerObjectConstants { - mat4 gProjectionMatrix; - mat4 gViewMatrix; - mat4 gModelMatrix; - vec4 gObjectIdColor; -}; - -layout(location = 0) out vec4 fragColor; - -void main() { - fragColor = gObjectIdColor; -} -)"; - -RHI::InputLayoutDesc BuildInputLayout() { - RHI::InputLayoutDesc inputLayout = {}; - - RHI::InputElementDesc position = {}; - position.semanticName = "POSITION"; - position.semanticIndex = 0; - position.format = static_cast(RHI::Format::R32G32B32_Float); - position.inputSlot = 0; - position.alignedByteOffset = static_cast(offsetof(Resources::StaticMeshVertex, position)); - inputLayout.elements.push_back(position); - return inputLayout; + return nullptr; } RHI::GraphicsPipelineDesc CreatePipelineDesc( RHI::RHIType backendType, - RHI::RHIPipelineLayout* pipelineLayout) { + RHI::RHIPipelineLayout* pipelineLayout, + const Resources::Shader& shader, + const Containers::String& passName) { RHI::GraphicsPipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = pipelineLayout; pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); @@ -99,7 +61,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( pipelineDesc.renderTargetFormats[0] = static_cast(RHI::Format::R8G8B8A8_UNorm); pipelineDesc.depthStencilFormat = static_cast(RHI::Format::D24_UNorm_S8_UInt); pipelineDesc.sampleCount = 1; - pipelineDesc.inputLayout = BuildInputLayout(); + pipelineDesc.inputLayout = BuiltinObjectIdPass::BuildInputLayout(); pipelineDesc.rasterizerState.fillMode = static_cast(RHI::FillMode::Solid); pipelineDesc.rasterizerState.cullMode = static_cast(RHI::CullMode::None); @@ -113,32 +75,14 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( pipelineDesc.depthStencilState.depthWriteEnable = false; pipelineDesc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::LessEqual); - if (backendType == RHI::RHIType::D3D12) { - pipelineDesc.vertexShader.source.assign( - kBuiltinObjectIdHlsl, - kBuiltinObjectIdHlsl + std::strlen(kBuiltinObjectIdHlsl)); - pipelineDesc.vertexShader.sourceLanguage = RHI::ShaderLanguage::HLSL; - pipelineDesc.vertexShader.entryPoint = L"MainVS"; - pipelineDesc.vertexShader.profile = L"vs_5_0"; - - pipelineDesc.fragmentShader.source.assign( - kBuiltinObjectIdHlsl, - kBuiltinObjectIdHlsl + std::strlen(kBuiltinObjectIdHlsl)); - pipelineDesc.fragmentShader.sourceLanguage = RHI::ShaderLanguage::HLSL; - pipelineDesc.fragmentShader.entryPoint = L"MainPS"; - pipelineDesc.fragmentShader.profile = L"ps_5_0"; - } else { - pipelineDesc.vertexShader.source.assign( - kBuiltinObjectIdVertexShader, - kBuiltinObjectIdVertexShader + std::strlen(kBuiltinObjectIdVertexShader)); - pipelineDesc.vertexShader.sourceLanguage = RHI::ShaderLanguage::GLSL; - pipelineDesc.vertexShader.profile = L"vs_4_30"; - - pipelineDesc.fragmentShader.source.assign( - kBuiltinObjectIdFragmentShader, - kBuiltinObjectIdFragmentShader + std::strlen(kBuiltinObjectIdFragmentShader)); - pipelineDesc.fragmentShader.sourceLanguage = RHI::ShaderLanguage::GLSL; - pipelineDesc.fragmentShader.profile = L"fs_4_30"; + const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType); + if (const Resources::ShaderStageVariant* vertexVariant = + shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { + ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader); + } + if (const Resources::ShaderStageVariant* fragmentVariant = + shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { + ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader); } return pipelineDesc; @@ -150,6 +94,19 @@ BuiltinObjectIdPass::~BuiltinObjectIdPass() { Shutdown(); } +RHI::InputLayoutDesc BuiltinObjectIdPass::BuildInputLayout() { + RHI::InputLayoutDesc inputLayout = {}; + + RHI::InputElementDesc position = {}; + position.semanticName = "POSITION"; + position.semanticIndex = 0; + position.format = static_cast(RHI::Format::R32G32B32_Float); + position.inputSlot = 0; + position.alignedByteOffset = static_cast(offsetof(Resources::StaticMeshVertex, position)); + inputLayout.elements.push_back(position); + return inputLayout; +} + bool BuiltinObjectIdPass::Render( const RenderContext& context, const RenderSurface& surface, @@ -244,6 +201,26 @@ bool BuiltinObjectIdPass::EnsureInitialized(const RenderContext& context) { bool BuiltinObjectIdPass::CreateResources(const RenderContext& context) { m_device = context.device; m_backendType = context.backendType; + m_builtinObjectIdShader = Resources::ResourceManager::Get().Load( + Resources::GetBuiltinObjectIdShaderPath()); + if (!m_builtinObjectIdShader.IsValid()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinObjectIdPass failed to load builtin object-id shader resource"); + DestroyResources(); + return false; + } + + const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType); + const Resources::ShaderPass* objectIdPass = + FindObjectIdCompatiblePass(*m_builtinObjectIdShader.Get(), backend); + if (objectIdPass == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinObjectIdPass could not resolve a valid ObjectId shader pass"); + DestroyResources(); + return false; + } RHI::DescriptorSetLayoutBinding constantBinding = {}; constantBinding.binding = 0; @@ -263,7 +240,12 @@ bool BuiltinObjectIdPass::CreateResources(const RenderContext& context) { return false; } - m_pipelineState = m_device->CreatePipelineState(CreatePipelineDesc(m_backendType, m_pipelineLayout)); + m_pipelineState = m_device->CreatePipelineState( + CreatePipelineDesc( + m_backendType, + m_pipelineLayout, + *m_builtinObjectIdShader.Get(), + objectIdPass->name)); if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { if (m_pipelineState != nullptr) { m_pipelineState->Shutdown(); @@ -299,6 +281,7 @@ void BuiltinObjectIdPass::DestroyResources() { m_device = nullptr; m_backendType = RHI::RHIType::D3D12; + m_builtinObjectIdShader.Reset(); } RHI::RHIDescriptorSet* BuiltinObjectIdPass::GetOrCreatePerObjectSet(uint64_t objectId) { diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp index 9f78635e..75804b6d 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp @@ -6,6 +6,7 @@ #include "Debug/Logger.h" #include "Core/Asset/ResourceManager.h" #include "RHI/RHICommandList.h" +#include "Rendering/Detail/ShaderVariantUtils.h" #include "Rendering/RenderMaterialUtility.h" #include "Rendering/RenderSurface.h" #include "Resources/BuiltinResources.h" @@ -53,47 +54,6 @@ namespace { constexpr uint32_t kDescriptorFirstSet = 1; constexpr uint32_t kDescriptorSetCount = 5; -Resources::ShaderBackend ToShaderBackend(RHI::RHIType backendType) { - switch (backendType) { - case RHI::RHIType::D3D12: - return Resources::ShaderBackend::D3D12; - case RHI::RHIType::Vulkan: - return Resources::ShaderBackend::Vulkan; - case RHI::RHIType::OpenGL: - default: - return Resources::ShaderBackend::OpenGL; - } -} - -RHI::ShaderLanguage ToRHIShaderLanguage(Resources::ShaderLanguage language) { - switch (language) { - case Resources::ShaderLanguage::HLSL: - return RHI::ShaderLanguage::HLSL; - case Resources::ShaderLanguage::SPIRV: - return RHI::ShaderLanguage::SPIRV; - case Resources::ShaderLanguage::GLSL: - default: - return RHI::ShaderLanguage::GLSL; - } -} - -std::wstring ToWideAscii(const Containers::String& value) { - std::wstring wide; - wide.reserve(value.Length()); - for (size_t index = 0; index < value.Length(); ++index) { - wide.push_back(static_cast(value[index])); - } - return wide; -} - -bool ShaderPassHasGraphicsVariants( - const Resources::Shader& shader, - const Containers::String& passName, - Resources::ShaderBackend backend) { - return shader.FindVariant(passName, Resources::ShaderType::Vertex, backend) != nullptr && - shader.FindVariant(passName, Resources::ShaderType::Fragment, backend) != nullptr; -} - const Resources::ShaderPass* FindForwardCompatiblePass( const Resources::Shader& shader, const Resources::Material* material, @@ -101,49 +61,38 @@ const Resources::ShaderPass* FindForwardCompatiblePass( if (material != nullptr && !material->GetShaderPass().Empty()) { const Resources::ShaderPass* explicitPass = shader.FindPass(material->GetShaderPass()); if (explicitPass != nullptr && - ShaderPassHasGraphicsVariants(shader, explicitPass->name, backend)) { + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, explicitPass->name, backend)) { return explicitPass; } } for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) { if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::ForwardLit) && - ShaderPassHasGraphicsVariants(shader, shaderPass.name, backend)) { + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shaderPass.name, backend)) { return &shaderPass; } } const Resources::ShaderPass* defaultPass = shader.FindPass("ForwardLit"); if (defaultPass != nullptr && - ShaderPassHasGraphicsVariants(shader, defaultPass->name, backend)) { + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, defaultPass->name, backend)) { return defaultPass; } defaultPass = shader.FindPass("Default"); if (defaultPass != nullptr && - ShaderPassHasGraphicsVariants(shader, defaultPass->name, backend)) { + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, defaultPass->name, backend)) { return defaultPass; } if (shader.GetPassCount() > 0 && - ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) { + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) { return &shader.GetPasses()[0]; } return nullptr; } -void ApplyShaderStageVariant( - const Resources::ShaderStageVariant& variant, - RHI::ShaderCompileDesc& compileDesc) { - compileDesc.source.assign( - variant.sourceCode.CStr(), - variant.sourceCode.CStr() + variant.sourceCode.Length()); - compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language); - compileDesc.entryPoint = ToWideAscii(variant.entryPoint); - compileDesc.profile = ToWideAscii(variant.profile); -} - RHI::GraphicsPipelineDesc CreatePipelineDesc( RHI::RHIType backendType, RHI::RHIPipelineLayout* pipelineLayout, @@ -161,16 +110,16 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( pipelineDesc.inputLayout = BuiltinForwardPipeline::BuildInputLayout(); - const Resources::ShaderBackend backend = ToShaderBackend(backendType); + const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType); const Resources::ShaderStageVariant* vertexVariant = shader.FindVariant(passName, Resources::ShaderType::Vertex, backend); const Resources::ShaderStageVariant* fragmentVariant = shader.FindVariant(passName, Resources::ShaderType::Fragment, backend); if (vertexVariant != nullptr) { - ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader); + ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader); } if (fragmentVariant != nullptr) { - ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader); + ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader); } return pipelineDesc; @@ -561,7 +510,7 @@ void BuiltinForwardPipeline::DestroyPipelineResources() { BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveForwardShaderPass( const Resources::Material* material) const { ResolvedShaderPass resolved = {}; - const Resources::ShaderBackend backend = ToShaderBackend(m_backendType); + const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType); if (material != nullptr && material->GetShader() != nullptr) { const Resources::Shader* materialShader = material->GetShader(); diff --git a/engine/src/Resources/BuiltinResources.cpp b/engine/src/Resources/BuiltinResources.cpp index 8f66d53c..89851334 100644 --- a/engine/src/Resources/BuiltinResources.cpp +++ b/engine/src/Resources/BuiltinResources.cpp @@ -25,6 +25,7 @@ constexpr const char* kBuiltinShaderPrefix = "builtin://shaders/"; constexpr const char* kBuiltinTexturePrefix = "builtin://textures/"; constexpr const char* kBuiltinDefaultPrimitiveMaterialPath = "builtin://materials/default-primitive"; constexpr const char* kBuiltinForwardLitShaderPath = "builtin://shaders/forward-lit"; +constexpr const char* kBuiltinObjectIdShaderPath = "builtin://shaders/object-id"; constexpr const char* kBuiltinDefaultPrimitiveTexturePath = "builtin://textures/default-primitive-albedo"; constexpr float kPi = 3.14159265358979323846f; @@ -214,6 +215,99 @@ void main() { } )"; +const char kBuiltinObjectIdHlsl[] = R"( +cbuffer PerObjectConstants : register(b0) { + float4x4 gProjectionMatrix; + float4x4 gViewMatrix; + float4x4 gModelMatrix; + float4 gObjectIdColor; +}; + +struct VSInput { + float3 position : POSITION; +}; + +struct PSInput { + float4 position : SV_POSITION; +}; + +PSInput MainVS(VSInput input) { + PSInput output; + float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0)); + float4 positionVS = mul(gViewMatrix, positionWS); + output.position = mul(gProjectionMatrix, positionVS); + return output; +} + +float4 MainPS(PSInput input) : SV_TARGET { + return gObjectIdColor; +} +)"; + +const char kBuiltinObjectIdVertexShader[] = R"(#version 430 +layout(location = 0) in vec3 aPosition; + +layout(std140, binding = 0) uniform PerObjectConstants { + mat4 gProjectionMatrix; + mat4 gViewMatrix; + mat4 gModelMatrix; + vec4 gObjectIdColor; +}; + +void main() { + vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); + vec4 positionVS = gViewMatrix * positionWS; + gl_Position = gProjectionMatrix * positionVS; +} +)"; + +const char kBuiltinObjectIdFragmentShader[] = R"(#version 430 +layout(std140, binding = 0) uniform PerObjectConstants { + mat4 gProjectionMatrix; + mat4 gViewMatrix; + mat4 gModelMatrix; + vec4 gObjectIdColor; +}; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = gObjectIdColor; +} +)"; + +const char kBuiltinObjectIdVulkanVertexShader[] = R"(#version 450 +layout(location = 0) in vec3 aPosition; + +layout(set = 0, binding = 0, std140) uniform PerObjectConstants { + mat4 gProjectionMatrix; + mat4 gViewMatrix; + mat4 gModelMatrix; + vec4 gObjectIdColor; +}; + +void main() { + vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); + vec4 positionVS = gViewMatrix * positionWS; + gl_Position = gProjectionMatrix * positionVS; +} +)"; + +const char kBuiltinObjectIdVulkanFragmentShader[] = R"(#version 450 +layout(set = 0, binding = 0, std140) uniform PerObjectConstants { + mat4 gProjectionMatrix; + mat4 gViewMatrix; + mat4 gModelMatrix; + vec4 gObjectIdColor; +}; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = gObjectIdColor; +} +)"; + Math::Bounds ComputeBounds(const std::vector& vertices) { if (vertices.empty()) { return Math::Bounds(); @@ -776,6 +870,79 @@ Shader* BuildBuiltinForwardLitShader(const Containers::String& path) { return shader; } +Shader* BuildBuiltinObjectIdShader(const Containers::String& path) { + auto* shader = new Shader(); + IResource::ConstructParams params; + params.name = Containers::String("Builtin Object Id"); + params.path = path; + params.guid = ResourceGUID::Generate(path); + params.memorySize = 0; + shader->Initialize(params); + + const Containers::String passName("ObjectId"); + shader->SetPassTag(passName, "LightMode", "ObjectId"); + + AddBuiltinShaderStageVariant( + *shader, + passName, + ShaderType::Vertex, + ShaderLanguage::HLSL, + ShaderBackend::D3D12, + kBuiltinObjectIdHlsl, + "MainVS", + "vs_5_0"); + AddBuiltinShaderStageVariant( + *shader, + passName, + ShaderType::Fragment, + ShaderLanguage::HLSL, + ShaderBackend::D3D12, + kBuiltinObjectIdHlsl, + "MainPS", + "ps_5_0"); + + AddBuiltinShaderStageVariant( + *shader, + passName, + ShaderType::Vertex, + ShaderLanguage::GLSL, + ShaderBackend::OpenGL, + kBuiltinObjectIdVertexShader, + "main", + "vs_4_30"); + AddBuiltinShaderStageVariant( + *shader, + passName, + ShaderType::Fragment, + ShaderLanguage::GLSL, + ShaderBackend::OpenGL, + kBuiltinObjectIdFragmentShader, + "main", + "fs_4_30"); + + AddBuiltinShaderStageVariant( + *shader, + passName, + ShaderType::Vertex, + ShaderLanguage::GLSL, + ShaderBackend::Vulkan, + kBuiltinObjectIdVulkanVertexShader, + "main", + "vs_4_50"); + AddBuiltinShaderStageVariant( + *shader, + passName, + ShaderType::Fragment, + ShaderLanguage::GLSL, + ShaderBackend::Vulkan, + kBuiltinObjectIdVulkanFragmentShader, + "main", + "fs_4_50"); + + shader->m_memorySize = CalculateBuiltinShaderMemorySize(*shader); + return shader; +} + Material* BuildDefaultPrimitiveMaterial(const Containers::String& path) { auto* material = new Material(); IResource::ConstructParams params; @@ -878,6 +1045,10 @@ Containers::String GetBuiltinForwardLitShaderPath() { return Containers::String(kBuiltinForwardLitShaderPath); } +Containers::String GetBuiltinObjectIdShaderPath() { + return Containers::String(kBuiltinObjectIdShaderPath); +} + Containers::String GetBuiltinDefaultPrimitiveTexturePath() { return Containers::String(kBuiltinDefaultPrimitiveTexturePath); } @@ -965,11 +1136,15 @@ LoadResult CreateBuiltinMaterialResource(const Containers::String& path) { } LoadResult CreateBuiltinShaderResource(const Containers::String& path) { - if (path != GetBuiltinForwardLitShaderPath()) { + Shader* shader = nullptr; + if (path == GetBuiltinForwardLitShaderPath()) { + shader = BuildBuiltinForwardLitShader(path); + } else if (path == GetBuiltinObjectIdShaderPath()) { + shader = BuildBuiltinObjectIdShader(path); + } else { return LoadResult(Containers::String("Unknown builtin shader: ") + path); } - Shader* shader = BuildBuiltinForwardLitShader(path); if (shader == nullptr) { return LoadResult(Containers::String("Failed to create builtin shader: ") + path); } diff --git a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp index 36f00860..376bff62 100644 --- a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp +++ b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp @@ -1,10 +1,12 @@ #include +#include #include #include #include using namespace XCEngine::Rendering::Pipelines; +using namespace XCEngine::Rendering::Passes; using namespace XCEngine::Resources; using namespace XCEngine::RHI; @@ -34,3 +36,16 @@ TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVert EXPECT_EQ(texcoord.inputSlot, 0u); EXPECT_EQ(texcoord.alignedByteOffset, static_cast(offsetof(StaticMeshVertex, uv0))); } + +TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) { + const InputLayoutDesc inputLayout = BuiltinObjectIdPass::BuildInputLayout(); + + ASSERT_EQ(inputLayout.elements.size(), 1u); + + const InputElementDesc& position = inputLayout.elements[0]; + EXPECT_EQ(position.semanticName, "POSITION"); + EXPECT_EQ(position.semanticIndex, 0u); + EXPECT_EQ(position.format, static_cast(Format::R32G32B32_Float)); + EXPECT_EQ(position.inputSlot, 0u); + EXPECT_EQ(position.alignedByteOffset, static_cast(offsetof(StaticMeshVertex, position))); +} diff --git a/tests/Resources/Shader/test_shader_loader.cpp b/tests/Resources/Shader/test_shader_loader.cpp index b9fa8b24..1fd92f77 100644 --- a/tests/Resources/Shader/test_shader_loader.cpp +++ b/tests/Resources/Shader/test_shader_loader.cpp @@ -32,6 +32,7 @@ TEST(ShaderLoader, CanLoad) { EXPECT_TRUE(loader.CanLoad("test.glsl")); EXPECT_TRUE(loader.CanLoad("test.hlsl")); EXPECT_TRUE(loader.CanLoad(GetBuiltinForwardLitShaderPath())); + EXPECT_TRUE(loader.CanLoad(GetBuiltinObjectIdShaderPath())); EXPECT_FALSE(loader.CanLoad("test.txt")); EXPECT_FALSE(loader.CanLoad("test.png")); } @@ -100,6 +101,33 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) { delete shader; } +TEST(ShaderLoader, LoadBuiltinObjectIdShaderBuildsBackendVariants) { + ShaderLoader loader; + LoadResult result = loader.Load(GetBuiltinObjectIdShaderPath()); + ASSERT_TRUE(result); + ASSERT_NE(result.resource, nullptr); + + Shader* shader = static_cast(result.resource); + ASSERT_NE(shader, nullptr); + ASSERT_TRUE(shader->IsValid()); + + const ShaderPass* pass = shader->FindPass("ObjectId"); + ASSERT_NE(pass, nullptr); + ASSERT_EQ(pass->variants.Size(), 6u); + ASSERT_EQ(pass->tags.Size(), 1u); + EXPECT_EQ(pass->tags[0].name, "LightMode"); + EXPECT_EQ(pass->tags[0].value, "ObjectId"); + + EXPECT_NE(shader->FindVariant("ObjectId", ShaderType::Vertex, ShaderBackend::D3D12), nullptr); + EXPECT_NE(shader->FindVariant("ObjectId", ShaderType::Fragment, ShaderBackend::D3D12), nullptr); + EXPECT_NE(shader->FindVariant("ObjectId", ShaderType::Vertex, ShaderBackend::OpenGL), nullptr); + EXPECT_NE(shader->FindVariant("ObjectId", ShaderType::Fragment, ShaderBackend::OpenGL), nullptr); + EXPECT_NE(shader->FindVariant("ObjectId", ShaderType::Vertex, ShaderBackend::Vulkan), nullptr); + EXPECT_NE(shader->FindVariant("ObjectId", ShaderType::Fragment, ShaderBackend::Vulkan), nullptr); + + delete shader; +} + TEST(ShaderLoader, ResourceManagerLazilyLoadsBuiltinForwardLitShader) { ResourceManager& manager = ResourceManager::Get(); manager.Shutdown(); @@ -109,6 +137,10 @@ TEST(ShaderLoader, ResourceManagerLazilyLoadsBuiltinForwardLitShader) { ASSERT_TRUE(shaderHandle.IsValid()); ASSERT_NE(shaderHandle->FindPass("ForwardLit"), nullptr); + ResourceHandle objectIdShaderHandle = manager.Load(GetBuiltinObjectIdShaderPath()); + ASSERT_TRUE(objectIdShaderHandle.IsValid()); + ASSERT_NE(objectIdShaderHandle->FindPass("ObjectId"), nullptr); + manager.Shutdown(); }