#include #include #include #include #include #include #include #include #include #include #include using namespace XCEngine::Rendering::Pipelines; using namespace XCEngine::Rendering::Passes; using namespace XCEngine::Rendering; using namespace XCEngine::Containers; using namespace XCEngine::Resources; using namespace XCEngine::RHI; TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) { const InputLayoutDesc inputLayout = BuiltinForwardPipeline::BuildInputLayout(); ASSERT_EQ(inputLayout.elements.size(), 3u); 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, 0u); const InputElementDesc& normal = inputLayout.elements[1]; EXPECT_EQ(normal.semanticName, "NORMAL"); EXPECT_EQ(normal.semanticIndex, 0u); EXPECT_EQ(normal.format, static_cast(Format::R32G32B32_Float)); EXPECT_EQ(normal.inputSlot, 0u); EXPECT_EQ(normal.alignedByteOffset, static_cast(offsetof(StaticMeshVertex, normal))); const InputElementDesc& texcoord = inputLayout.elements[2]; EXPECT_EQ(texcoord.semanticName, "TEXCOORD"); EXPECT_EQ(texcoord.semanticIndex, 0u); EXPECT_EQ(texcoord.format, static_cast(Format::R32G32_Float)); EXPECT_EQ(texcoord.inputSlot, 0u); EXPECT_EQ(texcoord.alignedByteOffset, static_cast(offsetof(StaticMeshVertex, uv0))); } TEST(BuiltinForwardPipeline_Test, SplitsSceneItemsIntoOpaqueAndTransparentQueueRanges) { EXPECT_FALSE(IsTransparentRenderQueue(static_cast(MaterialRenderQueue::Background))); EXPECT_FALSE(IsTransparentRenderQueue(static_cast(MaterialRenderQueue::Geometry))); EXPECT_FALSE(IsTransparentRenderQueue(static_cast(MaterialRenderQueue::AlphaTest))); EXPECT_TRUE(IsTransparentRenderQueue(static_cast(MaterialRenderQueue::Transparent))); EXPECT_TRUE(IsTransparentRenderQueue(static_cast(MaterialRenderQueue::Overlay))); } TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderDeclaresExplicitForwardResourceContract) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("ForwardLit"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 8u); EXPECT_EQ(pass->resources[0].semantic, "PerObject"); EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[0].set, 0u); EXPECT_EQ(pass->resources[0].binding, 0u); EXPECT_EQ(pass->resources[1].semantic, "Lighting"); EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[1].set, 1u); EXPECT_EQ(pass->resources[1].binding, 0u); EXPECT_EQ(pass->resources[2].semantic, "Material"); EXPECT_EQ(pass->resources[2].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[2].set, 2u); EXPECT_EQ(pass->resources[2].binding, 0u); EXPECT_EQ(pass->resources[3].semantic, "ShadowReceiver"); EXPECT_EQ(pass->resources[3].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[3].set, 3u); EXPECT_EQ(pass->resources[3].binding, 0u); EXPECT_EQ(pass->resources[4].semantic, "BaseColorTexture"); EXPECT_EQ(pass->resources[4].type, ShaderResourceType::Texture2D); EXPECT_EQ(pass->resources[4].set, 4u); EXPECT_EQ(pass->resources[4].binding, 0u); EXPECT_EQ(pass->resources[5].semantic, "LinearClampSampler"); EXPECT_EQ(pass->resources[5].type, ShaderResourceType::Sampler); EXPECT_EQ(pass->resources[5].set, 5u); EXPECT_EQ(pass->resources[5].binding, 0u); EXPECT_EQ(pass->resources[6].semantic, "ShadowMapTexture"); EXPECT_EQ(pass->resources[6].type, ShaderResourceType::Texture2D); EXPECT_EQ(pass->resources[6].set, 6u); EXPECT_EQ(pass->resources[6].binding, 0u); EXPECT_EQ(pass->resources[7].semantic, "ShadowMapSampler"); EXPECT_EQ(pass->resources[7].type, ShaderResourceType::Sampler); EXPECT_EQ(pass->resources[7].set, 7u); EXPECT_EQ(pass->resources[7].binding, 0u); delete shader; } TEST(BuiltinForwardPipeline_Test, BuiltinUnlitShaderDeclaresExplicitSurfaceResourceContract) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinUnlitShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("Unlit"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 4u); EXPECT_EQ(pass->resources[0].semantic, "PerObject"); EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[0].set, 0u); EXPECT_EQ(pass->resources[0].binding, 0u); EXPECT_EQ(pass->resources[1].semantic, "Material"); EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[1].set, 1u); EXPECT_EQ(pass->resources[1].binding, 0u); EXPECT_EQ(pass->resources[2].semantic, "BaseColorTexture"); EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D); EXPECT_EQ(pass->resources[2].set, 2u); EXPECT_EQ(pass->resources[2].binding, 0u); EXPECT_EQ(pass->resources[3].semantic, "LinearClampSampler"); EXPECT_EQ(pass->resources[3].type, ShaderResourceType::Sampler); EXPECT_EQ(pass->resources[3].set, 3u); EXPECT_EQ(pass->resources[3].binding, 0u); delete shader; } TEST(BuiltinForwardPipeline_Test, BuiltinSkyboxShaderDeclaresExplicitEnvironmentResourceContract) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinSkyboxShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("Skybox"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 1u); EXPECT_EQ(pass->resources[0].semantic, "Environment"); EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[0].set, 0u); EXPECT_EQ(pass->resources[0].binding, 0u); delete shader; } TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplicitForwardResources) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("ForwardLit"); ASSERT_NE(pass, nullptr); BuiltinPassResourceBindingPlan plan = {}; String error; EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); EXPECT_TRUE(plan.perObject.IsValid()); EXPECT_TRUE(plan.lighting.IsValid()); EXPECT_TRUE(plan.material.IsValid()); EXPECT_TRUE(plan.shadowReceiver.IsValid()); EXPECT_TRUE(plan.baseColorTexture.IsValid()); EXPECT_TRUE(plan.linearClampSampler.IsValid()); EXPECT_TRUE(plan.shadowMapTexture.IsValid()); EXPECT_TRUE(plan.shadowMapSampler.IsValid()); EXPECT_EQ(plan.firstDescriptorSet, 0u); EXPECT_EQ(plan.descriptorSetCount, 8u); EXPECT_TRUE(plan.usesConstantBuffers); EXPECT_TRUE(plan.usesTextures); EXPECT_TRUE(plan.usesSamplers); delete shader; } TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplicitUnlitResources) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinUnlitShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("Unlit"); ASSERT_NE(pass, nullptr); BuiltinPassResourceBindingPlan plan = {}; String error; EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); EXPECT_TRUE(plan.perObject.IsValid()); EXPECT_TRUE(plan.material.IsValid()); EXPECT_TRUE(plan.baseColorTexture.IsValid()); EXPECT_TRUE(plan.linearClampSampler.IsValid()); EXPECT_EQ(plan.firstDescriptorSet, 0u); EXPECT_EQ(plan.descriptorSetCount, 4u); EXPECT_TRUE(plan.usesConstantBuffers); EXPECT_TRUE(plan.usesTextures); EXPECT_TRUE(plan.usesSamplers); delete shader; } TEST(BuiltinForwardPipeline_Test, UsesNormalizedExplicitSetIndicesForSurfaceResources) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("ForwardLit"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 8u); BuiltinPassResourceBindingPlan plan = {}; String error; EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); ASSERT_EQ(plan.bindings.Size(), 8u); EXPECT_EQ(plan.perObject.set, 0u); EXPECT_EQ(plan.lighting.set, 1u); EXPECT_EQ(plan.material.set, 2u); EXPECT_EQ(plan.shadowReceiver.set, 3u); EXPECT_EQ(plan.baseColorTexture.set, 4u); EXPECT_EQ(plan.linearClampSampler.set, 5u); EXPECT_EQ(plan.shadowMapTexture.set, 6u); EXPECT_EQ(plan.shadowMapSampler.set, 7u); delete shader; } TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitForwardResources) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("ForwardLit"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 8u); BuiltinPassResourceBindingPlan plan = {}; String error; ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); std::vector setLayouts; ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr(); ASSERT_EQ(setLayouts.size(), 8u); EXPECT_EQ(setLayouts[0].layout.bindingCount, 1u); EXPECT_FALSE(setLayouts[0].shaderVisible); EXPECT_TRUE(setLayouts[0].usesPerObject); EXPECT_FALSE(setLayouts[0].usesMaterial); EXPECT_EQ(setLayouts[1].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[1].usesLighting); EXPECT_FALSE(setLayouts[1].shaderVisible); EXPECT_EQ(setLayouts[2].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[2].usesMaterial); EXPECT_FALSE(setLayouts[2].shaderVisible); EXPECT_EQ(setLayouts[3].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[3].usesShadowReceiver); EXPECT_FALSE(setLayouts[3].shaderVisible); EXPECT_EQ(setLayouts[4].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[4].usesTexture); EXPECT_TRUE(setLayouts[4].shaderVisible); EXPECT_EQ(setLayouts[4].heapType, DescriptorHeapType::CBV_SRV_UAV); EXPECT_EQ(setLayouts[5].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[5].usesSampler); EXPECT_TRUE(setLayouts[5].shaderVisible); EXPECT_EQ(setLayouts[5].heapType, DescriptorHeapType::Sampler); EXPECT_EQ(setLayouts[6].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[6].usesTexture); EXPECT_TRUE(setLayouts[6].shaderVisible); EXPECT_EQ(setLayouts[7].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[7].usesSampler); EXPECT_TRUE(setLayouts[7].shaderVisible); EXPECT_EQ(setLayouts[7].heapType, DescriptorHeapType::Sampler); delete shader; } TEST(BuiltinDepthStylePass_Test, BuiltinDepthOnlyShaderDeclaresExplicitPerObjectResourceContract) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("DepthOnly"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 1u); EXPECT_EQ(pass->resources[0].semantic, "PerObject"); EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[0].set, 0u); EXPECT_EQ(pass->resources[0].binding, 0u); delete shader; } TEST(BuiltinDepthStylePass_Test, BuiltinShadowCasterShaderDeclaresExplicitPerObjectResourceContract) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinShadowCasterShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("ShadowCaster"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 1u); EXPECT_EQ(pass->resources[0].semantic, "PerObject"); EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[0].set, 0u); EXPECT_EQ(pass->resources[0].binding, 0u); delete shader; } TEST(BuiltinObjectIdPass_Test, BuiltinObjectIdShaderDeclaresExplicitPerObjectResourceContract) { 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); const ShaderPass* pass = shader->FindPass("ObjectId"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 1u); EXPECT_EQ(pass->resources[0].semantic, "PerObject"); EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer); EXPECT_EQ(pass->resources[0].set, 0u); EXPECT_EQ(pass->resources[0].binding, 0u); delete shader; } TEST(BuiltinObjectIdPass_Test, BuildsBuiltinPassResourceBindingPlanFromExplicitObjectIdResources) { 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); const ShaderPass* pass = shader->FindPass("ObjectId"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 1u); BuiltinPassResourceBindingPlan plan = {}; String error; EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); ASSERT_EQ(plan.bindings.Size(), 1u); EXPECT_TRUE(plan.perObject.IsValid()); EXPECT_FALSE(plan.material.IsValid()); EXPECT_FALSE(plan.baseColorTexture.IsValid()); EXPECT_FALSE(plan.linearClampSampler.IsValid()); EXPECT_EQ(plan.firstDescriptorSet, 0u); EXPECT_EQ(plan.descriptorSetCount, 1u); EXPECT_TRUE(plan.usesConstantBuffers); EXPECT_FALSE(plan.usesTextures); EXPECT_FALSE(plan.usesSamplers); delete shader; } TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) { const InputLayoutDesc inputLayout = BuiltinObjectIdPass::BuildInputLayout(); ASSERT_EQ(inputLayout.elements.size(), 3u); 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))); const InputElementDesc& normal = inputLayout.elements[1]; EXPECT_EQ(normal.semanticName, "NORMAL"); EXPECT_EQ(normal.semanticIndex, 0u); EXPECT_EQ(normal.format, static_cast(Format::R32G32B32_Float)); EXPECT_EQ(normal.inputSlot, 0u); EXPECT_EQ(normal.alignedByteOffset, static_cast(offsetof(StaticMeshVertex, normal))); const InputElementDesc& texcoord = inputLayout.elements[2]; EXPECT_EQ(texcoord.semanticName, "TEXCOORD"); EXPECT_EQ(texcoord.semanticIndex, 0u); EXPECT_EQ(texcoord.format, static_cast(Format::R32G32_Float)); EXPECT_EQ(texcoord.inputSlot, 0u); EXPECT_EQ(texcoord.alignedByteOffset, static_cast(offsetof(StaticMeshVertex, uv0))); } TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitObjectIdResources) { 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); const ShaderPass* pass = shader->FindPass("ObjectId"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 1u); BuiltinPassResourceBindingPlan plan = {}; String error; ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); std::vector setLayouts; ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr(); ASSERT_EQ(setLayouts.size(), 1u); EXPECT_EQ(setLayouts[0].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[0].usesPerObject); EXPECT_FALSE(setLayouts[0].usesMaterial); EXPECT_FALSE(setLayouts[0].usesTexture); EXPECT_FALSE(setLayouts[0].usesSampler); EXPECT_FALSE(setLayouts[0].shaderVisible); EXPECT_EQ(setLayouts[0].heapType, DescriptorHeapType::CBV_SRV_UAV); delete shader; } TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitDepthOnlyResources) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("DepthOnly"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 1u); BuiltinPassResourceBindingPlan plan = {}; String error; ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); std::vector setLayouts; ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr(); ASSERT_EQ(setLayouts.size(), 1u); EXPECT_EQ(setLayouts[0].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[0].usesPerObject); EXPECT_FALSE(setLayouts[0].usesMaterial); EXPECT_FALSE(setLayouts[0].usesTexture); EXPECT_FALSE(setLayouts[0].usesSampler); delete shader; } TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitShadowCasterResources) { ShaderLoader loader; LoadResult result = loader.Load(GetBuiltinShadowCasterShaderPath()); ASSERT_TRUE(result); ASSERT_NE(result.resource, nullptr); Shader* shader = static_cast(result.resource); ASSERT_NE(shader, nullptr); const ShaderPass* pass = shader->FindPass("ShadowCaster"); ASSERT_NE(pass, nullptr); ASSERT_EQ(pass->resources.Size(), 1u); BuiltinPassResourceBindingPlan plan = {}; String error; ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); std::vector setLayouts; ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr(); ASSERT_EQ(setLayouts.size(), 1u); EXPECT_EQ(setLayouts[0].layout.bindingCount, 1u); EXPECT_TRUE(setLayouts[0].usesPerObject); EXPECT_FALSE(setLayouts[0].usesMaterial); EXPECT_FALSE(setLayouts[0].usesTexture); EXPECT_FALSE(setLayouts[0].usesSampler); delete shader; } TEST(BuiltinPassLayout_Test, RejectsMixedSamplerAndNonSamplerBindingsInOneSet) { Array bindings; bindings.Resize(2); bindings[0].name = "BaseColorTexture"; bindings[0].type = ShaderResourceType::Texture2D; bindings[0].set = 0; bindings[0].binding = 0; bindings[0].semantic = "BaseColorTexture"; bindings[1].name = "LinearClampSampler"; bindings[1].type = ShaderResourceType::Sampler; bindings[1].set = 0; bindings[1].binding = 1; bindings[1].semantic = "LinearClampSampler"; BuiltinPassResourceBindingPlan plan = {}; String error; ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr(); std::vector setLayouts; EXPECT_FALSE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)); EXPECT_EQ(error, "Builtin pass does not support mixing sampler and non-sampler bindings in one set"); } TEST(BuiltinPassLayout_Test, RejectsDuplicateBindingsInOneSet) { BuiltinPassResourceBindingPlan plan = {}; BuiltinPassResourceBindingDesc perObjectBinding = {}; perObjectBinding.semantic = BuiltinPassResourceSemantic::PerObject; perObjectBinding.resourceType = ShaderResourceType::ConstantBuffer; perObjectBinding.location = { 0, 0 }; plan.bindings.PushBack(perObjectBinding); BuiltinPassResourceBindingDesc materialBinding = {}; materialBinding.semantic = BuiltinPassResourceSemantic::Material; materialBinding.resourceType = ShaderResourceType::ConstantBuffer; materialBinding.location = { 0, 0 }; plan.bindings.PushBack(materialBinding); plan.maxSetIndex = 0; plan.firstDescriptorSet = 0; plan.descriptorSetCount = 1; plan.usesConstantBuffers = true; plan.perObject = { 0, 0 }; plan.material = { 0, 0 }; std::vector setLayouts; String error; EXPECT_FALSE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)); EXPECT_EQ(error, "Builtin pass encountered duplicate bindings inside one descriptor set"); } TEST(BuiltinDepthOnlyPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) { const InputLayoutDesc inputLayout = BuiltinDepthOnlyPass::BuildInputLayout(); ASSERT_EQ(inputLayout.elements.size(), 3u); EXPECT_EQ(inputLayout.elements[0].semanticName, "POSITION"); EXPECT_EQ(inputLayout.elements[1].semanticName, "NORMAL"); EXPECT_EQ(inputLayout.elements[2].semanticName, "TEXCOORD"); EXPECT_EQ(inputLayout.elements[0].alignedByteOffset, static_cast(offsetof(StaticMeshVertex, position))); EXPECT_EQ(inputLayout.elements[1].alignedByteOffset, static_cast(offsetof(StaticMeshVertex, normal))); EXPECT_EQ(inputLayout.elements[2].alignedByteOffset, static_cast(offsetof(StaticMeshVertex, uv0))); } TEST(BuiltinShadowCasterPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) { const InputLayoutDesc inputLayout = BuiltinShadowCasterPass::BuildInputLayout(); ASSERT_EQ(inputLayout.elements.size(), 3u); EXPECT_EQ(inputLayout.elements[0].semanticName, "POSITION"); EXPECT_EQ(inputLayout.elements[1].semanticName, "NORMAL"); EXPECT_EQ(inputLayout.elements[2].semanticName, "TEXCOORD"); EXPECT_EQ(inputLayout.elements[0].alignedByteOffset, static_cast(offsetof(StaticMeshVertex, position))); EXPECT_EQ(inputLayout.elements[1].alignedByteOffset, static_cast(offsetof(StaticMeshVertex, normal))); EXPECT_EQ(inputLayout.elements[2].alignedByteOffset, static_cast(offsetof(StaticMeshVertex, uv0))); }