Add gaussian splat compute shader contracts
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
Shader "Builtin Gaussian Splat Utilities"
|
||||||
|
{
|
||||||
|
HLSLINCLUDE
|
||||||
|
uint FloatToSortableUint(float value)
|
||||||
|
{
|
||||||
|
const uint rawValue = asuint(value);
|
||||||
|
const uint mask = (rawValue & 0x80000000u) != 0u ? 0xffffffffu : 0x80000000u;
|
||||||
|
return rawValue ^ mask;
|
||||||
|
}
|
||||||
|
ENDHLSL
|
||||||
|
|
||||||
|
SubShader
|
||||||
|
{
|
||||||
|
Pass
|
||||||
|
{
|
||||||
|
Name "GaussianSplatPrepareOrder"
|
||||||
|
HLSLPROGRAM
|
||||||
|
#pragma target 4.5
|
||||||
|
#pragma compute GaussianSplatPrepareOrderCS
|
||||||
|
|
||||||
|
cbuffer PerObjectConstants
|
||||||
|
{
|
||||||
|
float4x4 gProjectionMatrix;
|
||||||
|
float4x4 gViewMatrix;
|
||||||
|
float4x4 gModelMatrix;
|
||||||
|
float4 gCameraRight;
|
||||||
|
float4 gCameraUp;
|
||||||
|
};
|
||||||
|
|
||||||
|
StructuredBuffer<float3> GaussianSplatPositions;
|
||||||
|
RWStructuredBuffer<uint> GaussianSplatSortDistances;
|
||||||
|
RWStructuredBuffer<uint> GaussianSplatOrderBuffer;
|
||||||
|
|
||||||
|
[numthreads(64, 1, 1)]
|
||||||
|
void GaussianSplatPrepareOrderCS(uint3 dispatchThreadId : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
uint splatCount = 0u;
|
||||||
|
GaussianSplatOrderBuffer.GetDimensions(splatCount);
|
||||||
|
|
||||||
|
const uint index = dispatchThreadId.x;
|
||||||
|
if (index >= splatCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GaussianSplatOrderBuffer[index] = index;
|
||||||
|
|
||||||
|
const float3 localCenter = GaussianSplatPositions[index];
|
||||||
|
const float3 viewCenter =
|
||||||
|
mul(gViewMatrix, mul(gModelMatrix, float4(localCenter, 1.0))).xyz;
|
||||||
|
GaussianSplatSortDistances[index] = FloatToSortableUint(viewCenter.z);
|
||||||
|
}
|
||||||
|
ENDHLSL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,12 @@ inline bool TryBuildBuiltinPassResourceBindingPlan(
|
|||||||
case BuiltinPassResourceSemantic::VolumeField:
|
case BuiltinPassResourceSemantic::VolumeField:
|
||||||
location = &outPlan.volumeField;
|
location = &outPlan.volumeField;
|
||||||
break;
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatSortDistanceBuffer:
|
||||||
|
location = &outPlan.gaussianSplatSortDistanceBuffer;
|
||||||
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatOrderBuffer:
|
||||||
|
location = &outPlan.gaussianSplatOrderBuffer;
|
||||||
|
break;
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
||||||
location = &outPlan.gaussianSplatPositionBuffer;
|
location = &outPlan.gaussianSplatPositionBuffer;
|
||||||
break;
|
break;
|
||||||
@@ -74,6 +80,9 @@ inline bool TryBuildBuiltinPassResourceBindingPlan(
|
|||||||
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
location = &outPlan.gaussianSplatSHBuffer;
|
location = &outPlan.gaussianSplatSHBuffer;
|
||||||
break;
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
||||||
|
location = &outPlan.gaussianSplatViewDataBuffer;
|
||||||
|
break;
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
location = &outPlan.baseColorTexture;
|
location = &outPlan.baseColorTexture;
|
||||||
break;
|
break;
|
||||||
@@ -600,6 +609,12 @@ inline bool TryBuildBuiltinPassSetLayouts(
|
|||||||
case BuiltinPassResourceSemantic::VolumeField:
|
case BuiltinPassResourceSemantic::VolumeField:
|
||||||
setLayout.usesVolumeField = true;
|
setLayout.usesVolumeField = true;
|
||||||
break;
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatSortDistanceBuffer:
|
||||||
|
setLayout.usesGaussianSplatSortDistanceBuffer = true;
|
||||||
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatOrderBuffer:
|
||||||
|
setLayout.usesGaussianSplatOrderBuffer = true;
|
||||||
|
break;
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
||||||
setLayout.usesGaussianSplatPositionBuffer = true;
|
setLayout.usesGaussianSplatPositionBuffer = true;
|
||||||
break;
|
break;
|
||||||
@@ -612,6 +627,9 @@ inline bool TryBuildBuiltinPassSetLayouts(
|
|||||||
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
setLayout.usesGaussianSplatSHBuffer = true;
|
setLayout.usesGaussianSplatSHBuffer = true;
|
||||||
break;
|
break;
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
||||||
|
setLayout.usesGaussianSplatViewDataBuffer = true;
|
||||||
|
break;
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
setLayout.usesTexture = true;
|
setLayout.usesTexture = true;
|
||||||
setLayout.usesBaseColorTexture = true;
|
setLayout.usesBaseColorTexture = true;
|
||||||
|
|||||||
@@ -147,6 +147,18 @@ inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic(
|
|||||||
return BuiltinPassResourceSemantic::VolumeField;
|
return BuiltinPassResourceSemantic::VolumeField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (semantic == Containers::String("gaussiansplatsortdistancebuffer") ||
|
||||||
|
semantic == Containers::String("gaussiansplatsortdistances") ||
|
||||||
|
semantic == Containers::String("splatsortdistances")) {
|
||||||
|
return BuiltinPassResourceSemantic::GaussianSplatSortDistanceBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (semantic == Containers::String("gaussiansplatorderbuffer") ||
|
||||||
|
semantic == Containers::String("gaussiansplatorder") ||
|
||||||
|
semantic == Containers::String("orderbuffer")) {
|
||||||
|
return BuiltinPassResourceSemantic::GaussianSplatOrderBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
if (semantic == Containers::String("gaussiansplatpositionbuffer") ||
|
if (semantic == Containers::String("gaussiansplatpositionbuffer") ||
|
||||||
semantic == Containers::String("gaussiansplatpositions") ||
|
semantic == Containers::String("gaussiansplatpositions") ||
|
||||||
semantic == Containers::String("splatpositions")) {
|
semantic == Containers::String("splatpositions")) {
|
||||||
@@ -171,6 +183,12 @@ inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic(
|
|||||||
return BuiltinPassResourceSemantic::GaussianSplatSHBuffer;
|
return BuiltinPassResourceSemantic::GaussianSplatSHBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (semantic == Containers::String("gaussiansplatviewdatabuffer") ||
|
||||||
|
semantic == Containers::String("gaussiansplatviewdata") ||
|
||||||
|
semantic == Containers::String("splatviewdata")) {
|
||||||
|
return BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
if (semantic == Containers::String("basecolortexture") ||
|
if (semantic == Containers::String("basecolortexture") ||
|
||||||
semantic == Containers::String("maintex")) {
|
semantic == Containers::String("maintex")) {
|
||||||
return BuiltinPassResourceSemantic::BaseColorTexture;
|
return BuiltinPassResourceSemantic::BaseColorTexture;
|
||||||
@@ -233,6 +251,10 @@ inline const char* BuiltinPassResourceSemanticToString(BuiltinPassResourceSemant
|
|||||||
return "PassConstants";
|
return "PassConstants";
|
||||||
case BuiltinPassResourceSemantic::VolumeField:
|
case BuiltinPassResourceSemantic::VolumeField:
|
||||||
return "VolumeField";
|
return "VolumeField";
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatSortDistanceBuffer:
|
||||||
|
return "GaussianSplatSortDistanceBuffer";
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatOrderBuffer:
|
||||||
|
return "GaussianSplatOrderBuffer";
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
||||||
return "GaussianSplatPositionBuffer";
|
return "GaussianSplatPositionBuffer";
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
|
||||||
@@ -241,6 +263,8 @@ inline const char* BuiltinPassResourceSemanticToString(BuiltinPassResourceSemant
|
|||||||
return "GaussianSplatColorBuffer";
|
return "GaussianSplatColorBuffer";
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
return "GaussianSplatSHBuffer";
|
return "GaussianSplatSHBuffer";
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
||||||
|
return "GaussianSplatViewDataBuffer";
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
return "BaseColorTexture";
|
return "BaseColorTexture";
|
||||||
case BuiltinPassResourceSemantic::SourceColorTexture:
|
case BuiltinPassResourceSemantic::SourceColorTexture:
|
||||||
@@ -329,12 +353,17 @@ 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::GaussianSplatSortDistanceBuffer:
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatOrderBuffer:
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
|
||||||
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
|
||||||
|
case BuiltinPassResourceSemantic::GaussianSplatViewDataBuffer:
|
||||||
return type == Resources::ShaderResourceType::StructuredBuffer ||
|
return type == Resources::ShaderResourceType::StructuredBuffer ||
|
||||||
type == Resources::ShaderResourceType::RawBuffer;
|
type == Resources::ShaderResourceType::RawBuffer ||
|
||||||
|
type == Resources::ShaderResourceType::RWStructuredBuffer ||
|
||||||
|
type == Resources::ShaderResourceType::RWRawBuffer;
|
||||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||||
case BuiltinPassResourceSemantic::SourceColorTexture:
|
case BuiltinPassResourceSemantic::SourceColorTexture:
|
||||||
case BuiltinPassResourceSemantic::SkyboxPanoramicTexture:
|
case BuiltinPassResourceSemantic::SkyboxPanoramicTexture:
|
||||||
|
|||||||
@@ -46,11 +46,13 @@ enum class BuiltinPassResourceSemantic : Core::uint8 {
|
|||||||
Environment,
|
Environment,
|
||||||
PassConstants,
|
PassConstants,
|
||||||
VolumeField,
|
VolumeField,
|
||||||
|
GaussianSplatSortDistanceBuffer,
|
||||||
GaussianSplatOrderBuffer,
|
GaussianSplatOrderBuffer,
|
||||||
GaussianSplatPositionBuffer,
|
GaussianSplatPositionBuffer,
|
||||||
GaussianSplatOtherBuffer,
|
GaussianSplatOtherBuffer,
|
||||||
GaussianSplatColorBuffer,
|
GaussianSplatColorBuffer,
|
||||||
GaussianSplatSHBuffer,
|
GaussianSplatSHBuffer,
|
||||||
|
GaussianSplatViewDataBuffer,
|
||||||
BaseColorTexture,
|
BaseColorTexture,
|
||||||
SourceColorTexture,
|
SourceColorTexture,
|
||||||
SkyboxPanoramicTexture,
|
SkyboxPanoramicTexture,
|
||||||
@@ -84,11 +86,13 @@ struct BuiltinPassResourceBindingPlan {
|
|||||||
PassResourceBindingLocation shadowReceiver = {};
|
PassResourceBindingLocation shadowReceiver = {};
|
||||||
PassResourceBindingLocation environment = {};
|
PassResourceBindingLocation environment = {};
|
||||||
PassResourceBindingLocation passConstants = {};
|
PassResourceBindingLocation passConstants = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatSortDistanceBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatOrderBuffer = {};
|
PassResourceBindingLocation gaussianSplatOrderBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
|
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
|
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatViewDataBuffer = {};
|
||||||
PassResourceBindingLocation baseColorTexture = {};
|
PassResourceBindingLocation baseColorTexture = {};
|
||||||
PassResourceBindingLocation sourceColorTexture = {};
|
PassResourceBindingLocation sourceColorTexture = {};
|
||||||
PassResourceBindingLocation skyboxPanoramicTexture = {};
|
PassResourceBindingLocation skyboxPanoramicTexture = {};
|
||||||
@@ -122,11 +126,13 @@ struct BuiltinPassSetLayoutMetadata {
|
|||||||
bool usesPassConstants = false;
|
bool usesPassConstants = false;
|
||||||
bool usesMaterialBuffers = false;
|
bool usesMaterialBuffers = false;
|
||||||
bool usesVolumeField = false;
|
bool usesVolumeField = false;
|
||||||
|
bool usesGaussianSplatSortDistanceBuffer = false;
|
||||||
bool usesGaussianSplatOrderBuffer = false;
|
bool usesGaussianSplatOrderBuffer = false;
|
||||||
bool usesGaussianSplatPositionBuffer = false;
|
bool usesGaussianSplatPositionBuffer = false;
|
||||||
bool usesGaussianSplatOtherBuffer = false;
|
bool usesGaussianSplatOtherBuffer = false;
|
||||||
bool usesGaussianSplatColorBuffer = false;
|
bool usesGaussianSplatColorBuffer = false;
|
||||||
bool usesGaussianSplatSHBuffer = false;
|
bool usesGaussianSplatSHBuffer = false;
|
||||||
|
bool usesGaussianSplatViewDataBuffer = false;
|
||||||
bool usesTexture = false;
|
bool usesTexture = false;
|
||||||
bool usesBaseColorTexture = false;
|
bool usesBaseColorTexture = false;
|
||||||
bool usesSourceColorTexture = false;
|
bool usesSourceColorTexture = false;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ Containers::String GetBuiltinSelectionMaskShaderPath();
|
|||||||
Containers::String GetBuiltinSelectionOutlineShaderPath();
|
Containers::String GetBuiltinSelectionOutlineShaderPath();
|
||||||
Containers::String GetBuiltinSkyboxShaderPath();
|
Containers::String GetBuiltinSkyboxShaderPath();
|
||||||
Containers::String GetBuiltinGaussianSplatShaderPath();
|
Containers::String GetBuiltinGaussianSplatShaderPath();
|
||||||
|
Containers::String GetBuiltinGaussianSplatUtilitiesShaderPath();
|
||||||
Containers::String GetBuiltinVolumetricShaderPath();
|
Containers::String GetBuiltinVolumetricShaderPath();
|
||||||
Containers::String GetBuiltinColorScalePostProcessShaderPath();
|
Containers::String GetBuiltinColorScalePostProcessShaderPath();
|
||||||
Containers::String GetBuiltinFinalColorShaderPath();
|
Containers::String GetBuiltinFinalColorShaderPath();
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ constexpr const char* kBuiltinSelectionMaskShaderPath = "builtin://shaders/selec
|
|||||||
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* kBuiltinGaussianSplatShaderPath = "builtin://shaders/gaussian-splat";
|
||||||
|
constexpr const char* kBuiltinGaussianSplatUtilitiesShaderPath =
|
||||||
|
"builtin://shaders/gaussian-splat-utilities";
|
||||||
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";
|
||||||
@@ -71,6 +73,8 @@ constexpr const char* kBuiltinSkyboxShaderAssetRelativePath =
|
|||||||
"engine/assets/builtin/shaders/skybox.shader";
|
"engine/assets/builtin/shaders/skybox.shader";
|
||||||
constexpr const char* kBuiltinGaussianSplatShaderAssetRelativePath =
|
constexpr const char* kBuiltinGaussianSplatShaderAssetRelativePath =
|
||||||
"engine/assets/builtin/shaders/gaussian-splat.shader";
|
"engine/assets/builtin/shaders/gaussian-splat.shader";
|
||||||
|
constexpr const char* kBuiltinGaussianSplatUtilitiesShaderAssetRelativePath =
|
||||||
|
"engine/assets/builtin/shaders/gaussian-splat-utilities.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 =
|
||||||
@@ -170,6 +174,9 @@ const char* GetBuiltinShaderAssetRelativePath(const Containers::String& builtinS
|
|||||||
if (builtinShaderPath == Containers::String(kBuiltinGaussianSplatShaderPath)) {
|
if (builtinShaderPath == Containers::String(kBuiltinGaussianSplatShaderPath)) {
|
||||||
return kBuiltinGaussianSplatShaderAssetRelativePath;
|
return kBuiltinGaussianSplatShaderAssetRelativePath;
|
||||||
}
|
}
|
||||||
|
if (builtinShaderPath == Containers::String(kBuiltinGaussianSplatUtilitiesShaderPath)) {
|
||||||
|
return kBuiltinGaussianSplatUtilitiesShaderAssetRelativePath;
|
||||||
|
}
|
||||||
if (builtinShaderPath == Containers::String(kBuiltinVolumetricShaderPath)) {
|
if (builtinShaderPath == Containers::String(kBuiltinVolumetricShaderPath)) {
|
||||||
return kBuiltinVolumetricShaderAssetRelativePath;
|
return kBuiltinVolumetricShaderAssetRelativePath;
|
||||||
}
|
}
|
||||||
@@ -698,6 +705,9 @@ size_t CalculateBuiltinShaderMemorySize(const Shader& shader) {
|
|||||||
memorySize += variant.profile.Length();
|
memorySize += variant.profile.Length();
|
||||||
memorySize += variant.sourceCode.Length();
|
memorySize += variant.sourceCode.Length();
|
||||||
memorySize += variant.compiledBinary.Size();
|
memorySize += variant.compiledBinary.Size();
|
||||||
|
for (const ShaderBackendCompiledBinary& record : variant.backendCompiledBinaries) {
|
||||||
|
memorySize += record.payload.Size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -866,6 +876,10 @@ bool TryGetBuiltinShaderPathByShaderName(
|
|||||||
outPath = GetBuiltinGaussianSplatShaderPath();
|
outPath = GetBuiltinGaussianSplatShaderPath();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (shaderName == "Builtin Gaussian Splat Utilities") {
|
||||||
|
outPath = GetBuiltinGaussianSplatUtilitiesShaderPath();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (shaderName == "Builtin Volumetric") {
|
if (shaderName == "Builtin Volumetric") {
|
||||||
outPath = GetBuiltinVolumetricShaderPath();
|
outPath = GetBuiltinVolumetricShaderPath();
|
||||||
return true;
|
return true;
|
||||||
@@ -957,6 +971,10 @@ Containers::String GetBuiltinGaussianSplatShaderPath() {
|
|||||||
return Containers::String(kBuiltinGaussianSplatShaderPath);
|
return Containers::String(kBuiltinGaussianSplatShaderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Containers::String GetBuiltinGaussianSplatUtilitiesShaderPath() {
|
||||||
|
return Containers::String(kBuiltinGaussianSplatUtilitiesShaderPath);
|
||||||
|
}
|
||||||
|
|
||||||
Containers::String GetBuiltinVolumetricShaderPath() {
|
Containers::String GetBuiltinVolumetricShaderPath() {
|
||||||
return Containers::String(kBuiltinVolumetricShaderPath);
|
return Containers::String(kBuiltinVolumetricShaderPath);
|
||||||
}
|
}
|
||||||
@@ -1077,6 +1095,8 @@ LoadResult CreateBuiltinShaderResource(const Containers::String& path) {
|
|||||||
shader = BuildBuiltinSkyboxShader(path);
|
shader = BuildBuiltinSkyboxShader(path);
|
||||||
} else if (path == GetBuiltinGaussianSplatShaderPath()) {
|
} else if (path == GetBuiltinGaussianSplatShaderPath()) {
|
||||||
shader = BuildBuiltinGaussianSplatShader(path);
|
shader = BuildBuiltinGaussianSplatShader(path);
|
||||||
|
} else if (path == GetBuiltinGaussianSplatUtilitiesShaderPath()) {
|
||||||
|
shader = TryLoadBuiltinShaderFromAsset(path);
|
||||||
} else if (path == GetBuiltinVolumetricShaderPath()) {
|
} else if (path == GetBuiltinVolumetricShaderPath()) {
|
||||||
shader = BuildBuiltinVolumetricShader(path);
|
shader = BuildBuiltinVolumetricShader(path);
|
||||||
} else if (path == GetBuiltinColorScalePostProcessShaderPath()) {
|
} else if (path == GetBuiltinColorScalePostProcessShaderPath()) {
|
||||||
|
|||||||
@@ -192,8 +192,9 @@ void ImportConcretePass(Shader& shader, const ShaderPass& sourcePass) {
|
|||||||
shader.AddPass(sourcePass);
|
shader.AddPass(sourcePass);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsBufferShaderResourceType(ShaderResourceType type) {
|
bool IsScannedAuthoringResourceType(ShaderResourceType type) {
|
||||||
return type == ShaderResourceType::StructuredBuffer ||
|
return type == ShaderResourceType::ConstantBuffer ||
|
||||||
|
type == ShaderResourceType::StructuredBuffer ||
|
||||||
type == ShaderResourceType::RawBuffer ||
|
type == ShaderResourceType::RawBuffer ||
|
||||||
type == ShaderResourceType::RWStructuredBuffer ||
|
type == ShaderResourceType::RWStructuredBuffer ||
|
||||||
type == ShaderResourceType::RWRawBuffer;
|
type == ShaderResourceType::RWRawBuffer;
|
||||||
@@ -239,10 +240,13 @@ Core::uint32 FindNextBindingInSet(
|
|||||||
return nextBinding;
|
return nextBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TryParseHlslBufferResourceLine(
|
bool TryParseHlslAuthoringResourceLine(
|
||||||
const std::string& line,
|
const std::string& line,
|
||||||
ShaderResourceType& outType,
|
ShaderResourceType& outType,
|
||||||
Containers::String& outName) {
|
Containers::String& outName) {
|
||||||
|
static const std::regex kConstantBufferPattern(
|
||||||
|
R"(^\s*cbuffer\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*(?:\{\s*)?$)",
|
||||||
|
std::regex::ECMAScript);
|
||||||
static const std::regex kStructuredPattern(
|
static const std::regex kStructuredPattern(
|
||||||
R"(^\s*(?:globallycoherent\s+)?(RWStructuredBuffer|StructuredBuffer)\s*<[^;\r\n>]+>\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*;)",
|
R"(^\s*(?:globallycoherent\s+)?(RWStructuredBuffer|StructuredBuffer)\s*<[^;\r\n>]+>\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*;)",
|
||||||
std::regex::ECMAScript);
|
std::regex::ECMAScript);
|
||||||
@@ -251,6 +255,12 @@ bool TryParseHlslBufferResourceLine(
|
|||||||
std::regex::ECMAScript);
|
std::regex::ECMAScript);
|
||||||
|
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
|
if (std::regex_match(line, match, kConstantBufferPattern) && match.size() >= 2u) {
|
||||||
|
outType = ShaderResourceType::ConstantBuffer;
|
||||||
|
outName = match[1].str().c_str();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (std::regex_match(line, match, kStructuredPattern) && match.size() >= 3u) {
|
if (std::regex_match(line, match, kStructuredPattern) && match.size() >= 3u) {
|
||||||
outType =
|
outType =
|
||||||
match[1].str() == "RWStructuredBuffer"
|
match[1].str() == "RWStructuredBuffer"
|
||||||
@@ -272,7 +282,7 @@ bool TryParseHlslBufferResourceLine(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppendScannedBufferResourceBindings(
|
void AppendScannedAuthoringResourceBindings(
|
||||||
const Containers::String& sourceText,
|
const Containers::String& sourceText,
|
||||||
Containers::Array<ShaderResourceBindingDesc>& ioBindings) {
|
Containers::Array<ShaderResourceBindingDesc>& ioBindings) {
|
||||||
if (sourceText.Empty()) {
|
if (sourceText.Empty()) {
|
||||||
@@ -286,11 +296,11 @@ void AppendScannedBufferResourceBindings(
|
|||||||
const std::string line = Internal::StripAuthoringLineComment(rawLine);
|
const std::string line = Internal::StripAuthoringLineComment(rawLine);
|
||||||
ShaderResourceType resourceType = ShaderResourceType::ConstantBuffer;
|
ShaderResourceType resourceType = ShaderResourceType::ConstantBuffer;
|
||||||
Containers::String resourceName;
|
Containers::String resourceName;
|
||||||
if (!TryParseHlslBufferResourceLine(line, resourceType, resourceName)) {
|
if (!TryParseHlslAuthoringResourceLine(line, resourceType, resourceName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsBufferShaderResourceType(resourceType) ||
|
if (!IsScannedAuthoringResourceType(resourceType) ||
|
||||||
resourceName.Empty() ||
|
resourceName.Empty() ||
|
||||||
HasResourceBindingNamed(ioBindings, resourceName)) {
|
HasResourceBindingNamed(ioBindings, resourceName)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -354,7 +364,7 @@ ShaderPass BuildConcretePass(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AppendScannedBufferResourceBindings(strippedCombinedSource, shaderPass.resources);
|
AppendScannedAuthoringResourceBindings(strippedCombinedSource, shaderPass.resources);
|
||||||
const std::vector<ShaderKeywordSet> keywordSets =
|
const std::vector<ShaderKeywordSet> keywordSets =
|
||||||
BuildShaderKeywordVariantSets(pass.keywordDeclarations);
|
BuildShaderKeywordVariantSets(pass.keywordDeclarations);
|
||||||
|
|
||||||
@@ -615,6 +625,9 @@ size_t CalculateShaderMemorySize(const Shader& shader) {
|
|||||||
memorySize += variant.profile.Length();
|
memorySize += variant.profile.Length();
|
||||||
memorySize += variant.sourceCode.Length();
|
memorySize += variant.sourceCode.Length();
|
||||||
memorySize += variant.compiledBinary.Size();
|
memorySize += variant.compiledBinary.Size();
|
||||||
|
for (const ShaderBackendCompiledBinary& record : variant.backendCompiledBinaries) {
|
||||||
|
memorySize += record.payload.Size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1087,6 +1087,201 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringGauss
|
|||||||
delete shader;
|
delete shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatUtilitiesShaderUsesComputeAuthoringContract) {
|
||||||
|
ShaderLoader loader;
|
||||||
|
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_NE(result.resource, nullptr);
|
||||||
|
|
||||||
|
Shader* shader = static_cast<Shader*>(result.resource);
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
|
||||||
|
const ShaderPass* pass = shader->FindPass("GaussianSplatPrepareOrder");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
EXPECT_EQ(pass->resources.Size(), 4u);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* perObject =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "PerObjectConstants");
|
||||||
|
ASSERT_NE(perObject, nullptr);
|
||||||
|
EXPECT_EQ(perObject->type, ShaderResourceType::ConstantBuffer);
|
||||||
|
EXPECT_EQ(perObject->set, 0u);
|
||||||
|
EXPECT_EQ(perObject->binding, 0u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*perObject),
|
||||||
|
BuiltinPassResourceSemantic::PerObject);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* positions =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "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* sortDistances =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatSortDistances");
|
||||||
|
ASSERT_NE(sortDistances, nullptr);
|
||||||
|
EXPECT_EQ(sortDistances->type, ShaderResourceType::RWStructuredBuffer);
|
||||||
|
EXPECT_EQ(sortDistances->set, 4u);
|
||||||
|
EXPECT_EQ(sortDistances->binding, 0u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*sortDistances),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatSortDistanceBuffer);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* orderBuffer =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplatPrepareOrder", "GaussianSplatOrderBuffer");
|
||||||
|
ASSERT_NE(orderBuffer, nullptr);
|
||||||
|
EXPECT_EQ(orderBuffer->type, ShaderResourceType::RWStructuredBuffer);
|
||||||
|
EXPECT_EQ(orderBuffer->set, 4u);
|
||||||
|
EXPECT_EQ(orderBuffer->binding, 1u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*orderBuffer),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatOrderBuffer);
|
||||||
|
|
||||||
|
const ShaderStageVariant* computeVariant = shader->FindVariant(
|
||||||
|
"GaussianSplatPrepareOrder",
|
||||||
|
XCEngine::Resources::ShaderType::Compute,
|
||||||
|
XCEngine::Resources::ShaderBackend::D3D12);
|
||||||
|
ASSERT_NE(computeVariant, nullptr);
|
||||||
|
EXPECT_EQ(computeVariant->entryPoint, "GaussianSplatPrepareOrderCS");
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoadedGaussianSplatUtilitiesShaderContract) {
|
||||||
|
ShaderLoader loader;
|
||||||
|
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_NE(result.resource, nullptr);
|
||||||
|
|
||||||
|
Shader* shader = static_cast<Shader*>(result.resource);
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
|
||||||
|
const ShaderPass* pass = shader->FindPass("GaussianSplatPrepareOrder");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
|
||||||
|
BuiltinPassResourceBindingPlan plan = {};
|
||||||
|
String error;
|
||||||
|
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
|
||||||
|
ASSERT_EQ(plan.bindings.Size(), 4u);
|
||||||
|
EXPECT_TRUE(plan.perObject.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatPositionBuffer.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatSortDistanceBuffer.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatOrderBuffer.IsValid());
|
||||||
|
EXPECT_EQ(plan.perObject.set, 0u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatPositionBuffer.set, 2u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatSortDistanceBuffer.set, 4u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatSortDistanceBuffer.binding, 0u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatOrderBuffer.set, 4u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatOrderBuffer.binding, 1u);
|
||||||
|
EXPECT_EQ(plan.firstDescriptorSet, 0u);
|
||||||
|
EXPECT_EQ(plan.descriptorSetCount, 5u);
|
||||||
|
|
||||||
|
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
||||||
|
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
|
||||||
|
ASSERT_EQ(setLayouts.size(), 5u);
|
||||||
|
EXPECT_TRUE(setLayouts[0].usesPerObject);
|
||||||
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatPositionBuffer);
|
||||||
|
EXPECT_TRUE(setLayouts[4].usesGaussianSplatSortDistanceBuffer);
|
||||||
|
EXPECT_TRUE(setLayouts[4].usesGaussianSplatOrderBuffer);
|
||||||
|
ASSERT_EQ(setLayouts[4].bindings.size(), 2u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[4].bindings[0].type),
|
||||||
|
DescriptorType::UAV);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[4].bindings[1].type),
|
||||||
|
DescriptorType::UAV);
|
||||||
|
EXPECT_EQ(
|
||||||
|
setLayouts[4].bindings[0].resourceDimension,
|
||||||
|
ResourceViewDimension::StructuredBuffer);
|
||||||
|
EXPECT_EQ(
|
||||||
|
setLayouts[4].bindings[1].resourceDimension,
|
||||||
|
ResourceViewDimension::StructuredBuffer);
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesGaussianSplatUtilitiesComputeBindingsToDescriptorSpaces) {
|
||||||
|
ShaderLoader loader;
|
||||||
|
LoadResult result = loader.Load(GetBuiltinGaussianSplatUtilitiesShaderPath());
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_NE(result.resource, nullptr);
|
||||||
|
|
||||||
|
Shader* shader = static_cast<Shader*>(result.resource);
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
|
||||||
|
const ShaderPass* pass = shader->FindPass("GaussianSplatPrepareOrder");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
|
||||||
|
const ShaderStageVariant* d3d12Compute = shader->FindVariant(
|
||||||
|
"GaussianSplatPrepareOrder",
|
||||||
|
XCEngine::Resources::ShaderType::Compute,
|
||||||
|
XCEngine::Resources::ShaderBackend::D3D12);
|
||||||
|
ASSERT_NE(d3d12Compute, nullptr);
|
||||||
|
|
||||||
|
ShaderCompileDesc d3d12CompileDesc = {};
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
*pass,
|
||||||
|
XCEngine::Resources::ShaderBackend::D3D12,
|
||||||
|
*d3d12Compute,
|
||||||
|
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,
|
||||||
|
"StructuredBuffer<float3> GaussianSplatPositions",
|
||||||
|
"register(t0)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
d3d12Source,
|
||||||
|
"RWStructuredBuffer<uint> GaussianSplatSortDistances",
|
||||||
|
"register(u0)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
d3d12Source,
|
||||||
|
"RWStructuredBuffer<uint> GaussianSplatOrderBuffer",
|
||||||
|
"register(u1)"));
|
||||||
|
|
||||||
|
const ShaderStageVariant* vulkanCompute = shader->FindVariant(
|
||||||
|
"GaussianSplatPrepareOrder",
|
||||||
|
XCEngine::Resources::ShaderType::Compute,
|
||||||
|
XCEngine::Resources::ShaderBackend::Vulkan);
|
||||||
|
ASSERT_NE(vulkanCompute, nullptr);
|
||||||
|
|
||||||
|
ShaderCompileDesc vulkanCompileDesc = {};
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
*pass,
|
||||||
|
XCEngine::Resources::ShaderBackend::Vulkan,
|
||||||
|
*vulkanCompute,
|
||||||
|
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,
|
||||||
|
"StructuredBuffer<float3> GaussianSplatPositions",
|
||||||
|
"register(t0, space2)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
vulkanSource,
|
||||||
|
"RWStructuredBuffer<uint> GaussianSplatSortDistances",
|
||||||
|
"register(u0, space4)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
vulkanSource,
|
||||||
|
"RWStructuredBuffer<uint> GaussianSplatOrderBuffer",
|
||||||
|
"register(u1, space4)"));
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
TEST(BuiltinForwardPipeline_Test, BuiltinVolumetricShaderUsesAuthoringContract) {
|
TEST(BuiltinForwardPipeline_Test, BuiltinVolumetricShaderUsesAuthoringContract) {
|
||||||
ShaderLoader loader;
|
ShaderLoader loader;
|
||||||
LoadResult result = loader.Load(GetBuiltinVolumetricShaderPath());
|
LoadResult result = loader.Load(GetBuiltinVolumetricShaderPath());
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <regex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
using namespace XCEngine::Resources;
|
using namespace XCEngine::Resources;
|
||||||
@@ -71,6 +72,7 @@ TEST(ShaderLoader, CanLoad) {
|
|||||||
EXPECT_TRUE(loader.CanLoad(GetBuiltinObjectIdOutlineShaderPath()));
|
EXPECT_TRUE(loader.CanLoad(GetBuiltinObjectIdOutlineShaderPath()));
|
||||||
EXPECT_TRUE(loader.CanLoad(GetBuiltinSelectionMaskShaderPath()));
|
EXPECT_TRUE(loader.CanLoad(GetBuiltinSelectionMaskShaderPath()));
|
||||||
EXPECT_TRUE(loader.CanLoad(GetBuiltinSelectionOutlineShaderPath()));
|
EXPECT_TRUE(loader.CanLoad(GetBuiltinSelectionOutlineShaderPath()));
|
||||||
|
EXPECT_TRUE(loader.CanLoad(GetBuiltinGaussianSplatUtilitiesShaderPath()));
|
||||||
EXPECT_FALSE(loader.CanLoad("test.vert"));
|
EXPECT_FALSE(loader.CanLoad("test.vert"));
|
||||||
EXPECT_FALSE(loader.CanLoad("test.frag"));
|
EXPECT_FALSE(loader.CanLoad("test.frag"));
|
||||||
EXPECT_FALSE(loader.CanLoad("test.glsl"));
|
EXPECT_FALSE(loader.CanLoad("test.glsl"));
|
||||||
@@ -603,6 +605,100 @@ TEST(ShaderLoader, LoadShaderAuthoringBuildsComputeOnlyPassVariant) {
|
|||||||
fs::remove_all(shaderRoot);
|
fs::remove_all(shaderRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ShaderLoader, LoadShaderAuthoringBuildsComputeOnlyPassConstantBufferBindings) {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
const fs::path shaderRoot = fs::temp_directory_path() / "xc_shader_authoring_compute_constants";
|
||||||
|
const fs::path shaderPath = shaderRoot / "compute_constants.shader";
|
||||||
|
|
||||||
|
fs::remove_all(shaderRoot);
|
||||||
|
fs::create_directories(shaderRoot);
|
||||||
|
|
||||||
|
WriteTextFile(
|
||||||
|
shaderPath,
|
||||||
|
R"(Shader "ComputeConstantsShader"
|
||||||
|
{
|
||||||
|
SubShader
|
||||||
|
{
|
||||||
|
Pass
|
||||||
|
{
|
||||||
|
Name "Prepare"
|
||||||
|
HLSLPROGRAM
|
||||||
|
#pragma target 4.5
|
||||||
|
#pragma compute PrepareCS
|
||||||
|
cbuffer PerObjectConstants
|
||||||
|
{
|
||||||
|
float4x4 ObjectToWorld;
|
||||||
|
};
|
||||||
|
StructuredBuffer<float4> InputData;
|
||||||
|
RWStructuredBuffer<uint> OutputOrder;
|
||||||
|
[numthreads(64, 1, 1)]
|
||||||
|
void PrepareCS(uint3 dispatchThreadId : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
OutputOrder[dispatchThreadId.x] =
|
||||||
|
(uint)mul(ObjectToWorld, InputData[dispatchThreadId.x]).x;
|
||||||
|
}
|
||||||
|
ENDHLSL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
ShaderLoader loader;
|
||||||
|
LoadResult result = loader.Load(shaderPath.string().c_str());
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_NE(result.resource, nullptr);
|
||||||
|
|
||||||
|
Shader* shader = static_cast<Shader*>(result.resource);
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
|
||||||
|
const ShaderPass* pass = shader->FindPass("Prepare");
|
||||||
|
ASSERT_NE(pass, nullptr);
|
||||||
|
EXPECT_EQ(pass->resources.Size(), 3u);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* perObject =
|
||||||
|
shader->FindPassResourceBinding("Prepare", "PerObjectConstants");
|
||||||
|
ASSERT_NE(perObject, nullptr);
|
||||||
|
EXPECT_EQ(perObject->type, ShaderResourceType::ConstantBuffer);
|
||||||
|
EXPECT_EQ(perObject->set, 0u);
|
||||||
|
EXPECT_EQ(perObject->binding, 0u);
|
||||||
|
|
||||||
|
const ShaderStageVariant* computeVariant =
|
||||||
|
shader->FindVariant("Prepare", ShaderType::Compute, ShaderBackend::D3D12);
|
||||||
|
ASSERT_NE(computeVariant, nullptr);
|
||||||
|
|
||||||
|
XCEngine::RHI::ShaderCompileDesc d3d12CompileDesc = {};
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
*pass,
|
||||||
|
ShaderBackend::D3D12,
|
||||||
|
*computeVariant,
|
||||||
|
d3d12CompileDesc);
|
||||||
|
const std::string d3d12Source(
|
||||||
|
reinterpret_cast<const char*>(d3d12CompileDesc.source.data()),
|
||||||
|
d3d12CompileDesc.source.size());
|
||||||
|
EXPECT_TRUE(std::regex_search(
|
||||||
|
d3d12Source,
|
||||||
|
std::regex(R"(cbuffer\s+PerObjectConstants\s*:\s*register\(b0\))", std::regex::ECMAScript)));
|
||||||
|
|
||||||
|
XCEngine::RHI::ShaderCompileDesc vulkanCompileDesc = {};
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
*pass,
|
||||||
|
ShaderBackend::Vulkan,
|
||||||
|
*computeVariant,
|
||||||
|
vulkanCompileDesc);
|
||||||
|
const std::string vulkanSource(
|
||||||
|
reinterpret_cast<const char*>(vulkanCompileDesc.source.data()),
|
||||||
|
vulkanCompileDesc.source.size());
|
||||||
|
EXPECT_TRUE(std::regex_search(
|
||||||
|
vulkanSource,
|
||||||
|
std::regex(
|
||||||
|
R"(cbuffer\s+PerObjectConstants\s*:\s*register\(b0,\s*space0\))",
|
||||||
|
std::regex::ECMAScript)));
|
||||||
|
|
||||||
|
delete shader;
|
||||||
|
fs::remove_all(shaderRoot);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ShaderLoader, LoadShaderAuthoringRejectsPassMixingComputeAndGraphicsPragmas) {
|
TEST(ShaderLoader, LoadShaderAuthoringRejectsPassMixingComputeAndGraphicsPragmas) {
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user