Add builtin GaussianSplat forward pass baseline
This commit is contained in:
@@ -508,6 +508,7 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinGaussianSplatPass.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraRenderer.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraRenderer.cpp
|
||||||
@@ -525,6 +526,7 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinSelectionOutlinePass.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinSelectionOutlinePass.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinGaussianSplatPass.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinVolumetricPass.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinVolumetricPass.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/Internal/BuiltinGaussianSplatPassResources.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/Internal/BuiltinGaussianSplatPassResources.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSurface.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSurface.cpp
|
||||||
|
|||||||
113
engine/assets/builtin/shaders/gaussian-splat.shader
Normal file
113
engine/assets/builtin/shaders/gaussian-splat.shader
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
Shader "Builtin Gaussian Splat"
|
||||||
|
{
|
||||||
|
Properties
|
||||||
|
{
|
||||||
|
_PointScale ("Point Scale", Float) = 1.0
|
||||||
|
_OpacityScale ("Opacity Scale", Float) = 1.0
|
||||||
|
}
|
||||||
|
HLSLINCLUDE
|
||||||
|
cbuffer PerObjectConstants
|
||||||
|
{
|
||||||
|
float4x4 gProjectionMatrix;
|
||||||
|
float4x4 gViewMatrix;
|
||||||
|
float4x4 gModelMatrix;
|
||||||
|
float4 gCameraRight;
|
||||||
|
float4 gCameraUp;
|
||||||
|
};
|
||||||
|
|
||||||
|
cbuffer MaterialConstants
|
||||||
|
{
|
||||||
|
float4 gSplatParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GaussianSplatOtherData
|
||||||
|
{
|
||||||
|
float4 rotation;
|
||||||
|
float4 scaleReserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
StructuredBuffer<float3> GaussianSplatPositions;
|
||||||
|
StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther;
|
||||||
|
StructuredBuffer<float4> GaussianSplatColor;
|
||||||
|
|
||||||
|
struct VSOutput
|
||||||
|
{
|
||||||
|
float4 position : SV_POSITION;
|
||||||
|
float2 localUv : TEXCOORD0;
|
||||||
|
float4 colorOpacity : TEXCOORD1;
|
||||||
|
};
|
||||||
|
|
||||||
|
float2 ResolveQuadCorner(uint vertexId)
|
||||||
|
{
|
||||||
|
switch (vertexId)
|
||||||
|
{
|
||||||
|
case 0u: return float2(-1.0, -1.0);
|
||||||
|
case 1u: return float2( 1.0, -1.0);
|
||||||
|
case 2u: return float2( 1.0, 1.0);
|
||||||
|
case 3u: return float2(-1.0, -1.0);
|
||||||
|
case 4u: return float2( 1.0, 1.0);
|
||||||
|
default: return float2(-1.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VSOutput MainVS(uint vertexId : SV_VertexID, uint instanceId : SV_InstanceID)
|
||||||
|
{
|
||||||
|
VSOutput output;
|
||||||
|
const float2 corner = ResolveQuadCorner(vertexId);
|
||||||
|
const float3 localCenter = GaussianSplatPositions[instanceId];
|
||||||
|
const GaussianSplatOtherData otherData = GaussianSplatOther[instanceId];
|
||||||
|
const float4 colorOpacity = GaussianSplatColor[instanceId];
|
||||||
|
|
||||||
|
const float3 worldCenter = mul(gModelMatrix, float4(localCenter, 1.0)).xyz;
|
||||||
|
const float maxAxisScale =
|
||||||
|
max(max(otherData.scaleReserved.x, otherData.scaleReserved.y), otherData.scaleReserved.z);
|
||||||
|
const float radius = max(maxAxisScale * gSplatParams.x, 0.0001);
|
||||||
|
const float3 worldPosition =
|
||||||
|
worldCenter +
|
||||||
|
gCameraRight.xyz * (corner.x * radius) +
|
||||||
|
gCameraUp.xyz * (corner.y * radius);
|
||||||
|
|
||||||
|
output.position = mul(gProjectionMatrix, mul(gViewMatrix, float4(worldPosition, 1.0)));
|
||||||
|
output.localUv = corner;
|
||||||
|
output.colorOpacity = colorOpacity;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 MainPS(VSOutput input) : SV_TARGET
|
||||||
|
{
|
||||||
|
const float radiusSq = dot(input.localUv, input.localUv);
|
||||||
|
if (radiusSq > 1.0)
|
||||||
|
{
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float gaussianFalloff = exp(-radiusSq * 2.5);
|
||||||
|
const float alpha = saturate(input.colorOpacity.a * gSplatParams.y * gaussianFalloff);
|
||||||
|
if (alpha <= 0.001)
|
||||||
|
{
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return float4(input.colorOpacity.rgb, alpha);
|
||||||
|
}
|
||||||
|
ENDHLSL
|
||||||
|
|
||||||
|
SubShader
|
||||||
|
{
|
||||||
|
Tags { "Queue" = "Transparent" }
|
||||||
|
Pass
|
||||||
|
{
|
||||||
|
Name "GaussianSplat"
|
||||||
|
Tags { "LightMode" = "GaussianSplat" }
|
||||||
|
Cull Off
|
||||||
|
ZWrite Off
|
||||||
|
ZTest LEqual
|
||||||
|
Blend SrcAlpha OneMinusSrcAlpha
|
||||||
|
HLSLPROGRAM
|
||||||
|
#pragma target 4.5
|
||||||
|
#pragma vertex MainVS
|
||||||
|
#pragma fragment MainPS
|
||||||
|
ENDHLSL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,18 @@ inline bool TryBuildBuiltinPassResourceBindingPlan(
|
|||||||
case BuiltinPassResourceSemantic::VolumeField:
|
case BuiltinPassResourceSemantic::VolumeField:
|
||||||
location = &outPlan.volumeField;
|
location = &outPlan.volumeField;
|
||||||
break;
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
||||||
|
location = &outPlan.gaussianSplatPositionBuffer;
|
||||||
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
|
||||||
|
location = &outPlan.gaussianSplatOtherBuffer;
|
||||||
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
|
||||||
|
location = &outPlan.gaussianSplatColorBuffer;
|
||||||
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
|
location = &outPlan.gaussianSplatSHBuffer;
|
||||||
|
break;
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
location = &outPlan.baseColorTexture;
|
location = &outPlan.baseColorTexture;
|
||||||
break;
|
break;
|
||||||
@@ -334,6 +346,24 @@ inline bool TryBuildBuiltinPassDefaultResourceBindings(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::GaussianSplat)) {
|
||||||
|
AppendBuiltinPassResourceBinding(
|
||||||
|
outBindings,
|
||||||
|
"PerObjectConstants",
|
||||||
|
Resources::ShaderResourceType::ConstantBuffer,
|
||||||
|
0u,
|
||||||
|
0u,
|
||||||
|
"PerObject");
|
||||||
|
AppendBuiltinPassResourceBinding(
|
||||||
|
outBindings,
|
||||||
|
"MaterialConstants",
|
||||||
|
Resources::ShaderResourceType::ConstantBuffer,
|
||||||
|
1u,
|
||||||
|
0u,
|
||||||
|
"Material");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::PostProcess)) {
|
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::PostProcess)) {
|
||||||
AppendBuiltinPassResourceBinding(
|
AppendBuiltinPassResourceBinding(
|
||||||
outBindings,
|
outBindings,
|
||||||
@@ -570,6 +600,18 @@ inline bool TryBuildBuiltinPassSetLayouts(
|
|||||||
case BuiltinPassResourceSemantic::VolumeField:
|
case BuiltinPassResourceSemantic::VolumeField:
|
||||||
setLayout.usesVolumeField = true;
|
setLayout.usesVolumeField = true;
|
||||||
break;
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
||||||
|
setLayout.usesGaussianSplatPositionBuffer = true;
|
||||||
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
|
||||||
|
setLayout.usesGaussianSplatOtherBuffer = true;
|
||||||
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
|
||||||
|
setLayout.usesGaussianSplatColorBuffer = true;
|
||||||
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
|
setLayout.usesGaussianSplatSHBuffer = true;
|
||||||
|
break;
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
setLayout.usesTexture = true;
|
setLayout.usesTexture = true;
|
||||||
setLayout.usesBaseColorTexture = true;
|
setLayout.usesBaseColorTexture = true;
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ inline const char* GetBuiltinPassCanonicalName(BuiltinMaterialPass pass) {
|
|||||||
return "selectionmask";
|
return "selectionmask";
|
||||||
case BuiltinMaterialPass::Skybox:
|
case BuiltinMaterialPass::Skybox:
|
||||||
return "skybox";
|
return "skybox";
|
||||||
|
case BuiltinMaterialPass::GaussianSplat:
|
||||||
|
return "gaussiansplat";
|
||||||
case BuiltinMaterialPass::Volumetric:
|
case BuiltinMaterialPass::Volumetric:
|
||||||
return "volumetric";
|
return "volumetric";
|
||||||
case BuiltinMaterialPass::PostProcess:
|
case BuiltinMaterialPass::PostProcess:
|
||||||
@@ -145,6 +147,30 @@ inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic(
|
|||||||
return BuiltinPassResourceSemantic::VolumeField;
|
return BuiltinPassResourceSemantic::VolumeField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (semantic == Containers::String("gaussiansplatpositionbuffer") ||
|
||||||
|
semantic == Containers::String("gaussiansplatpositions") ||
|
||||||
|
semantic == Containers::String("splatpositions")) {
|
||||||
|
return BuiltinPassResourceSemantic::GaussianSplatPositionBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (semantic == Containers::String("gaussiansplatotherbuffer") ||
|
||||||
|
semantic == Containers::String("gaussiansplatother") ||
|
||||||
|
semantic == Containers::String("splatother")) {
|
||||||
|
return BuiltinPassResourceSemantic::GaussianSplatOtherBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (semantic == Containers::String("gaussiansplatcolorbuffer") ||
|
||||||
|
semantic == Containers::String("gaussiansplatcolor") ||
|
||||||
|
semantic == Containers::String("splatcolor")) {
|
||||||
|
return BuiltinPassResourceSemantic::GaussianSplatColorBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (semantic == Containers::String("gaussiansplatshbuffer") ||
|
||||||
|
semantic == Containers::String("gaussiansplatsh") ||
|
||||||
|
semantic == Containers::String("splatsh")) {
|
||||||
|
return BuiltinPassResourceSemantic::GaussianSplatSHBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
if (semantic == Containers::String("basecolortexture") ||
|
if (semantic == Containers::String("basecolortexture") ||
|
||||||
semantic == Containers::String("maintex")) {
|
semantic == Containers::String("maintex")) {
|
||||||
return BuiltinPassResourceSemantic::BaseColorTexture;
|
return BuiltinPassResourceSemantic::BaseColorTexture;
|
||||||
@@ -207,6 +233,14 @@ inline const char* BuiltinPassResourceSemanticToString(BuiltinPassResourceSemant
|
|||||||
return "PassConstants";
|
return "PassConstants";
|
||||||
case BuiltinPassResourceSemantic::VolumeField:
|
case BuiltinPassResourceSemantic::VolumeField:
|
||||||
return "VolumeField";
|
return "VolumeField";
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
||||||
|
return "GaussianSplatPositionBuffer";
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
|
||||||
|
return "GaussianSplatOtherBuffer";
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
|
||||||
|
return "GaussianSplatColorBuffer";
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
|
return "GaussianSplatSHBuffer";
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
return "BaseColorTexture";
|
return "BaseColorTexture";
|
||||||
case BuiltinPassResourceSemantic::SourceColorTexture:
|
case BuiltinPassResourceSemantic::SourceColorTexture:
|
||||||
@@ -295,6 +329,12 @@ inline bool IsBuiltinPassResourceTypeCompatible(
|
|||||||
case BuiltinPassResourceSemantic::VolumeField:
|
case BuiltinPassResourceSemantic::VolumeField:
|
||||||
return type == Resources::ShaderResourceType::StructuredBuffer ||
|
return type == Resources::ShaderResourceType::StructuredBuffer ||
|
||||||
type == Resources::ShaderResourceType::RawBuffer;
|
type == Resources::ShaderResourceType::RawBuffer;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
|
return type == Resources::ShaderResourceType::StructuredBuffer ||
|
||||||
|
type == Resources::ShaderResourceType::RawBuffer;
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
case BuiltinPassResourceSemantic::SourceColorTexture:
|
case BuiltinPassResourceSemantic::SourceColorTexture:
|
||||||
case BuiltinPassResourceSemantic::SkyboxPanoramicTexture:
|
case BuiltinPassResourceSemantic::SkyboxPanoramicTexture:
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ enum class BuiltinMaterialPass : Core::uint32 {
|
|||||||
ObjectId,
|
ObjectId,
|
||||||
SelectionMask,
|
SelectionMask,
|
||||||
Skybox,
|
Skybox,
|
||||||
|
GaussianSplat,
|
||||||
Volumetric,
|
Volumetric,
|
||||||
PostProcess,
|
PostProcess,
|
||||||
FinalColor,
|
FinalColor,
|
||||||
@@ -45,6 +46,11 @@ enum class BuiltinPassResourceSemantic : Core::uint8 {
|
|||||||
Environment,
|
Environment,
|
||||||
PassConstants,
|
PassConstants,
|
||||||
VolumeField,
|
VolumeField,
|
||||||
|
GaussianSplatOrderBuffer,
|
||||||
|
GaussianSplatPositionBuffer,
|
||||||
|
GaussianSplatOtherBuffer,
|
||||||
|
GaussianSplatColorBuffer,
|
||||||
|
GaussianSplatSHBuffer,
|
||||||
BaseColorTexture,
|
BaseColorTexture,
|
||||||
SourceColorTexture,
|
SourceColorTexture,
|
||||||
SkyboxPanoramicTexture,
|
SkyboxPanoramicTexture,
|
||||||
@@ -78,6 +84,11 @@ struct BuiltinPassResourceBindingPlan {
|
|||||||
PassResourceBindingLocation shadowReceiver = {};
|
PassResourceBindingLocation shadowReceiver = {};
|
||||||
PassResourceBindingLocation environment = {};
|
PassResourceBindingLocation environment = {};
|
||||||
PassResourceBindingLocation passConstants = {};
|
PassResourceBindingLocation passConstants = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatOrderBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
||||||
PassResourceBindingLocation baseColorTexture = {};
|
PassResourceBindingLocation baseColorTexture = {};
|
||||||
PassResourceBindingLocation sourceColorTexture = {};
|
PassResourceBindingLocation sourceColorTexture = {};
|
||||||
PassResourceBindingLocation skyboxPanoramicTexture = {};
|
PassResourceBindingLocation skyboxPanoramicTexture = {};
|
||||||
@@ -111,6 +122,11 @@ struct BuiltinPassSetLayoutMetadata {
|
|||||||
bool usesPassConstants = false;
|
bool usesPassConstants = false;
|
||||||
bool usesMaterialBuffers = false;
|
bool usesMaterialBuffers = false;
|
||||||
bool usesVolumeField = false;
|
bool usesVolumeField = false;
|
||||||
|
bool usesGaussianSplatOrderBuffer = false;
|
||||||
|
bool usesGaussianSplatPositionBuffer = false;
|
||||||
|
bool usesGaussianSplatOtherBuffer = false;
|
||||||
|
bool usesGaussianSplatColorBuffer = false;
|
||||||
|
bool usesGaussianSplatSHBuffer = false;
|
||||||
bool usesTexture = false;
|
bool usesTexture = false;
|
||||||
bool usesBaseColorTexture = false;
|
bool usesBaseColorTexture = false;
|
||||||
bool usesSourceColorTexture = false;
|
bool usesSourceColorTexture = false;
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/Core/Asset/ResourceHandle.h>
|
||||||
|
#include <XCEngine/Core/Math/Matrix4.h>
|
||||||
|
#include <XCEngine/Rendering/Builtin/BuiltinPassTypes.h>
|
||||||
|
#include <XCEngine/Rendering/Caches/RenderResourceCache.h>
|
||||||
|
#include <XCEngine/Rendering/Materials/RenderMaterialResolve.h>
|
||||||
|
#include <XCEngine/Rendering/Materials/RenderMaterialStateUtils.h>
|
||||||
|
#include <XCEngine/Rendering/RenderPass.h>
|
||||||
|
#include <XCEngine/RHI/RHIDescriptorPool.h>
|
||||||
|
#include <XCEngine/RHI/RHIDescriptorSet.h>
|
||||||
|
#include <XCEngine/RHI/RHIPipelineLayout.h>
|
||||||
|
#include <XCEngine/RHI/RHIPipelineState.h>
|
||||||
|
#include <XCEngine/Resources/Material/Material.h>
|
||||||
|
#include <XCEngine/Resources/Shader/Shader.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
class GameObject;
|
||||||
|
} // namespace Components
|
||||||
|
|
||||||
|
namespace Resources {
|
||||||
|
class GaussianSplat;
|
||||||
|
class Material;
|
||||||
|
class Shader;
|
||||||
|
} // namespace Resources
|
||||||
|
|
||||||
|
namespace Rendering {
|
||||||
|
struct VisibleGaussianSplatItem;
|
||||||
|
|
||||||
|
namespace Passes {
|
||||||
|
|
||||||
|
class BuiltinGaussianSplatPass final : public RenderPass {
|
||||||
|
public:
|
||||||
|
~BuiltinGaussianSplatPass() override;
|
||||||
|
|
||||||
|
const char* GetName() const override;
|
||||||
|
bool Initialize(const RenderContext& context) override;
|
||||||
|
bool PrepareGaussianSplatResources(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSceneData& sceneData);
|
||||||
|
bool Execute(const RenderPassContext& context) override;
|
||||||
|
void Shutdown() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct PerObjectConstants {
|
||||||
|
Math::Matrix4x4 projection = Math::Matrix4x4::Identity();
|
||||||
|
Math::Matrix4x4 view = Math::Matrix4x4::Identity();
|
||||||
|
Math::Matrix4x4 model = Math::Matrix4x4::Identity();
|
||||||
|
Math::Vector4 cameraRight = Math::Vector4::Zero();
|
||||||
|
Math::Vector4 cameraUp = Math::Vector4::Zero();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OwnedDescriptorSet {
|
||||||
|
RHI::RHIDescriptorPool* pool = nullptr;
|
||||||
|
RHI::RHIDescriptorSet* set = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PassLayoutKey {
|
||||||
|
const Resources::Shader* shader = nullptr;
|
||||||
|
Containers::String passName;
|
||||||
|
|
||||||
|
bool operator==(const PassLayoutKey& other) const {
|
||||||
|
return shader == other.shader && passName == other.passName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PassLayoutKeyHash {
|
||||||
|
size_t operator()(const PassLayoutKey& key) const noexcept {
|
||||||
|
size_t hash = reinterpret_cast<size_t>(key.shader);
|
||||||
|
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PassResourceLayout {
|
||||||
|
RHI::RHIPipelineLayout* pipelineLayout = nullptr;
|
||||||
|
Core::uint32 firstDescriptorSet = 0;
|
||||||
|
Core::uint32 descriptorSetCount = 0;
|
||||||
|
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
||||||
|
PassResourceBindingLocation perObject = {};
|
||||||
|
PassResourceBindingLocation material = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DynamicDescriptorSetKey {
|
||||||
|
PassLayoutKey passLayout = {};
|
||||||
|
Core::uint32 setIndex = 0;
|
||||||
|
Core::uint64 objectId = 0;
|
||||||
|
const Resources::Material* material = nullptr;
|
||||||
|
const Resources::GaussianSplat* gaussianSplat = nullptr;
|
||||||
|
|
||||||
|
bool operator==(const DynamicDescriptorSetKey& other) const {
|
||||||
|
return passLayout == other.passLayout &&
|
||||||
|
setIndex == other.setIndex &&
|
||||||
|
objectId == other.objectId &&
|
||||||
|
material == other.material &&
|
||||||
|
gaussianSplat == other.gaussianSplat;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DynamicDescriptorSetKeyHash {
|
||||||
|
size_t operator()(const DynamicDescriptorSetKey& key) const noexcept {
|
||||||
|
size_t hash = PassLayoutKeyHash()(key.passLayout);
|
||||||
|
hash ^= std::hash<Core::uint32>{}(key.setIndex) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Core::uint64>{}(key.objectId) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= reinterpret_cast<size_t>(key.material) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= reinterpret_cast<size_t>(key.gaussianSplat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CachedDescriptorSet {
|
||||||
|
OwnedDescriptorSet descriptorSet = {};
|
||||||
|
Core::uint64 materialVersion = 0;
|
||||||
|
RHI::RHIResourceView* positionsView = nullptr;
|
||||||
|
RHI::RHIResourceView* otherView = nullptr;
|
||||||
|
RHI::RHIResourceView* colorView = nullptr;
|
||||||
|
RHI::RHIResourceView* shView = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ResolvedShaderPass {
|
||||||
|
const Resources::Shader* shader = nullptr;
|
||||||
|
const Resources::ShaderPass* pass = nullptr;
|
||||||
|
Containers::String passName;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipelineStateKey {
|
||||||
|
Resources::MaterialRenderState renderState;
|
||||||
|
const Resources::Shader* shader = nullptr;
|
||||||
|
Containers::String passName;
|
||||||
|
Containers::String keywordSignature;
|
||||||
|
Core::uint32 renderTargetCount = 0;
|
||||||
|
Core::uint32 renderTargetFormat = 0;
|
||||||
|
Core::uint32 depthStencilFormat = 0;
|
||||||
|
Core::uint32 sampleCount = 1;
|
||||||
|
Core::uint32 sampleQuality = 0;
|
||||||
|
|
||||||
|
bool operator==(const PipelineStateKey& other) const {
|
||||||
|
return renderState == other.renderState &&
|
||||||
|
shader == other.shader &&
|
||||||
|
passName == other.passName &&
|
||||||
|
keywordSignature == other.keywordSignature &&
|
||||||
|
renderTargetCount == other.renderTargetCount &&
|
||||||
|
renderTargetFormat == other.renderTargetFormat &&
|
||||||
|
depthStencilFormat == other.depthStencilFormat &&
|
||||||
|
sampleCount == other.sampleCount &&
|
||||||
|
sampleQuality == other.sampleQuality;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipelineStateKeyHash {
|
||||||
|
size_t operator()(const PipelineStateKey& key) const noexcept {
|
||||||
|
size_t hash = MaterialRenderStateHash()(key.renderState);
|
||||||
|
hash ^= reinterpret_cast<size_t>(key.shader) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Containers::String>{}(key.keywordSignature) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Core::uint32>{}(key.renderTargetCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Core::uint32>{}(key.renderTargetFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Core::uint32>{}(key.depthStencilFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Core::uint32>{}(key.sampleCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Core::uint32>{}(key.sampleQuality) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool EnsureInitialized(const RenderContext& context);
|
||||||
|
bool CreateResources(const RenderContext& context);
|
||||||
|
void DestroyResources();
|
||||||
|
|
||||||
|
const Resources::Material* ResolveGaussianSplatMaterial(
|
||||||
|
const VisibleGaussianSplatItem& visibleGaussianSplat) const;
|
||||||
|
ResolvedShaderPass ResolveGaussianSplatShaderPass(
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const Resources::Material* material) const;
|
||||||
|
PassResourceLayout* GetOrCreatePassResourceLayout(
|
||||||
|
const RenderContext& context,
|
||||||
|
const ResolvedShaderPass& resolvedShaderPass);
|
||||||
|
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSurface& surface,
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const Resources::Material* material);
|
||||||
|
bool CreateOwnedDescriptorSet(
|
||||||
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
|
OwnedDescriptorSet& descriptorSet);
|
||||||
|
CachedDescriptorSet* GetOrCreateDynamicDescriptorSet(
|
||||||
|
const PassLayoutKey& passLayoutKey,
|
||||||
|
const PassResourceLayout& passLayout,
|
||||||
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
|
Core::uint32 setIndex,
|
||||||
|
Core::uint64 objectId,
|
||||||
|
const Resources::Material* material,
|
||||||
|
const Resources::GaussianSplat* gaussianSplat,
|
||||||
|
const MaterialConstantPayloadView& materialConstants,
|
||||||
|
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat);
|
||||||
|
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
||||||
|
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||||
|
bool DrawVisibleGaussianSplat(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSurface& surface,
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const VisibleGaussianSplatItem& visibleGaussianSplat);
|
||||||
|
|
||||||
|
RHI::RHIDevice* m_device = nullptr;
|
||||||
|
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
|
||||||
|
Resources::ResourceHandle<Resources::Shader> m_builtinGaussianSplatShader;
|
||||||
|
std::unique_ptr<Resources::Material> m_builtinGaussianSplatMaterial;
|
||||||
|
RenderResourceCache m_resourceCache;
|
||||||
|
|
||||||
|
std::unordered_map<PassLayoutKey, PassResourceLayout, PassLayoutKeyHash> m_passResourceLayouts;
|
||||||
|
std::unordered_map<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
|
||||||
|
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Passes
|
||||||
|
} // namespace Rendering
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -39,6 +39,7 @@ namespace Pipelines {
|
|||||||
} // namespace Pipelines
|
} // namespace Pipelines
|
||||||
|
|
||||||
namespace Passes {
|
namespace Passes {
|
||||||
|
class BuiltinGaussianSplatPass;
|
||||||
class BuiltinVolumetricPass;
|
class BuiltinVolumetricPass;
|
||||||
} // namespace Passes
|
} // namespace Passes
|
||||||
|
|
||||||
@@ -355,6 +356,7 @@ private:
|
|||||||
OwnedDescriptorSet m_skyboxSamplerSet = {};
|
OwnedDescriptorSet m_skyboxSamplerSet = {};
|
||||||
RHI::RHIResourceView* m_skyboxBoundPanoramicTextureView = nullptr;
|
RHI::RHIResourceView* m_skyboxBoundPanoramicTextureView = nullptr;
|
||||||
RHI::RHIResourceView* m_skyboxBoundCubemapTextureView = nullptr;
|
RHI::RHIResourceView* m_skyboxBoundCubemapTextureView = nullptr;
|
||||||
|
std::unique_ptr<Passes::BuiltinGaussianSplatPass> m_gaussianSplatPass;
|
||||||
std::unique_ptr<Passes::BuiltinVolumetricPass> m_volumetricPass;
|
std::unique_ptr<Passes::BuiltinVolumetricPass> m_volumetricPass;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ Containers::String GetBuiltinObjectIdOutlineShaderPath();
|
|||||||
Containers::String GetBuiltinSelectionMaskShaderPath();
|
Containers::String GetBuiltinSelectionMaskShaderPath();
|
||||||
Containers::String GetBuiltinSelectionOutlineShaderPath();
|
Containers::String GetBuiltinSelectionOutlineShaderPath();
|
||||||
Containers::String GetBuiltinSkyboxShaderPath();
|
Containers::String GetBuiltinSkyboxShaderPath();
|
||||||
|
Containers::String GetBuiltinGaussianSplatShaderPath();
|
||||||
Containers::String GetBuiltinVolumetricShaderPath();
|
Containers::String GetBuiltinVolumetricShaderPath();
|
||||||
Containers::String GetBuiltinColorScalePostProcessShaderPath();
|
Containers::String GetBuiltinColorScalePostProcessShaderPath();
|
||||||
Containers::String GetBuiltinFinalColorShaderPath();
|
Containers::String GetBuiltinFinalColorShaderPath();
|
||||||
|
|||||||
730
engine/src/Rendering/Passes/BuiltinGaussianSplatPass.cpp
Normal file
730
engine/src/Rendering/Passes/BuiltinGaussianSplatPass.cpp
Normal file
@@ -0,0 +1,730 @@
|
|||||||
|
#include "Rendering/Passes/BuiltinGaussianSplatPass.h"
|
||||||
|
|
||||||
|
#include "Components/GameObject.h"
|
||||||
|
#include "Debug/Logger.h"
|
||||||
|
#include "RHI/RHICommandList.h"
|
||||||
|
#include "RHI/RHIDevice.h"
|
||||||
|
#include "Rendering/Builtin/BuiltinPassLayoutUtils.h"
|
||||||
|
#include "Rendering/FrameData/RenderSceneData.h"
|
||||||
|
#include "Rendering/FrameData/VisibleGaussianSplatItem.h"
|
||||||
|
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
|
||||||
|
#include "Rendering/Internal/ShaderVariantUtils.h"
|
||||||
|
#include "Rendering/RenderSurface.h"
|
||||||
|
#include "Resources/BuiltinResources.h"
|
||||||
|
#include "Resources/GaussianSplat/GaussianSplat.h"
|
||||||
|
#include "Resources/Material/Material.h"
|
||||||
|
#include "Resources/Shader/Shader.h"
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Rendering {
|
||||||
|
namespace Passes {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Resources::ShaderKeywordSet ResolvePassKeywordSet(
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const Resources::Material* material) {
|
||||||
|
return Resources::CombineShaderKeywordSets(
|
||||||
|
sceneData.globalShaderKeywords,
|
||||||
|
material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
const Resources::ShaderPass* FindCompatibleGaussianSplatPass(
|
||||||
|
const Resources::Shader& shader,
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const Resources::Material* material,
|
||||||
|
Resources::ShaderBackend backend) {
|
||||||
|
const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material);
|
||||||
|
for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) {
|
||||||
|
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::GaussianSplat) &&
|
||||||
|
::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(
|
||||||
|
shader,
|
||||||
|
shaderPass.name,
|
||||||
|
backend,
|
||||||
|
keywordSet)) {
|
||||||
|
return &shaderPass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||||
|
RHI::RHIType backendType,
|
||||||
|
RHI::RHIPipelineLayout* pipelineLayout,
|
||||||
|
const Resources::Shader& shader,
|
||||||
|
const Resources::ShaderPass& shaderPass,
|
||||||
|
const Containers::String& passName,
|
||||||
|
const Resources::ShaderKeywordSet& keywordSet,
|
||||||
|
const Resources::Material* material,
|
||||||
|
const RenderSurface& surface) {
|
||||||
|
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
||||||
|
pipelineDesc.pipelineLayout = pipelineLayout;
|
||||||
|
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
||||||
|
::XCEngine::Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc(surface, pipelineDesc);
|
||||||
|
pipelineDesc.depthStencilFormat =
|
||||||
|
static_cast<uint32_t>(::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface));
|
||||||
|
ApplyResolvedRenderState(&shaderPass, material, pipelineDesc);
|
||||||
|
|
||||||
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType);
|
||||||
|
if (const Resources::ShaderStageVariant* vertexVariant =
|
||||||
|
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend, keywordSet)) {
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
shader.GetPath(),
|
||||||
|
shaderPass,
|
||||||
|
backend,
|
||||||
|
*vertexVariant,
|
||||||
|
pipelineDesc.vertexShader);
|
||||||
|
}
|
||||||
|
if (const Resources::ShaderStageVariant* fragmentVariant =
|
||||||
|
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend, keywordSet)) {
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
shader.GetPath(),
|
||||||
|
shaderPass,
|
||||||
|
backend,
|
||||||
|
*fragmentVariant,
|
||||||
|
pipelineDesc.fragmentShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipelineDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BuiltinGaussianSplatPass::~BuiltinGaussianSplatPass() {
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* BuiltinGaussianSplatPass::GetName() const {
|
||||||
|
return "BuiltinGaussianSplatPass";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::Initialize(const RenderContext& context) {
|
||||||
|
return EnsureInitialized(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSceneData& sceneData) {
|
||||||
|
if (!EnsureInitialized(context)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const VisibleGaussianSplatItem& visibleGaussianSplat : sceneData.visibleGaussianSplats) {
|
||||||
|
if (visibleGaussianSplat.gaussianSplat == nullptr ||
|
||||||
|
!visibleGaussianSplat.gaussianSplat->IsValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat =
|
||||||
|
m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat);
|
||||||
|
if (cachedGaussianSplat == nullptr ||
|
||||||
|
cachedGaussianSplat->positions.shaderResourceView == nullptr ||
|
||||||
|
cachedGaussianSplat->other.shaderResourceView == nullptr ||
|
||||||
|
cachedGaussianSplat->color.shaderResourceView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::Execute(const RenderPassContext& context) {
|
||||||
|
if (!context.renderContext.IsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.sceneData.visibleGaussianSplats.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<RHI::RHIResourceView*>& colorAttachments = context.surface.GetColorAttachments();
|
||||||
|
if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(context.surface) ||
|
||||||
|
colorAttachments.empty() ||
|
||||||
|
colorAttachments[0] == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Math::RectInt renderArea = context.surface.GetRenderArea();
|
||||||
|
if (renderArea.width <= 0 || renderArea.height <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PrepareGaussianSplatResources(context.renderContext, context.sceneData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHICommandList* commandList = context.renderContext.commandList;
|
||||||
|
RHI::RHIResourceView* renderTarget = colorAttachments[0];
|
||||||
|
commandList->SetRenderTargets(1, &renderTarget, context.surface.GetDepthAttachment());
|
||||||
|
|
||||||
|
const RHI::Viewport viewport = {
|
||||||
|
static_cast<float>(renderArea.x),
|
||||||
|
static_cast<float>(renderArea.y),
|
||||||
|
static_cast<float>(renderArea.width),
|
||||||
|
static_cast<float>(renderArea.height),
|
||||||
|
0.0f,
|
||||||
|
1.0f
|
||||||
|
};
|
||||||
|
const RHI::Rect scissorRect = {
|
||||||
|
renderArea.x,
|
||||||
|
renderArea.y,
|
||||||
|
renderArea.x + renderArea.width,
|
||||||
|
renderArea.y + renderArea.height
|
||||||
|
};
|
||||||
|
commandList->SetViewport(viewport);
|
||||||
|
commandList->SetScissorRect(scissorRect);
|
||||||
|
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
||||||
|
|
||||||
|
for (const VisibleGaussianSplatItem& visibleGaussianSplat : context.sceneData.visibleGaussianSplats) {
|
||||||
|
if (!DrawVisibleGaussianSplat(
|
||||||
|
context.renderContext,
|
||||||
|
context.surface,
|
||||||
|
context.sceneData,
|
||||||
|
visibleGaussianSplat)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltinGaussianSplatPass::Shutdown() {
|
||||||
|
DestroyResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::EnsureInitialized(const RenderContext& context) {
|
||||||
|
if (!context.IsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_device == context.device &&
|
||||||
|
m_backendType == context.backendType &&
|
||||||
|
m_builtinGaussianSplatShader.IsValid() &&
|
||||||
|
m_builtinGaussianSplatMaterial != nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyResources();
|
||||||
|
return CreateResources(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::CreateResources(const RenderContext& context) {
|
||||||
|
m_device = context.device;
|
||||||
|
m_backendType = context.backendType;
|
||||||
|
m_builtinGaussianSplatShader = Resources::ResourceManager::Get().Load<Resources::Shader>(
|
||||||
|
Resources::GetBuiltinGaussianSplatShaderPath());
|
||||||
|
if (!m_builtinGaussianSplatShader.IsValid()) {
|
||||||
|
Debug::Logger::Get().Error(
|
||||||
|
Debug::LogCategory::Rendering,
|
||||||
|
"BuiltinGaussianSplatPass failed to load builtin gaussian splat shader resource");
|
||||||
|
DestroyResources();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_builtinGaussianSplatMaterial = std::make_unique<Resources::Material>();
|
||||||
|
Resources::IResource::ConstructParams params = {};
|
||||||
|
params.name = "BuiltinGaussianSplatMaterial";
|
||||||
|
params.path = "builtin://materials/gaussian-splat-default";
|
||||||
|
params.guid = Resources::ResourceGUID::Generate(params.path);
|
||||||
|
m_builtinGaussianSplatMaterial->Initialize(params);
|
||||||
|
m_builtinGaussianSplatMaterial->SetShader(m_builtinGaussianSplatShader);
|
||||||
|
m_builtinGaussianSplatMaterial->SetRenderQueue(Resources::MaterialRenderQueue::Transparent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltinGaussianSplatPass::DestroyResources() {
|
||||||
|
m_resourceCache.Shutdown();
|
||||||
|
|
||||||
|
for (auto& pipelinePair : m_pipelineStates) {
|
||||||
|
if (pipelinePair.second != nullptr) {
|
||||||
|
pipelinePair.second->Shutdown();
|
||||||
|
delete pipelinePair.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_pipelineStates.clear();
|
||||||
|
|
||||||
|
for (auto& descriptorSetPair : m_dynamicDescriptorSets) {
|
||||||
|
DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet);
|
||||||
|
}
|
||||||
|
m_dynamicDescriptorSets.clear();
|
||||||
|
|
||||||
|
for (auto& passLayoutPair : m_passResourceLayouts) {
|
||||||
|
DestroyPassResourceLayout(passLayoutPair.second);
|
||||||
|
}
|
||||||
|
m_passResourceLayouts.clear();
|
||||||
|
|
||||||
|
m_builtinGaussianSplatMaterial.reset();
|
||||||
|
m_builtinGaussianSplatShader.Reset();
|
||||||
|
m_device = nullptr;
|
||||||
|
m_backendType = RHI::RHIType::D3D12;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Resources::Material* BuiltinGaussianSplatPass::ResolveGaussianSplatMaterial(
|
||||||
|
const VisibleGaussianSplatItem& visibleGaussianSplat) const {
|
||||||
|
if (visibleGaussianSplat.material != nullptr &&
|
||||||
|
visibleGaussianSplat.material->GetShader() != nullptr &&
|
||||||
|
MatchesBuiltinPass(visibleGaussianSplat.material, BuiltinMaterialPass::GaussianSplat)) {
|
||||||
|
return visibleGaussianSplat.material;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_builtinGaussianSplatMaterial.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolveGaussianSplatShaderPass(
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const Resources::Material* material) const {
|
||||||
|
ResolvedShaderPass resolved = {};
|
||||||
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType);
|
||||||
|
|
||||||
|
if (material != nullptr && material->GetShader() != nullptr) {
|
||||||
|
const Resources::Shader* shader = material->GetShader();
|
||||||
|
if (const Resources::ShaderPass* shaderPass =
|
||||||
|
FindCompatibleGaussianSplatPass(*shader, sceneData, material, backend)) {
|
||||||
|
resolved.shader = shader;
|
||||||
|
resolved.pass = shaderPass;
|
||||||
|
resolved.passName = shaderPass->name;
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_builtinGaussianSplatShader.IsValid()) {
|
||||||
|
const Resources::Shader* shader = m_builtinGaussianSplatShader.Get();
|
||||||
|
if (const Resources::ShaderPass* shaderPass =
|
||||||
|
FindCompatibleGaussianSplatPass(*shader, sceneData, material, backend)) {
|
||||||
|
resolved.shader = shader;
|
||||||
|
resolved.pass = shaderPass;
|
||||||
|
resolved.passName = shaderPass->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCreatePassResourceLayout(
|
||||||
|
const RenderContext& context,
|
||||||
|
const ResolvedShaderPass& resolvedShaderPass) {
|
||||||
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassLayoutKey passLayoutKey = {};
|
||||||
|
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||||
|
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||||
|
|
||||||
|
const auto existing = m_passResourceLayouts.find(passLayoutKey);
|
||||||
|
if (existing != m_passResourceLayouts.end()) {
|
||||||
|
return &existing->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassResourceLayout passLayout = {};
|
||||||
|
auto failLayout = [this, &passLayout](const char* message) -> PassResourceLayout* {
|
||||||
|
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message);
|
||||||
|
DestroyPassResourceLayout(passLayout);
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
BuiltinPassResourceBindingPlan bindingPlan = {};
|
||||||
|
Containers::String bindingPlanError;
|
||||||
|
if (!TryBuildBuiltinPassResourceBindingPlan(*resolvedShaderPass.pass, bindingPlan, &bindingPlanError)) {
|
||||||
|
const Containers::String contextualError =
|
||||||
|
Containers::String("BuiltinGaussianSplatPass failed to resolve pass resource bindings for shader='") +
|
||||||
|
resolvedShaderPass.shader->GetPath() +
|
||||||
|
"', pass='" + resolvedShaderPass.passName +
|
||||||
|
"': " + bindingPlanError +
|
||||||
|
". Bindings: " + DescribeShaderResourceBindings(resolvedShaderPass.pass->resources);
|
||||||
|
return failLayout(contextualError.CStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
Containers::String setLayoutError;
|
||||||
|
if (!TryBuildBuiltinPassSetLayouts(bindingPlan, passLayout.setLayouts, &setLayoutError)) {
|
||||||
|
return failLayout(setLayoutError.CStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet;
|
||||||
|
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
|
||||||
|
passLayout.perObject = bindingPlan.perObject;
|
||||||
|
passLayout.material = bindingPlan.material;
|
||||||
|
passLayout.gaussianSplatPositionBuffer = bindingPlan.gaussianSplatPositionBuffer;
|
||||||
|
passLayout.gaussianSplatOtherBuffer = bindingPlan.gaussianSplatOtherBuffer;
|
||||||
|
passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer;
|
||||||
|
passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer;
|
||||||
|
|
||||||
|
if (!passLayout.perObject.IsValid()) {
|
||||||
|
return failLayout("BuiltinGaussianSplatPass requires a PerObject resource binding");
|
||||||
|
}
|
||||||
|
if (!passLayout.material.IsValid()) {
|
||||||
|
return failLayout("BuiltinGaussianSplatPass requires a Material resource binding");
|
||||||
|
}
|
||||||
|
if (!passLayout.gaussianSplatPositionBuffer.IsValid() ||
|
||||||
|
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
||||||
|
!passLayout.gaussianSplatColorBuffer.IsValid()) {
|
||||||
|
return failLayout("BuiltinGaussianSplatPass requires position, other, and color gaussian splat buffer bindings");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<RHI::DescriptorSetLayoutDesc> nativeSetLayouts(passLayout.setLayouts.size());
|
||||||
|
for (size_t setIndex = 0; setIndex < passLayout.setLayouts.size(); ++setIndex) {
|
||||||
|
nativeSetLayouts[setIndex] = passLayout.setLayouts[setIndex].layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
||||||
|
pipelineLayoutDesc.setLayouts = nativeSetLayouts.empty() ? nullptr : nativeSetLayouts.data();
|
||||||
|
pipelineLayoutDesc.setLayoutCount = static_cast<Core::uint32>(nativeSetLayouts.size());
|
||||||
|
passLayout.pipelineLayout = context.device->CreatePipelineLayout(pipelineLayoutDesc);
|
||||||
|
if (passLayout.pipelineLayout == nullptr) {
|
||||||
|
return failLayout("BuiltinGaussianSplatPass failed to create pipeline layout from shader pass resources");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = m_passResourceLayouts.emplace(passLayoutKey, passLayout);
|
||||||
|
PassResourceLayout& storedPassLayout = result.first->second;
|
||||||
|
RefreshBuiltinPassSetLayouts(storedPassLayout.setLayouts);
|
||||||
|
return &storedPassLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreatePipelineState(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSurface& surface,
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const Resources::Material* material) {
|
||||||
|
const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material);
|
||||||
|
const ResolvedShaderPass resolvedShaderPass = ResolveGaussianSplatShaderPass(sceneData, material);
|
||||||
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||||
|
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipelineStateKey pipelineKey = {};
|
||||||
|
pipelineKey.renderState =
|
||||||
|
BuildStaticPipelineRenderStateKey(ResolveEffectiveRenderState(resolvedShaderPass.pass, material));
|
||||||
|
pipelineKey.shader = resolvedShaderPass.shader;
|
||||||
|
pipelineKey.passName = resolvedShaderPass.passName;
|
||||||
|
pipelineKey.keywordSignature = ::XCEngine::Rendering::Internal::BuildShaderKeywordSignature(keywordSet);
|
||||||
|
pipelineKey.renderTargetCount =
|
||||||
|
::XCEngine::Rendering::Internal::HasSingleColorAttachment(surface) ? 1u : 0u;
|
||||||
|
pipelineKey.renderTargetFormat =
|
||||||
|
static_cast<Core::uint32>(::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u));
|
||||||
|
pipelineKey.depthStencilFormat =
|
||||||
|
static_cast<Core::uint32>(::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface));
|
||||||
|
pipelineKey.sampleCount = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface);
|
||||||
|
pipelineKey.sampleQuality = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleQuality(surface);
|
||||||
|
|
||||||
|
const auto existing = m_pipelineStates.find(pipelineKey);
|
||||||
|
if (existing != m_pipelineStates.end()) {
|
||||||
|
return existing->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RHI::GraphicsPipelineDesc pipelineDesc =
|
||||||
|
CreatePipelineDesc(
|
||||||
|
context.backendType,
|
||||||
|
passLayout->pipelineLayout,
|
||||||
|
*resolvedShaderPass.shader,
|
||||||
|
*resolvedShaderPass.pass,
|
||||||
|
resolvedShaderPass.passName,
|
||||||
|
keywordSet,
|
||||||
|
material,
|
||||||
|
surface);
|
||||||
|
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
|
||||||
|
if (pipelineState == nullptr || !pipelineState->IsValid()) {
|
||||||
|
if (pipelineState != nullptr) {
|
||||||
|
pipelineState->Shutdown();
|
||||||
|
delete pipelineState;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pipelineStates.emplace(pipelineKey, pipelineState);
|
||||||
|
return pipelineState;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::CreateOwnedDescriptorSet(
|
||||||
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
|
OwnedDescriptorSet& descriptorSet) {
|
||||||
|
RHI::DescriptorPoolDesc poolDesc = {};
|
||||||
|
poolDesc.type = setLayout.heapType;
|
||||||
|
poolDesc.descriptorCount = CountBuiltinPassHeapDescriptors(setLayout.heapType, setLayout.bindings);
|
||||||
|
poolDesc.shaderVisible = setLayout.shaderVisible;
|
||||||
|
|
||||||
|
descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc);
|
||||||
|
if (descriptorSet.pool == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptorSet.set = descriptorSet.pool->AllocateSet(setLayout.layout);
|
||||||
|
if (descriptorSet.set == nullptr) {
|
||||||
|
DestroyOwnedDescriptorSet(descriptorSet);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCreateDynamicDescriptorSet(
|
||||||
|
const PassLayoutKey& passLayoutKey,
|
||||||
|
const PassResourceLayout& passLayout,
|
||||||
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
|
Core::uint32 setIndex,
|
||||||
|
Core::uint64 objectId,
|
||||||
|
const Resources::Material* material,
|
||||||
|
const Resources::GaussianSplat* gaussianSplat,
|
||||||
|
const MaterialConstantPayloadView& materialConstants,
|
||||||
|
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat) {
|
||||||
|
DynamicDescriptorSetKey key = {};
|
||||||
|
key.passLayout = passLayoutKey;
|
||||||
|
key.setIndex = setIndex;
|
||||||
|
key.objectId = objectId;
|
||||||
|
key.material = material;
|
||||||
|
key.gaussianSplat = gaussianSplat;
|
||||||
|
|
||||||
|
CachedDescriptorSet& cachedDescriptorSet = m_dynamicDescriptorSets[key];
|
||||||
|
if (cachedDescriptorSet.descriptorSet.set == nullptr) {
|
||||||
|
if (!CreateOwnedDescriptorSet(setLayout, cachedDescriptorSet.descriptorSet)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0u;
|
||||||
|
if (setLayout.usesMaterial) {
|
||||||
|
if (!passLayout.material.IsValid() ||
|
||||||
|
passLayout.material.set != setIndex ||
|
||||||
|
!materialConstants.IsValid()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.materialVersion != materialVersion) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||||
|
passLayout.material.binding,
|
||||||
|
materialConstants.data,
|
||||||
|
materialConstants.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatPositionBuffer) {
|
||||||
|
if (cachedGaussianSplat.positions.shaderResourceView == nullptr ||
|
||||||
|
!passLayout.gaussianSplatPositionBuffer.IsValid() ||
|
||||||
|
passLayout.gaussianSplatPositionBuffer.set != setIndex) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.positionsView != cachedGaussianSplat.positions.shaderResourceView) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->Update(
|
||||||
|
passLayout.gaussianSplatPositionBuffer.binding,
|
||||||
|
cachedGaussianSplat.positions.shaderResourceView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatOtherBuffer) {
|
||||||
|
if (cachedGaussianSplat.other.shaderResourceView == nullptr ||
|
||||||
|
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
||||||
|
passLayout.gaussianSplatOtherBuffer.set != setIndex) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.otherView != cachedGaussianSplat.other.shaderResourceView) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->Update(
|
||||||
|
passLayout.gaussianSplatOtherBuffer.binding,
|
||||||
|
cachedGaussianSplat.other.shaderResourceView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatColorBuffer) {
|
||||||
|
if (cachedGaussianSplat.color.shaderResourceView == nullptr ||
|
||||||
|
!passLayout.gaussianSplatColorBuffer.IsValid() ||
|
||||||
|
passLayout.gaussianSplatColorBuffer.set != setIndex) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.colorView != cachedGaussianSplat.color.shaderResourceView) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->Update(
|
||||||
|
passLayout.gaussianSplatColorBuffer.binding,
|
||||||
|
cachedGaussianSplat.color.shaderResourceView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatSHBuffer) {
|
||||||
|
if (cachedGaussianSplat.sh.shaderResourceView == nullptr ||
|
||||||
|
!passLayout.gaussianSplatSHBuffer.IsValid() ||
|
||||||
|
passLayout.gaussianSplatSHBuffer.set != setIndex) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.shView != cachedGaussianSplat.sh.shaderResourceView) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->Update(
|
||||||
|
passLayout.gaussianSplatSHBuffer.binding,
|
||||||
|
cachedGaussianSplat.sh.shaderResourceView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedDescriptorSet.materialVersion = materialVersion;
|
||||||
|
cachedDescriptorSet.positionsView = cachedGaussianSplat.positions.shaderResourceView;
|
||||||
|
cachedDescriptorSet.otherView = cachedGaussianSplat.other.shaderResourceView;
|
||||||
|
cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView;
|
||||||
|
cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView;
|
||||||
|
return &cachedDescriptorSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltinGaussianSplatPass::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet) {
|
||||||
|
if (descriptorSet.set != nullptr) {
|
||||||
|
descriptorSet.set->Shutdown();
|
||||||
|
delete descriptorSet.set;
|
||||||
|
descriptorSet.set = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptorSet.pool != nullptr) {
|
||||||
|
descriptorSet.pool->Shutdown();
|
||||||
|
delete descriptorSet.pool;
|
||||||
|
descriptorSet.pool = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuiltinGaussianSplatPass::DestroyPassResourceLayout(PassResourceLayout& passLayout) {
|
||||||
|
if (passLayout.pipelineLayout != nullptr) {
|
||||||
|
passLayout.pipelineLayout->Shutdown();
|
||||||
|
delete passLayout.pipelineLayout;
|
||||||
|
passLayout.pipelineLayout = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
passLayout.setLayouts.clear();
|
||||||
|
passLayout.firstDescriptorSet = 0;
|
||||||
|
passLayout.descriptorSetCount = 0;
|
||||||
|
passLayout.perObject = {};
|
||||||
|
passLayout.material = {};
|
||||||
|
passLayout.gaussianSplatPositionBuffer = {};
|
||||||
|
passLayout.gaussianSplatOtherBuffer = {};
|
||||||
|
passLayout.gaussianSplatColorBuffer = {};
|
||||||
|
passLayout.gaussianSplatSHBuffer = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSurface& surface,
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const VisibleGaussianSplatItem& visibleGaussianSplat) {
|
||||||
|
if (visibleGaussianSplat.gameObject == nullptr ||
|
||||||
|
visibleGaussianSplat.gaussianSplat == nullptr ||
|
||||||
|
!visibleGaussianSplat.gaussianSplat->IsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat =
|
||||||
|
m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat);
|
||||||
|
if (cachedGaussianSplat == nullptr ||
|
||||||
|
cachedGaussianSplat->positions.shaderResourceView == nullptr ||
|
||||||
|
cachedGaussianSplat->other.shaderResourceView == nullptr ||
|
||||||
|
cachedGaussianSplat->color.shaderResourceView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Resources::Material* material = ResolveGaussianSplatMaterial(visibleGaussianSplat);
|
||||||
|
if (material == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResolvedShaderPass resolvedShaderPass = ResolveGaussianSplatShaderPass(sceneData, material);
|
||||||
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassLayoutKey passLayoutKey = {};
|
||||||
|
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||||
|
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||||
|
|
||||||
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||||
|
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material);
|
||||||
|
if (passLayout == nullptr || pipelineState == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material);
|
||||||
|
if (!materialConstants.IsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHICommandList* commandList = context.commandList;
|
||||||
|
commandList->SetPipelineState(pipelineState);
|
||||||
|
|
||||||
|
const PerObjectConstants perObjectConstants = {
|
||||||
|
sceneData.cameraData.projection,
|
||||||
|
sceneData.cameraData.view,
|
||||||
|
visibleGaussianSplat.localToWorld.Transpose(),
|
||||||
|
Math::Vector4(sceneData.cameraData.worldRight, 0.0f),
|
||||||
|
Math::Vector4(sceneData.cameraData.worldUp, 0.0f)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (passLayout->descriptorSetCount > 0u) {
|
||||||
|
std::vector<RHI::RHIDescriptorSet*> descriptorSets(passLayout->descriptorSetCount, nullptr);
|
||||||
|
for (Core::uint32 descriptorOffset = 0u; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
|
||||||
|
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
|
||||||
|
if (setIndex >= passLayout->setLayouts.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||||
|
if (!(setLayout.usesPerObject ||
|
||||||
|
setLayout.usesMaterial ||
|
||||||
|
setLayout.usesGaussianSplatPositionBuffer ||
|
||||||
|
setLayout.usesGaussianSplatOtherBuffer ||
|
||||||
|
setLayout.usesGaussianSplatColorBuffer ||
|
||||||
|
setLayout.usesGaussianSplatSHBuffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::uint64 objectId =
|
||||||
|
setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u;
|
||||||
|
const Resources::Material* materialKey =
|
||||||
|
setLayout.usesMaterial ? material : nullptr;
|
||||||
|
const Resources::GaussianSplat* gaussianSplatKey =
|
||||||
|
(setLayout.usesGaussianSplatPositionBuffer ||
|
||||||
|
setLayout.usesGaussianSplatOtherBuffer ||
|
||||||
|
setLayout.usesGaussianSplatColorBuffer ||
|
||||||
|
setLayout.usesGaussianSplatSHBuffer)
|
||||||
|
? visibleGaussianSplat.gaussianSplat
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
|
||||||
|
passLayoutKey,
|
||||||
|
*passLayout,
|
||||||
|
setLayout,
|
||||||
|
setIndex,
|
||||||
|
objectId,
|
||||||
|
materialKey,
|
||||||
|
gaussianSplatKey,
|
||||||
|
materialConstants,
|
||||||
|
*cachedGaussianSplat);
|
||||||
|
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHIDescriptorSet* descriptorSet = cachedDescriptorSet->descriptorSet.set;
|
||||||
|
if (setLayout.usesPerObject) {
|
||||||
|
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptorSet->WriteConstant(
|
||||||
|
passLayout->perObject.binding,
|
||||||
|
&perObjectConstants,
|
||||||
|
sizeof(perObjectConstants));
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptorSets[descriptorOffset] = descriptorSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
commandList->SetGraphicsDescriptorSets(
|
||||||
|
passLayout->firstDescriptorSet,
|
||||||
|
passLayout->descriptorSetCount,
|
||||||
|
descriptorSets.data(),
|
||||||
|
passLayout->pipelineLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyDynamicRenderState(ResolveEffectiveRenderState(resolvedShaderPass.pass, material), *commandList);
|
||||||
|
commandList->Draw(6u, cachedGaussianSplat->splatCount, 0u, 0u);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Passes
|
||||||
|
} // namespace Rendering
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "Core/Asset/ResourceManager.h"
|
#include "Core/Asset/ResourceManager.h"
|
||||||
#include "RHI/RHICommandList.h"
|
#include "RHI/RHICommandList.h"
|
||||||
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
|
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
|
||||||
|
#include "Rendering/Passes/BuiltinGaussianSplatPass.h"
|
||||||
#include "Rendering/Materials/RenderMaterialResolve.h"
|
#include "Rendering/Materials/RenderMaterialResolve.h"
|
||||||
#include "Rendering/Passes/BuiltinVolumetricPass.h"
|
#include "Rendering/Passes/BuiltinVolumetricPass.h"
|
||||||
#include "Rendering/RenderSurface.h"
|
#include "Rendering/RenderSurface.h"
|
||||||
@@ -66,6 +67,7 @@ std::vector<RHI::RHIResourceView*> CollectSurfaceColorAttachments(const RenderSu
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
BuiltinForwardPipeline::BuiltinForwardPipeline() {
|
BuiltinForwardPipeline::BuiltinForwardPipeline() {
|
||||||
|
m_gaussianSplatPass = std::make_unique<Passes::BuiltinGaussianSplatPass>();
|
||||||
m_volumetricPass = std::make_unique<Passes::BuiltinVolumetricPass>();
|
m_volumetricPass = std::make_unique<Passes::BuiltinVolumetricPass>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,11 +111,16 @@ RHI::InputLayoutDesc BuiltinForwardPipeline::BuildInputLayout() {
|
|||||||
|
|
||||||
bool BuiltinForwardPipeline::Initialize(const RenderContext& context) {
|
bool BuiltinForwardPipeline::Initialize(const RenderContext& context) {
|
||||||
return EnsureInitialized(context) &&
|
return EnsureInitialized(context) &&
|
||||||
|
m_gaussianSplatPass != nullptr &&
|
||||||
|
m_gaussianSplatPass->Initialize(context) &&
|
||||||
m_volumetricPass != nullptr &&
|
m_volumetricPass != nullptr &&
|
||||||
m_volumetricPass->Initialize(context);
|
m_volumetricPass->Initialize(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuiltinForwardPipeline::Shutdown() {
|
void BuiltinForwardPipeline::Shutdown() {
|
||||||
|
if (m_gaussianSplatPass != nullptr) {
|
||||||
|
m_gaussianSplatPass->Shutdown();
|
||||||
|
}
|
||||||
if (m_volumetricPass != nullptr) {
|
if (m_volumetricPass != nullptr) {
|
||||||
m_volumetricPass->Shutdown();
|
m_volumetricPass->Shutdown();
|
||||||
}
|
}
|
||||||
@@ -128,6 +135,17 @@ bool BuiltinForwardPipeline::Render(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_volumetricPass != nullptr &&
|
||||||
|
!sceneData.visibleVolumes.empty() &&
|
||||||
|
!m_volumetricPass->PrepareVolumeResources(context, sceneData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_gaussianSplatPass != nullptr &&
|
||||||
|
!sceneData.visibleGaussianSplats.empty() &&
|
||||||
|
!m_gaussianSplatPass->PrepareGaussianSplatResources(context, sceneData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const RenderPassContext passContext = {
|
const RenderPassContext passContext = {
|
||||||
context,
|
context,
|
||||||
surface,
|
surface,
|
||||||
@@ -150,6 +168,9 @@ bool BuiltinForwardPipeline::Render(
|
|||||||
if (renderResult) {
|
if (renderResult) {
|
||||||
renderResult = ExecuteForwardSkyboxPass(passContext);
|
renderResult = ExecuteForwardSkyboxPass(passContext);
|
||||||
}
|
}
|
||||||
|
if (renderResult && m_gaussianSplatPass != nullptr) {
|
||||||
|
renderResult = m_gaussianSplatPass->Execute(passContext);
|
||||||
|
}
|
||||||
if (renderResult && m_volumetricPass != nullptr) {
|
if (renderResult && m_volumetricPass != nullptr) {
|
||||||
renderResult = m_volumetricPass->Execute(passContext);
|
renderResult = m_volumetricPass->Execute(passContext);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ constexpr const char* kBuiltinObjectIdOutlineShaderPath = "builtin://shaders/obj
|
|||||||
constexpr const char* kBuiltinSelectionMaskShaderPath = "builtin://shaders/selection-mask";
|
constexpr const char* kBuiltinSelectionMaskShaderPath = "builtin://shaders/selection-mask";
|
||||||
constexpr const char* kBuiltinSelectionOutlineShaderPath = "builtin://shaders/selection-outline";
|
constexpr const char* kBuiltinSelectionOutlineShaderPath = "builtin://shaders/selection-outline";
|
||||||
constexpr const char* kBuiltinSkyboxShaderPath = "builtin://shaders/skybox";
|
constexpr const char* kBuiltinSkyboxShaderPath = "builtin://shaders/skybox";
|
||||||
|
constexpr const char* kBuiltinGaussianSplatShaderPath = "builtin://shaders/gaussian-splat";
|
||||||
constexpr const char* kBuiltinVolumetricShaderPath = "builtin://shaders/volumetric";
|
constexpr const char* kBuiltinVolumetricShaderPath = "builtin://shaders/volumetric";
|
||||||
constexpr const char* kBuiltinColorScalePostProcessShaderPath =
|
constexpr const char* kBuiltinColorScalePostProcessShaderPath =
|
||||||
"builtin://shaders/color-scale-post-process";
|
"builtin://shaders/color-scale-post-process";
|
||||||
@@ -68,6 +69,8 @@ constexpr const char* kBuiltinSelectionOutlineShaderAssetRelativePath =
|
|||||||
"engine/assets/builtin/shaders/selection-outline.shader";
|
"engine/assets/builtin/shaders/selection-outline.shader";
|
||||||
constexpr const char* kBuiltinSkyboxShaderAssetRelativePath =
|
constexpr const char* kBuiltinSkyboxShaderAssetRelativePath =
|
||||||
"engine/assets/builtin/shaders/skybox.shader";
|
"engine/assets/builtin/shaders/skybox.shader";
|
||||||
|
constexpr const char* kBuiltinGaussianSplatShaderAssetRelativePath =
|
||||||
|
"engine/assets/builtin/shaders/gaussian-splat.shader";
|
||||||
constexpr const char* kBuiltinVolumetricShaderAssetRelativePath =
|
constexpr const char* kBuiltinVolumetricShaderAssetRelativePath =
|
||||||
"engine/assets/builtin/shaders/volumetric.shader";
|
"engine/assets/builtin/shaders/volumetric.shader";
|
||||||
constexpr const char* kBuiltinColorScalePostProcessShaderAssetRelativePath =
|
constexpr const char* kBuiltinColorScalePostProcessShaderAssetRelativePath =
|
||||||
@@ -164,6 +167,9 @@ const char* GetBuiltinShaderAssetRelativePath(const Containers::String& builtinS
|
|||||||
if (builtinShaderPath == Containers::String(kBuiltinSkyboxShaderPath)) {
|
if (builtinShaderPath == Containers::String(kBuiltinSkyboxShaderPath)) {
|
||||||
return kBuiltinSkyboxShaderAssetRelativePath;
|
return kBuiltinSkyboxShaderAssetRelativePath;
|
||||||
}
|
}
|
||||||
|
if (builtinShaderPath == Containers::String(kBuiltinGaussianSplatShaderPath)) {
|
||||||
|
return kBuiltinGaussianSplatShaderAssetRelativePath;
|
||||||
|
}
|
||||||
if (builtinShaderPath == Containers::String(kBuiltinVolumetricShaderPath)) {
|
if (builtinShaderPath == Containers::String(kBuiltinVolumetricShaderPath)) {
|
||||||
return kBuiltinVolumetricShaderAssetRelativePath;
|
return kBuiltinVolumetricShaderAssetRelativePath;
|
||||||
}
|
}
|
||||||
@@ -734,6 +740,10 @@ Shader* BuildBuiltinSkyboxShader(const Containers::String& path) {
|
|||||||
return TryLoadBuiltinShaderFromAsset(path);
|
return TryLoadBuiltinShaderFromAsset(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shader* BuildBuiltinGaussianSplatShader(const Containers::String& path) {
|
||||||
|
return TryLoadBuiltinShaderFromAsset(path);
|
||||||
|
}
|
||||||
|
|
||||||
Shader* BuildBuiltinVolumetricShader(const Containers::String& path) {
|
Shader* BuildBuiltinVolumetricShader(const Containers::String& path) {
|
||||||
return TryLoadBuiltinShaderFromAsset(path);
|
return TryLoadBuiltinShaderFromAsset(path);
|
||||||
}
|
}
|
||||||
@@ -852,6 +862,10 @@ bool TryGetBuiltinShaderPathByShaderName(
|
|||||||
outPath = GetBuiltinSkyboxShaderPath();
|
outPath = GetBuiltinSkyboxShaderPath();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (shaderName == "Builtin Gaussian Splat") {
|
||||||
|
outPath = GetBuiltinGaussianSplatShaderPath();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (shaderName == "Builtin Volumetric") {
|
if (shaderName == "Builtin Volumetric") {
|
||||||
outPath = GetBuiltinVolumetricShaderPath();
|
outPath = GetBuiltinVolumetricShaderPath();
|
||||||
return true;
|
return true;
|
||||||
@@ -939,6 +953,10 @@ Containers::String GetBuiltinSkyboxShaderPath() {
|
|||||||
return Containers::String(kBuiltinSkyboxShaderPath);
|
return Containers::String(kBuiltinSkyboxShaderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Containers::String GetBuiltinGaussianSplatShaderPath() {
|
||||||
|
return Containers::String(kBuiltinGaussianSplatShaderPath);
|
||||||
|
}
|
||||||
|
|
||||||
Containers::String GetBuiltinVolumetricShaderPath() {
|
Containers::String GetBuiltinVolumetricShaderPath() {
|
||||||
return Containers::String(kBuiltinVolumetricShaderPath);
|
return Containers::String(kBuiltinVolumetricShaderPath);
|
||||||
}
|
}
|
||||||
@@ -1057,6 +1075,8 @@ LoadResult CreateBuiltinShaderResource(const Containers::String& path) {
|
|||||||
shader = BuildBuiltinSelectionOutlineShader(path);
|
shader = BuildBuiltinSelectionOutlineShader(path);
|
||||||
} else if (path == GetBuiltinSkyboxShaderPath()) {
|
} else if (path == GetBuiltinSkyboxShaderPath()) {
|
||||||
shader = BuildBuiltinSkyboxShader(path);
|
shader = BuildBuiltinSkyboxShader(path);
|
||||||
|
} else if (path == GetBuiltinGaussianSplatShaderPath()) {
|
||||||
|
shader = BuildBuiltinGaussianSplatShader(path);
|
||||||
} else if (path == GetBuiltinVolumetricShaderPath()) {
|
} else if (path == GetBuiltinVolumetricShaderPath()) {
|
||||||
shader = BuildBuiltinVolumetricShader(path);
|
shader = BuildBuiltinVolumetricShader(path);
|
||||||
} else if (path == GetBuiltinColorScalePostProcessShaderPath()) {
|
} else if (path == GetBuiltinColorScalePostProcessShaderPath()) {
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "Rendering/Detail/ShaderVariantUtils.h"
|
#include "Rendering/Internal/ShaderVariantUtils.h"
|
||||||
|
|
||||||
#include <XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h>
|
#include <XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h>
|
||||||
|
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
|
||||||
#include <XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h>
|
#include <XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h>
|
||||||
#include <XCEngine/Rendering/Passes/BuiltinObjectIdPass.h>
|
#include <XCEngine/Rendering/Passes/BuiltinObjectIdPass.h>
|
||||||
#include <XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h>
|
#include <XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h>
|
||||||
#include <XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h>
|
#include <XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h>
|
||||||
|
#include <XCEngine/Rendering/RenderSurface.h>
|
||||||
#include <XCEngine/Resources/BuiltinResources.h>
|
#include <XCEngine/Resources/BuiltinResources.h>
|
||||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||||
#include <XCEngine/Resources/Shader/Shader.h>
|
#include <XCEngine/Resources/Shader/Shader.h>
|
||||||
@@ -87,6 +89,46 @@ void AppendDefaultBuiltinPassResources(ShaderPass& pass) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TestResourceView final : public XCEngine::RHI::RHIResourceView {
|
||||||
|
public:
|
||||||
|
TestResourceView(
|
||||||
|
XCEngine::RHI::ResourceViewType viewType,
|
||||||
|
XCEngine::RHI::ResourceViewDimension dimension,
|
||||||
|
XCEngine::RHI::Format format)
|
||||||
|
: m_viewType(viewType),
|
||||||
|
m_dimension(dimension),
|
||||||
|
m_format(format) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown() override {
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GetNativeHandle() override {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid() const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
XCEngine::RHI::ResourceViewType GetViewType() const override {
|
||||||
|
return m_viewType;
|
||||||
|
}
|
||||||
|
|
||||||
|
XCEngine::RHI::ResourceViewDimension GetDimension() const override {
|
||||||
|
return m_dimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
XCEngine::RHI::Format GetFormat() const override {
|
||||||
|
return m_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
XCEngine::RHI::ResourceViewType m_viewType;
|
||||||
|
XCEngine::RHI::ResourceViewDimension m_dimension;
|
||||||
|
XCEngine::RHI::Format m_format;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
|
TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
|
||||||
@@ -116,6 +158,162 @@ TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVert
|
|||||||
EXPECT_EQ(texcoord.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
|
EXPECT_EQ(texcoord.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurfacePipelineUtils_Test, ResolvesContiguousSurfaceAttachmentFormatsIntoPipelineDesc) {
|
||||||
|
TestResourceView color0(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::R16G16B16A16_Float);
|
||||||
|
TestResourceView color1(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::R8G8B8A8_UNorm);
|
||||||
|
TestResourceView depth(
|
||||||
|
ResourceViewType::DepthStencil,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::D32_Float);
|
||||||
|
|
||||||
|
RenderSurface surface(1280u, 720u);
|
||||||
|
surface.SetColorAttachments({ &color0, &color1 });
|
||||||
|
surface.SetDepthAttachment(&depth);
|
||||||
|
surface.SetSampleDesc(4u, 0u);
|
||||||
|
|
||||||
|
GraphicsPipelineDesc pipelineDesc = {};
|
||||||
|
XCEngine::Rendering::Internal::ApplySurfacePropertiesToGraphicsPipelineDesc(surface, pipelineDesc);
|
||||||
|
|
||||||
|
EXPECT_EQ(pipelineDesc.renderTargetCount, 2u);
|
||||||
|
EXPECT_EQ(static_cast<Format>(pipelineDesc.renderTargetFormats[0]), Format::R16G16B16A16_Float);
|
||||||
|
EXPECT_EQ(static_cast<Format>(pipelineDesc.renderTargetFormats[1]), Format::R8G8B8A8_UNorm);
|
||||||
|
EXPECT_EQ(static_cast<Format>(pipelineDesc.depthStencilFormat), Format::D32_Float);
|
||||||
|
EXPECT_EQ(pipelineDesc.sampleCount, 4u);
|
||||||
|
EXPECT_EQ(pipelineDesc.sampleQuality, 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurfacePipelineUtils_Test, PropagatesSampleQualityIntoPipelineDesc) {
|
||||||
|
TestResourceView color0(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::R8G8B8A8_UNorm);
|
||||||
|
|
||||||
|
RenderSurface surface(640u, 360u);
|
||||||
|
surface.SetColorAttachment(&color0);
|
||||||
|
surface.SetSampleDesc(4u, 3u);
|
||||||
|
|
||||||
|
GraphicsPipelineDesc pipelineDesc = {};
|
||||||
|
XCEngine::Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc(surface, pipelineDesc);
|
||||||
|
|
||||||
|
EXPECT_EQ(pipelineDesc.renderTargetCount, 1u);
|
||||||
|
EXPECT_EQ(pipelineDesc.sampleCount, 4u);
|
||||||
|
EXPECT_EQ(pipelineDesc.sampleQuality, 3u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurfacePipelineUtils_Test, StopsAtFirstNullColorAttachmentWhenResolvingFormats) {
|
||||||
|
TestResourceView color0(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::R16G16B16A16_Float);
|
||||||
|
TestResourceView color2(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::R8G8B8A8_UNorm);
|
||||||
|
|
||||||
|
RenderSurface surface(1280u, 720u);
|
||||||
|
surface.SetColorAttachments({ &color0, nullptr, &color2 });
|
||||||
|
|
||||||
|
const uint32_t colorAttachmentCount =
|
||||||
|
XCEngine::Rendering::Internal::ResolveSurfaceColorAttachmentCount(surface);
|
||||||
|
const auto colorFormats =
|
||||||
|
XCEngine::Rendering::Internal::ResolveSurfaceColorFormats(surface);
|
||||||
|
|
||||||
|
EXPECT_EQ(colorAttachmentCount, 1u);
|
||||||
|
EXPECT_EQ(static_cast<Format>(colorFormats[0]), Format::R16G16B16A16_Float);
|
||||||
|
EXPECT_EQ(static_cast<Format>(colorFormats[1]), Format::Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurfacePipelineUtils_Test, NormalizesInvalidSampleCountToSingleSample) {
|
||||||
|
RenderSurface surface(640u, 480u);
|
||||||
|
surface.SetSampleDesc(0u, 7u);
|
||||||
|
|
||||||
|
EXPECT_EQ(surface.GetSampleCount(), 1u);
|
||||||
|
EXPECT_EQ(surface.GetSampleQuality(), 0u);
|
||||||
|
EXPECT_EQ(XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurfacePipelineUtils_Test, AcceptsDepthStyleSurfaceWithDepthOnlyOutput) {
|
||||||
|
TestResourceView depth(
|
||||||
|
ResourceViewType::DepthStencil,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::D32_Float);
|
||||||
|
|
||||||
|
RenderSurface surface(640u, 480u);
|
||||||
|
surface.SetDepthAttachment(&depth);
|
||||||
|
surface.SetSampleDesc(1u, 0u);
|
||||||
|
|
||||||
|
EXPECT_TRUE(XCEngine::Rendering::Internal::IsDepthStyleCompatibleSurface(surface));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurfacePipelineUtils_Test, RejectsDepthStyleSurfaceWithoutKnownDepthFormat) {
|
||||||
|
TestResourceView color(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::R8G8B8A8_UNorm);
|
||||||
|
|
||||||
|
RenderSurface surface(640u, 480u);
|
||||||
|
surface.SetColorAttachment(&color);
|
||||||
|
|
||||||
|
EXPECT_FALSE(XCEngine::Rendering::Internal::IsDepthStyleCompatibleSurface(surface));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurfacePipelineUtils_Test, RejectsDepthStyleSurfaceWithUnknownSingleColorFormat) {
|
||||||
|
TestResourceView color(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::Unknown);
|
||||||
|
TestResourceView depth(
|
||||||
|
ResourceViewType::DepthStencil,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::D32_Float);
|
||||||
|
|
||||||
|
RenderSurface surface(640u, 480u);
|
||||||
|
surface.SetColorAttachment(&color);
|
||||||
|
surface.SetDepthAttachment(&depth);
|
||||||
|
|
||||||
|
EXPECT_FALSE(XCEngine::Rendering::Internal::IsDepthStyleCompatibleSurface(surface));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurfacePipelineUtils_Test, RejectsDepthStyleSurfaceWithMultipleColorAttachments) {
|
||||||
|
TestResourceView color0(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::R16G16B16A16_Float);
|
||||||
|
TestResourceView color1(
|
||||||
|
ResourceViewType::RenderTarget,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::R8G8B8A8_UNorm);
|
||||||
|
TestResourceView depth(
|
||||||
|
ResourceViewType::DepthStencil,
|
||||||
|
ResourceViewDimension::Texture2D,
|
||||||
|
Format::D32_Float);
|
||||||
|
|
||||||
|
RenderSurface surface(640u, 480u);
|
||||||
|
surface.SetColorAttachments({ &color0, &color1 });
|
||||||
|
surface.SetDepthAttachment(&depth);
|
||||||
|
|
||||||
|
EXPECT_FALSE(XCEngine::Rendering::Internal::IsDepthStyleCompatibleSurface(surface));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RenderSurface_Test, DefaultsDepthStateToDepthWriteAndSupportsOverrides) {
|
||||||
|
RenderSurface surface(640u, 480u);
|
||||||
|
|
||||||
|
EXPECT_EQ(surface.GetDepthStateBefore(), ResourceStates::DepthWrite);
|
||||||
|
EXPECT_EQ(surface.GetDepthStateAfter(), ResourceStates::DepthWrite);
|
||||||
|
|
||||||
|
surface.SetDepthStateBefore(ResourceStates::Common);
|
||||||
|
surface.SetDepthStateAfter(ResourceStates::PixelShaderResource);
|
||||||
|
|
||||||
|
EXPECT_EQ(surface.GetDepthStateBefore(), ResourceStates::Common);
|
||||||
|
EXPECT_EQ(surface.GetDepthStateAfter(), ResourceStates::PixelShaderResource);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(BuiltinForwardPipeline_Test, SplitsSceneItemsIntoOpaqueAndTransparentQueueRanges) {
|
TEST(BuiltinForwardPipeline_Test, SplitsSceneItemsIntoOpaqueAndTransparentQueueRanges) {
|
||||||
EXPECT_FALSE(IsTransparentRenderQueue(static_cast<int>(MaterialRenderQueue::Background)));
|
EXPECT_FALSE(IsTransparentRenderQueue(static_cast<int>(MaterialRenderQueue::Background)));
|
||||||
EXPECT_FALSE(IsTransparentRenderQueue(static_cast<int>(MaterialRenderQueue::Geometry)));
|
EXPECT_FALSE(IsTransparentRenderQueue(static_cast<int>(MaterialRenderQueue::Geometry)));
|
||||||
@@ -207,7 +405,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinUnlitShaderBuildsVulkanRuntimeSourceWit
|
|||||||
ASSERT_NE(fragmentVariant, nullptr);
|
ASSERT_NE(fragmentVariant, nullptr);
|
||||||
|
|
||||||
const std::string runtimeSource =
|
const std::string runtimeSource =
|
||||||
::XCEngine::Rendering::Detail::BuildRuntimeShaderSource(
|
::XCEngine::Rendering::Internal::BuildRuntimeShaderSource(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::Vulkan,
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
*fragmentVariant);
|
*fragmentVariant);
|
||||||
@@ -243,7 +441,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringUnlit
|
|||||||
ASSERT_NE(d3d12Fragment, nullptr);
|
ASSERT_NE(d3d12Fragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc d3d12CompileDesc = {};
|
ShaderCompileDesc d3d12CompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::D3D12,
|
XCEngine::Resources::ShaderBackend::D3D12,
|
||||||
*d3d12Fragment,
|
*d3d12Fragment,
|
||||||
@@ -271,7 +469,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringUnlit
|
|||||||
ASSERT_NE(vulkanFragment, nullptr);
|
ASSERT_NE(vulkanFragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc vulkanCompileDesc = {};
|
ShaderCompileDesc vulkanCompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::Vulkan,
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
*vulkanFragment,
|
*vulkanFragment,
|
||||||
@@ -314,7 +512,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringForwa
|
|||||||
ASSERT_NE(d3d12Fragment, nullptr);
|
ASSERT_NE(d3d12Fragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc d3d12CompileDesc = {};
|
ShaderCompileDesc d3d12CompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::D3D12,
|
XCEngine::Resources::ShaderBackend::D3D12,
|
||||||
*d3d12Fragment,
|
*d3d12Fragment,
|
||||||
@@ -365,7 +563,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringForwa
|
|||||||
ASSERT_NE(vulkanFragment, nullptr);
|
ASSERT_NE(vulkanFragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc vulkanCompileDesc = {};
|
ShaderCompileDesc vulkanCompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::Vulkan,
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
*vulkanFragment,
|
*vulkanFragment,
|
||||||
@@ -432,7 +630,7 @@ TEST(BuiltinForwardPipeline_Test, OpenGLRuntimeTranspilesForwardShadowVariantToL
|
|||||||
ASSERT_NE(openGLFragment, nullptr);
|
ASSERT_NE(openGLFragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc compileDesc = {};
|
ShaderCompileDesc compileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::OpenGL,
|
XCEngine::Resources::ShaderBackend::OpenGL,
|
||||||
*openGLFragment,
|
*openGLFragment,
|
||||||
@@ -509,7 +707,7 @@ TEST(BuiltinDepthStylePass_Test, OpenGLRuntimeTranspilesDepthOnlyAlphaVariantWit
|
|||||||
const ShaderStageVariant& variant,
|
const ShaderStageVariant& variant,
|
||||||
std::string& glslSource) {
|
std::string& glslSource) {
|
||||||
ShaderCompileDesc compileDesc = {};
|
ShaderCompileDesc compileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
targetPass,
|
targetPass,
|
||||||
XCEngine::Resources::ShaderBackend::OpenGL,
|
XCEngine::Resources::ShaderBackend::OpenGL,
|
||||||
variant,
|
variant,
|
||||||
@@ -573,7 +771,7 @@ TEST(BuiltinDepthStylePass_Test, OpenGLRuntimeTranspilesShadowCasterAlphaVariant
|
|||||||
const ShaderStageVariant& variant,
|
const ShaderStageVariant& variant,
|
||||||
std::string& glslSource) {
|
std::string& glslSource) {
|
||||||
ShaderCompileDesc compileDesc = {};
|
ShaderCompileDesc compileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
targetPass,
|
targetPass,
|
||||||
XCEngine::Resources::ShaderBackend::OpenGL,
|
XCEngine::Resources::ShaderBackend::OpenGL,
|
||||||
variant,
|
variant,
|
||||||
@@ -676,6 +874,219 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
delete shader;
|
delete shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContract) {
|
||||||
|
ShaderLoader loader;
|
||||||
|
LoadResult result = loader.Load(GetBuiltinGaussianSplatShaderPath());
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_NE(result.resource, nullptr);
|
||||||
|
|
||||||
|
Shader* shader = static_cast<Shader*>(result.resource);
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
|
||||||
|
const ShaderPass* pass = shader->FindPass("GaussianSplat");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
EXPECT_EQ(pass->resources.Size(), 5u);
|
||||||
|
EXPECT_TRUE(pass->hasFixedFunctionState);
|
||||||
|
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::None);
|
||||||
|
EXPECT_FALSE(pass->fixedFunctionState.depthWriteEnable);
|
||||||
|
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
|
||||||
|
EXPECT_TRUE(pass->fixedFunctionState.blendEnable);
|
||||||
|
|
||||||
|
const ShaderPropertyDesc* pointScale = shader->FindProperty("_PointScale");
|
||||||
|
ASSERT_NE(pointScale, nullptr);
|
||||||
|
EXPECT_EQ(pointScale->type, ShaderPropertyType::Float);
|
||||||
|
|
||||||
|
const ShaderPropertyDesc* opacityScale = shader->FindProperty("_OpacityScale");
|
||||||
|
ASSERT_NE(opacityScale, nullptr);
|
||||||
|
EXPECT_EQ(opacityScale->type, ShaderPropertyType::Float);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* positions =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplat", "GaussianSplatPositions");
|
||||||
|
ASSERT_NE(positions, nullptr);
|
||||||
|
EXPECT_EQ(positions->type, ShaderResourceType::StructuredBuffer);
|
||||||
|
EXPECT_EQ(positions->set, 2u);
|
||||||
|
EXPECT_EQ(positions->binding, 0u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*positions),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatPositionBuffer);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* other =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplat", "GaussianSplatOther");
|
||||||
|
ASSERT_NE(other, nullptr);
|
||||||
|
EXPECT_EQ(other->type, ShaderResourceType::StructuredBuffer);
|
||||||
|
EXPECT_EQ(other->set, 2u);
|
||||||
|
EXPECT_EQ(other->binding, 1u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*other),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatOtherBuffer);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* color =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplat", "GaussianSplatColor");
|
||||||
|
ASSERT_NE(color, nullptr);
|
||||||
|
EXPECT_EQ(color->type, ShaderResourceType::StructuredBuffer);
|
||||||
|
EXPECT_EQ(color->set, 2u);
|
||||||
|
EXPECT_EQ(color->binding, 2u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*color),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatColorBuffer);
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoadedGaussianSplatShaderContract) {
|
||||||
|
ShaderLoader loader;
|
||||||
|
LoadResult result = loader.Load(GetBuiltinGaussianSplatShaderPath());
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_NE(result.resource, nullptr);
|
||||||
|
|
||||||
|
Shader* shader = static_cast<Shader*>(result.resource);
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
|
||||||
|
const ShaderPass* pass = shader->FindPass("GaussianSplat");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
|
||||||
|
BuiltinPassResourceBindingPlan plan = {};
|
||||||
|
String error;
|
||||||
|
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
|
||||||
|
ASSERT_EQ(plan.bindings.Size(), 5u);
|
||||||
|
EXPECT_TRUE(plan.perObject.IsValid());
|
||||||
|
EXPECT_TRUE(plan.material.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatPositionBuffer.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatOtherBuffer.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatColorBuffer.IsValid());
|
||||||
|
EXPECT_FALSE(plan.gaussianSplatSHBuffer.IsValid());
|
||||||
|
EXPECT_EQ(plan.perObject.set, 0u);
|
||||||
|
EXPECT_EQ(plan.material.set, 1u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatPositionBuffer.set, 2u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatPositionBuffer.binding, 0u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatOtherBuffer.set, 2u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatOtherBuffer.binding, 1u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatColorBuffer.set, 2u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatColorBuffer.binding, 2u);
|
||||||
|
EXPECT_EQ(plan.firstDescriptorSet, 0u);
|
||||||
|
EXPECT_EQ(plan.descriptorSetCount, 3u);
|
||||||
|
|
||||||
|
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
||||||
|
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
|
||||||
|
ASSERT_EQ(setLayouts.size(), 3u);
|
||||||
|
EXPECT_TRUE(setLayouts[0].usesPerObject);
|
||||||
|
EXPECT_TRUE(setLayouts[1].usesMaterial);
|
||||||
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatPositionBuffer);
|
||||||
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatOtherBuffer);
|
||||||
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatColorBuffer);
|
||||||
|
ASSERT_EQ(setLayouts[2].bindings.size(), 3u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[2].bindings[0].type),
|
||||||
|
DescriptorType::SRV);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[2].bindings[1].type),
|
||||||
|
DescriptorType::SRV);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[2].bindings[2].type),
|
||||||
|
DescriptorType::SRV);
|
||||||
|
EXPECT_EQ(
|
||||||
|
setLayouts[2].bindings[0].resourceDimension,
|
||||||
|
ResourceViewDimension::StructuredBuffer);
|
||||||
|
EXPECT_EQ(
|
||||||
|
setLayouts[2].bindings[1].resourceDimension,
|
||||||
|
ResourceViewDimension::StructuredBuffer);
|
||||||
|
EXPECT_EQ(
|
||||||
|
setLayouts[2].bindings[2].resourceDimension,
|
||||||
|
ResourceViewDimension::StructuredBuffer);
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringGaussianSplatBindingsToDescriptorSpaces) {
|
||||||
|
ShaderLoader loader;
|
||||||
|
LoadResult result = loader.Load(GetBuiltinGaussianSplatShaderPath());
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_NE(result.resource, nullptr);
|
||||||
|
|
||||||
|
Shader* shader = static_cast<Shader*>(result.resource);
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
|
||||||
|
const ShaderPass* pass = shader->FindPass("GaussianSplat");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
|
||||||
|
const ShaderStageVariant* d3d12Vertex = shader->FindVariant(
|
||||||
|
"GaussianSplat",
|
||||||
|
XCEngine::Resources::ShaderType::Vertex,
|
||||||
|
XCEngine::Resources::ShaderBackend::D3D12);
|
||||||
|
ASSERT_NE(d3d12Vertex, nullptr);
|
||||||
|
|
||||||
|
ShaderCompileDesc d3d12CompileDesc = {};
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
*pass,
|
||||||
|
XCEngine::Resources::ShaderBackend::D3D12,
|
||||||
|
*d3d12Vertex,
|
||||||
|
d3d12CompileDesc);
|
||||||
|
const std::string d3d12Source(
|
||||||
|
reinterpret_cast<const char*>(d3d12CompileDesc.source.data()),
|
||||||
|
d3d12CompileDesc.source.size());
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
d3d12Source,
|
||||||
|
"cbuffer PerObjectConstants",
|
||||||
|
"register(b0)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
d3d12Source,
|
||||||
|
"cbuffer MaterialConstants",
|
||||||
|
"register(b1)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
d3d12Source,
|
||||||
|
"StructuredBuffer<float3> GaussianSplatPositions",
|
||||||
|
"register(t0)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
d3d12Source,
|
||||||
|
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
|
||||||
|
"register(t1)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
d3d12Source,
|
||||||
|
"StructuredBuffer<float4> GaussianSplatColor",
|
||||||
|
"register(t2)"));
|
||||||
|
EXPECT_EQ(d3d12Source.find("space0"), std::string::npos);
|
||||||
|
EXPECT_EQ(d3d12Source.find("space1"), std::string::npos);
|
||||||
|
EXPECT_EQ(d3d12Source.find("space2"), std::string::npos);
|
||||||
|
|
||||||
|
const ShaderStageVariant* vulkanVertex = shader->FindVariant(
|
||||||
|
"GaussianSplat",
|
||||||
|
XCEngine::Resources::ShaderType::Vertex,
|
||||||
|
XCEngine::Resources::ShaderBackend::Vulkan);
|
||||||
|
ASSERT_NE(vulkanVertex, nullptr);
|
||||||
|
|
||||||
|
ShaderCompileDesc vulkanCompileDesc = {};
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
*pass,
|
||||||
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
|
*vulkanVertex,
|
||||||
|
vulkanCompileDesc);
|
||||||
|
const std::string vulkanSource(
|
||||||
|
reinterpret_cast<const char*>(vulkanCompileDesc.source.data()),
|
||||||
|
vulkanCompileDesc.source.size());
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
vulkanSource,
|
||||||
|
"cbuffer PerObjectConstants",
|
||||||
|
"register(b0, space0)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
vulkanSource,
|
||||||
|
"cbuffer MaterialConstants",
|
||||||
|
"register(b0, space1)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
vulkanSource,
|
||||||
|
"StructuredBuffer<float3> GaussianSplatPositions",
|
||||||
|
"register(t0, space2)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
vulkanSource,
|
||||||
|
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
|
||||||
|
"register(t1, space2)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
vulkanSource,
|
||||||
|
"StructuredBuffer<float4> GaussianSplatColor",
|
||||||
|
"register(t2, space2)"));
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
TEST(BuiltinForwardPipeline_Test, BuiltinVolumetricShaderUsesAuthoringContract) {
|
TEST(BuiltinForwardPipeline_Test, BuiltinVolumetricShaderUsesAuthoringContract) {
|
||||||
ShaderLoader loader;
|
ShaderLoader loader;
|
||||||
LoadResult result = loader.Load(GetBuiltinVolumetricShaderPath());
|
LoadResult result = loader.Load(GetBuiltinVolumetricShaderPath());
|
||||||
@@ -893,7 +1304,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringFinal
|
|||||||
ASSERT_NE(d3d12Fragment, nullptr);
|
ASSERT_NE(d3d12Fragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc d3d12CompileDesc = {};
|
ShaderCompileDesc d3d12CompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::D3D12,
|
XCEngine::Resources::ShaderBackend::D3D12,
|
||||||
*d3d12Fragment,
|
*d3d12Fragment,
|
||||||
@@ -923,7 +1334,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringFinal
|
|||||||
ASSERT_NE(vulkanFragment, nullptr);
|
ASSERT_NE(vulkanFragment, nullptr);
|
||||||
|
|
||||||
const std::string runtimeSource =
|
const std::string runtimeSource =
|
||||||
::XCEngine::Rendering::Detail::BuildRuntimeShaderSource(
|
::XCEngine::Rendering::Internal::BuildRuntimeShaderSource(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::Vulkan,
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
*vulkanFragment);
|
*vulkanFragment);
|
||||||
@@ -941,7 +1352,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringFinal
|
|||||||
"register(s0, space2)"));
|
"register(s0, space2)"));
|
||||||
|
|
||||||
ShaderCompileDesc vulkanCompileDesc = {};
|
ShaderCompileDesc vulkanCompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::Vulkan,
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
*vulkanFragment,
|
*vulkanFragment,
|
||||||
@@ -974,7 +1385,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringVolum
|
|||||||
ASSERT_NE(d3d12Fragment, nullptr);
|
ASSERT_NE(d3d12Fragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc d3d12CompileDesc = {};
|
ShaderCompileDesc d3d12CompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::D3D12,
|
XCEngine::Resources::ShaderBackend::D3D12,
|
||||||
*d3d12Fragment,
|
*d3d12Fragment,
|
||||||
@@ -1006,7 +1417,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringVolum
|
|||||||
ASSERT_NE(vulkanFragment, nullptr);
|
ASSERT_NE(vulkanFragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc vulkanCompileDesc = {};
|
ShaderCompileDesc vulkanCompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::Vulkan,
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
*vulkanFragment,
|
*vulkanFragment,
|
||||||
@@ -1053,7 +1464,7 @@ TEST(BuiltinForwardPipeline_Test, OpenGLRuntimeTranspilesFinalColorVariantToComb
|
|||||||
ASSERT_NE(openGLFragment, nullptr);
|
ASSERT_NE(openGLFragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc compileDesc = {};
|
ShaderCompileDesc compileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::OpenGL,
|
XCEngine::Resources::ShaderBackend::OpenGL,
|
||||||
*openGLFragment,
|
*openGLFragment,
|
||||||
@@ -1098,7 +1509,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringColor
|
|||||||
ASSERT_NE(d3d12Fragment, nullptr);
|
ASSERT_NE(d3d12Fragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc d3d12CompileDesc = {};
|
ShaderCompileDesc d3d12CompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::D3D12,
|
XCEngine::Resources::ShaderBackend::D3D12,
|
||||||
*d3d12Fragment,
|
*d3d12Fragment,
|
||||||
@@ -1128,7 +1539,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringColor
|
|||||||
ASSERT_NE(vulkanFragment, nullptr);
|
ASSERT_NE(vulkanFragment, nullptr);
|
||||||
|
|
||||||
const std::string runtimeSource =
|
const std::string runtimeSource =
|
||||||
::XCEngine::Rendering::Detail::BuildRuntimeShaderSource(
|
::XCEngine::Rendering::Internal::BuildRuntimeShaderSource(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::Vulkan,
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
*vulkanFragment);
|
*vulkanFragment);
|
||||||
@@ -1146,7 +1557,7 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringColor
|
|||||||
"register(s0, space2)"));
|
"register(s0, space2)"));
|
||||||
|
|
||||||
ShaderCompileDesc vulkanCompileDesc = {};
|
ShaderCompileDesc vulkanCompileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::Vulkan,
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
*vulkanFragment,
|
*vulkanFragment,
|
||||||
@@ -1179,7 +1590,7 @@ TEST(BuiltinForwardPipeline_Test, OpenGLRuntimeTranspilesColorScaleVariantToComb
|
|||||||
ASSERT_NE(openGLFragment, nullptr);
|
ASSERT_NE(openGLFragment, nullptr);
|
||||||
|
|
||||||
ShaderCompileDesc compileDesc = {};
|
ShaderCompileDesc compileDesc = {};
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
*pass,
|
*pass,
|
||||||
XCEngine::Resources::ShaderBackend::OpenGL,
|
XCEngine::Resources::ShaderBackend::OpenGL,
|
||||||
*openGLFragment,
|
*openGLFragment,
|
||||||
|
|||||||
Reference in New Issue
Block a user