Add Unity-style buffer shader resource support
This commit is contained in:
@@ -369,7 +369,12 @@ inline RHI::DescriptorType ToBuiltinPassDescriptorType(Resources::ShaderResource
|
||||
return RHI::DescriptorType::CBV;
|
||||
case Resources::ShaderResourceType::Texture2D:
|
||||
case Resources::ShaderResourceType::TextureCube:
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
return RHI::DescriptorType::SRV;
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
return RHI::DescriptorType::UAV;
|
||||
case Resources::ShaderResourceType::Sampler:
|
||||
return RHI::DescriptorType::Sampler;
|
||||
default:
|
||||
@@ -377,6 +382,23 @@ inline RHI::DescriptorType ToBuiltinPassDescriptorType(Resources::ShaderResource
|
||||
}
|
||||
}
|
||||
|
||||
inline RHI::ResourceViewDimension ToBuiltinPassResourceDimension(Resources::ShaderResourceType type) {
|
||||
switch (type) {
|
||||
case Resources::ShaderResourceType::Texture2D:
|
||||
return RHI::ResourceViewDimension::Texture2D;
|
||||
case Resources::ShaderResourceType::TextureCube:
|
||||
return RHI::ResourceViewDimension::TextureCube;
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
return RHI::ResourceViewDimension::StructuredBuffer;
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
return RHI::ResourceViewDimension::RawBuffer;
|
||||
default:
|
||||
return RHI::ResourceViewDimension::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
inline RHI::DescriptorHeapType ResolveBuiltinPassDescriptorHeapType(Resources::ShaderResourceType type) {
|
||||
return type == Resources::ShaderResourceType::Sampler
|
||||
? RHI::DescriptorHeapType::Sampler
|
||||
@@ -480,6 +502,7 @@ inline bool TryBuildBuiltinPassSetLayouts(
|
||||
layoutBinding.binding = binding.location.binding;
|
||||
layoutBinding.type = static_cast<Core::uint32>(descriptorType);
|
||||
layoutBinding.count = 1;
|
||||
layoutBinding.resourceDimension = ToBuiltinPassResourceDimension(binding.resourceType);
|
||||
setLayout.bindings.push_back(layoutBinding);
|
||||
|
||||
switch (binding.semantic) {
|
||||
|
||||
@@ -210,6 +210,14 @@ inline const char* ShaderResourceTypeToString(Resources::ShaderResourceType type
|
||||
return "TextureCube";
|
||||
case Resources::ShaderResourceType::Sampler:
|
||||
return "Sampler";
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
return "StructuredBuffer";
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
return "RawBuffer";
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
return "RWStructuredBuffer";
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
return "RWRawBuffer";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -45,7 +45,11 @@ enum class ShaderResourceType : Core::uint8 {
|
||||
ConstantBuffer = 0,
|
||||
Texture2D,
|
||||
TextureCube,
|
||||
Sampler
|
||||
Sampler,
|
||||
StructuredBuffer,
|
||||
RawBuffer,
|
||||
RWStructuredBuffer,
|
||||
RWRawBuffer
|
||||
};
|
||||
|
||||
struct ShaderUniform {
|
||||
|
||||
@@ -137,6 +137,38 @@ inline bool TryRewriteHlslRegisterBindingWithName(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resourceType == Resources::ShaderResourceType::StructuredBuffer ||
|
||||
resourceType == Resources::ShaderResourceType::RWStructuredBuffer) {
|
||||
const std::regex pattern(
|
||||
"((?:globallycoherent\\s+)?(?:StructuredBuffer|RWStructuredBuffer)\\s*<[^;\\r\\n>]+>\\s+" +
|
||||
escapedName + "\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*;)",
|
||||
std::regex::ECMAScript);
|
||||
const std::string rewritten =
|
||||
std::regex_replace(sourceText, pattern, "$1: " + registerClause + "$3");
|
||||
if (rewritten != sourceText) {
|
||||
sourceText = rewritten;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resourceType == Resources::ShaderResourceType::RawBuffer ||
|
||||
resourceType == Resources::ShaderResourceType::RWRawBuffer) {
|
||||
const std::regex pattern(
|
||||
"((?:globallycoherent\\s+)?(?:ByteAddressBuffer|RWByteAddressBuffer)\\s+" + escapedName +
|
||||
"\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*;)",
|
||||
std::regex::ECMAScript);
|
||||
const std::string rewritten =
|
||||
std::regex_replace(sourceText, pattern, "$1: " + registerClause + "$3");
|
||||
if (rewritten != sourceText) {
|
||||
sourceText = rewritten;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::regex pattern(
|
||||
"((?:Texture2D|TextureCube|SamplerState|SamplerComparisonState)\\s+" + escapedName +
|
||||
"\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*;)",
|
||||
@@ -157,9 +189,14 @@ inline const char* TryGetHlslRegisterPrefix(Resources::ShaderResourceType type)
|
||||
return "b";
|
||||
case Resources::ShaderResourceType::Texture2D:
|
||||
case Resources::ShaderResourceType::TextureCube:
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
return "t";
|
||||
case Resources::ShaderResourceType::Sampler:
|
||||
return "s";
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
return "u";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
@@ -223,11 +260,15 @@ inline bool TryBuildRuntimeShaderBindings(
|
||||
break;
|
||||
case Resources::ShaderResourceType::Texture2D:
|
||||
case Resources::ShaderResourceType::TextureCube:
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
binding.binding = nextTextureRegister++;
|
||||
break;
|
||||
case Resources::ShaderResourceType::Sampler:
|
||||
binding.binding = nextSamplerRegister++;
|
||||
break;
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
default:
|
||||
binding.binding = nextUnorderedAccessRegister++;
|
||||
break;
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <system_error>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@@ -190,6 +192,119 @@ void ImportConcretePass(Shader& shader, const ShaderPass& sourcePass) {
|
||||
shader.AddPass(sourcePass);
|
||||
}
|
||||
|
||||
bool IsBufferShaderResourceType(ShaderResourceType type) {
|
||||
return type == ShaderResourceType::StructuredBuffer ||
|
||||
type == ShaderResourceType::RawBuffer ||
|
||||
type == ShaderResourceType::RWStructuredBuffer ||
|
||||
type == ShaderResourceType::RWRawBuffer;
|
||||
}
|
||||
|
||||
Core::uint32 ResolveDefaultAuthoringSet(ShaderResourceType type) {
|
||||
switch (type) {
|
||||
case ShaderResourceType::StructuredBuffer:
|
||||
case ShaderResourceType::RawBuffer:
|
||||
return 2u;
|
||||
case ShaderResourceType::RWStructuredBuffer:
|
||||
case ShaderResourceType::RWRawBuffer:
|
||||
return 4u;
|
||||
default:
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasResourceBindingNamed(
|
||||
const Containers::Array<ShaderResourceBindingDesc>& bindings,
|
||||
const Containers::String& name) {
|
||||
for (const ShaderResourceBindingDesc& binding : bindings) {
|
||||
if (binding.name == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::uint32 FindNextBindingInSet(
|
||||
const Containers::Array<ShaderResourceBindingDesc>& bindings,
|
||||
Core::uint32 setIndex) {
|
||||
Core::uint32 nextBinding = 0;
|
||||
for (const ShaderResourceBindingDesc& binding : bindings) {
|
||||
if (binding.set != setIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nextBinding = std::max(nextBinding, binding.binding + 1u);
|
||||
}
|
||||
|
||||
return nextBinding;
|
||||
}
|
||||
|
||||
bool TryParseHlslBufferResourceLine(
|
||||
const std::string& line,
|
||||
ShaderResourceType& outType,
|
||||
Containers::String& outName) {
|
||||
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*;)",
|
||||
std::regex::ECMAScript);
|
||||
static const std::regex kRawPattern(
|
||||
R"(^\s*(?:globallycoherent\s+)?(RWByteAddressBuffer|ByteAddressBuffer)\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*register\s*\([^\)]*\))?\s*;)",
|
||||
std::regex::ECMAScript);
|
||||
|
||||
std::smatch match;
|
||||
if (std::regex_match(line, match, kStructuredPattern) && match.size() >= 3u) {
|
||||
outType =
|
||||
match[1].str() == "RWStructuredBuffer"
|
||||
? ShaderResourceType::RWStructuredBuffer
|
||||
: ShaderResourceType::StructuredBuffer;
|
||||
outName = match[2].str().c_str();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (std::regex_match(line, match, kRawPattern) && match.size() >= 3u) {
|
||||
outType =
|
||||
match[1].str() == "RWByteAddressBuffer"
|
||||
? ShaderResourceType::RWRawBuffer
|
||||
: ShaderResourceType::RawBuffer;
|
||||
outName = match[2].str().c_str();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AppendScannedBufferResourceBindings(
|
||||
const Containers::String& sourceText,
|
||||
Containers::Array<ShaderResourceBindingDesc>& ioBindings) {
|
||||
if (sourceText.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> lines;
|
||||
Internal::SplitShaderAuthoringLines(sourceText.CStr(), lines);
|
||||
|
||||
for (const std::string& rawLine : lines) {
|
||||
const std::string line = Internal::StripAuthoringLineComment(rawLine);
|
||||
ShaderResourceType resourceType = ShaderResourceType::ConstantBuffer;
|
||||
Containers::String resourceName;
|
||||
if (!TryParseHlslBufferResourceLine(line, resourceType, resourceName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsBufferShaderResourceType(resourceType) ||
|
||||
resourceName.Empty() ||
|
||||
HasResourceBindingNamed(ioBindings, resourceName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ShaderResourceBindingDesc binding = {};
|
||||
binding.name = resourceName;
|
||||
binding.type = resourceType;
|
||||
binding.set = ResolveDefaultAuthoringSet(resourceType);
|
||||
binding.binding = FindNextBindingInSet(ioBindings, binding.set);
|
||||
ioBindings.PushBack(binding);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderPass BuildConcretePass(
|
||||
const ShaderIR& shaderIR,
|
||||
const ShaderSubShaderIR& subShader,
|
||||
@@ -208,19 +323,19 @@ ShaderPass BuildConcretePass(
|
||||
for (const ShaderResourceBindingDesc& resourceBinding : pass.resources) {
|
||||
shaderPass.resources.PushBack(resourceBinding);
|
||||
}
|
||||
if (shaderPass.resources.Empty()) {
|
||||
Containers::Array<ShaderResourceBindingDesc> defaultBindings;
|
||||
if (::XCEngine::Rendering::TryBuildBuiltinPassDefaultResourceBindings(shaderPass, defaultBindings)) {
|
||||
for (const ShaderResourceBindingDesc& resourceBinding : defaultBindings) {
|
||||
shaderPass.resources.PushBack(resourceBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const ShaderKeywordDeclaration& keywordDeclaration : pass.keywordDeclarations) {
|
||||
shaderPass.keywordDeclarations.PushBack(keywordDeclaration);
|
||||
}
|
||||
|
||||
if (pass.programSource.Empty()) {
|
||||
if (shaderPass.resources.Empty()) {
|
||||
Containers::Array<ShaderResourceBindingDesc> defaultBindings;
|
||||
if (::XCEngine::Rendering::TryBuildBuiltinPassDefaultResourceBindings(shaderPass, defaultBindings)) {
|
||||
for (const ShaderResourceBindingDesc& resourceBinding : defaultBindings) {
|
||||
shaderPass.resources.PushBack(resourceBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shaderPass;
|
||||
}
|
||||
|
||||
@@ -231,6 +346,15 @@ ShaderPass BuildConcretePass(
|
||||
AppendAuthoringSourceBlock(combinedSource, pass.programSource);
|
||||
|
||||
const Containers::String strippedCombinedSource = StripShaderAuthoringPragmas(combinedSource);
|
||||
if (shaderPass.resources.Empty()) {
|
||||
Containers::Array<ShaderResourceBindingDesc> defaultBindings;
|
||||
if (::XCEngine::Rendering::TryBuildBuiltinPassDefaultResourceBindings(shaderPass, defaultBindings)) {
|
||||
for (const ShaderResourceBindingDesc& resourceBinding : defaultBindings) {
|
||||
shaderPass.resources.PushBack(resourceBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
AppendScannedBufferResourceBindings(strippedCombinedSource, shaderPass.resources);
|
||||
const std::vector<ShaderKeywordSet> keywordSets =
|
||||
BuildShaderKeywordVariantSets(pass.keywordDeclarations);
|
||||
|
||||
|
||||
@@ -202,6 +202,22 @@ bool TryParseShaderResourceType(const Containers::String& value, ShaderResourceT
|
||||
outType = ShaderResourceType::Sampler;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "structuredbuffer" || normalized == "structured") {
|
||||
outType = ShaderResourceType::StructuredBuffer;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "rawbuffer" || normalized == "byteaddressbuffer") {
|
||||
outType = ShaderResourceType::RawBuffer;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "rwstructuredbuffer" || normalized == "rwstructured") {
|
||||
outType = ShaderResourceType::RWStructuredBuffer;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "rwrawbuffer" || normalized == "rwbyteaddressbuffer") {
|
||||
outType = ShaderResourceType::RWRawBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -253,6 +253,9 @@ public:
|
||||
desc.dimension);
|
||||
}
|
||||
|
||||
XCEngine::RHI::RHIResourceView* CreateShaderResourceView(
|
||||
XCEngine::RHI::RHIBuffer*,
|
||||
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIResourceView* CreateShaderResourceView(
|
||||
XCEngine::RHI::RHITexture*,
|
||||
const XCEngine::RHI::ResourceViewDesc& desc) override {
|
||||
@@ -264,6 +267,9 @@ public:
|
||||
static_cast<XCEngine::RHI::Format>(desc.format),
|
||||
desc.dimension);
|
||||
}
|
||||
XCEngine::RHI::RHIResourceView* CreateUnorderedAccessView(
|
||||
XCEngine::RHI::RHIBuffer*,
|
||||
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHIResourceView* CreateUnorderedAccessView(
|
||||
XCEngine::RHI::RHITexture*,
|
||||
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include "Rendering/Detail/ShaderVariantUtils.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
@@ -497,6 +498,193 @@ TEST(ShaderLoader, LoadShaderAuthoringBuildsGenericHlslVariants) {
|
||||
fs::remove_all(shaderRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, LoadShaderAuthoringCollectsUnityStyleBufferResources) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path shaderRoot = fs::temp_directory_path() / "xc_shader_authoring_buffer_resources_test";
|
||||
const fs::path shaderPath = shaderRoot / "buffer_resources.shader";
|
||||
|
||||
fs::remove_all(shaderRoot);
|
||||
fs::create_directories(shaderRoot);
|
||||
WriteTextFile(
|
||||
shaderPath,
|
||||
R"(Shader "Test/BufferResources"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
{
|
||||
Name "Volume"
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma vertex MainVS
|
||||
#pragma fragment MainPS
|
||||
StructuredBuffer<float4> InputBuffer;
|
||||
ByteAddressBuffer RawInput;
|
||||
RWStructuredBuffer<uint> OutputBuffer;
|
||||
RWByteAddressBuffer OutputRaw;
|
||||
struct VSInput
|
||||
{
|
||||
float3 positionOS : POSITION;
|
||||
};
|
||||
float4 MainVS(VSInput input) : SV_POSITION
|
||||
{
|
||||
return float4(input.positionOS, 1.0);
|
||||
}
|
||||
float4 MainPS() : SV_TARGET
|
||||
{
|
||||
return float4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
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("Volume");
|
||||
ASSERT_NE(pass, nullptr);
|
||||
ASSERT_EQ(pass->resources.Size(), 4u);
|
||||
|
||||
const ShaderResourceBindingDesc* inputBuffer =
|
||||
shader->FindPassResourceBinding("Volume", "InputBuffer");
|
||||
ASSERT_NE(inputBuffer, nullptr);
|
||||
EXPECT_EQ(inputBuffer->type, ShaderResourceType::StructuredBuffer);
|
||||
EXPECT_EQ(inputBuffer->set, 2u);
|
||||
EXPECT_EQ(inputBuffer->binding, 0u);
|
||||
|
||||
const ShaderResourceBindingDesc* rawInput =
|
||||
shader->FindPassResourceBinding("Volume", "RawInput");
|
||||
ASSERT_NE(rawInput, nullptr);
|
||||
EXPECT_EQ(rawInput->type, ShaderResourceType::RawBuffer);
|
||||
EXPECT_EQ(rawInput->set, 2u);
|
||||
EXPECT_EQ(rawInput->binding, 1u);
|
||||
|
||||
const ShaderResourceBindingDesc* outputBuffer =
|
||||
shader->FindPassResourceBinding("Volume", "OutputBuffer");
|
||||
ASSERT_NE(outputBuffer, nullptr);
|
||||
EXPECT_EQ(outputBuffer->type, ShaderResourceType::RWStructuredBuffer);
|
||||
EXPECT_EQ(outputBuffer->set, 4u);
|
||||
EXPECT_EQ(outputBuffer->binding, 0u);
|
||||
|
||||
const ShaderResourceBindingDesc* outputRaw =
|
||||
shader->FindPassResourceBinding("Volume", "OutputRaw");
|
||||
ASSERT_NE(outputRaw, nullptr);
|
||||
EXPECT_EQ(outputRaw->type, ShaderResourceType::RWRawBuffer);
|
||||
EXPECT_EQ(outputRaw->set, 4u);
|
||||
EXPECT_EQ(outputRaw->binding, 1u);
|
||||
|
||||
delete shader;
|
||||
fs::remove_all(shaderRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, RuntimeShaderSourceRewritesUnityStyleBufferRegisters) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path shaderRoot = fs::temp_directory_path() / "xc_shader_authoring_buffer_runtime_test";
|
||||
const fs::path shaderPath = shaderRoot / "buffer_runtime.shader";
|
||||
|
||||
fs::remove_all(shaderRoot);
|
||||
fs::create_directories(shaderRoot);
|
||||
WriteTextFile(
|
||||
shaderPath,
|
||||
R"(Shader "Test/BufferRuntime"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
{
|
||||
Name "Volume"
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma vertex MainVS
|
||||
#pragma fragment MainPS
|
||||
StructuredBuffer<float4> InputBuffer;
|
||||
ByteAddressBuffer RawInput;
|
||||
RWStructuredBuffer<uint> OutputBuffer;
|
||||
RWByteAddressBuffer OutputRaw;
|
||||
struct VSInput
|
||||
{
|
||||
float3 positionOS : POSITION;
|
||||
};
|
||||
float4 MainVS(VSInput input) : SV_POSITION
|
||||
{
|
||||
return float4(input.positionOS, 1.0);
|
||||
}
|
||||
float4 MainPS() : SV_TARGET
|
||||
{
|
||||
return float4(1.0, 0.5, 0.25, 1.0);
|
||||
}
|
||||
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("Volume");
|
||||
ASSERT_NE(pass, nullptr);
|
||||
|
||||
const ShaderStageVariant* fragmentVariant =
|
||||
shader->FindVariant("Volume", ShaderType::Fragment, ShaderBackend::D3D12);
|
||||
ASSERT_NE(fragmentVariant, nullptr);
|
||||
const std::string d3d12Source =
|
||||
::XCEngine::Rendering::Detail::BuildRuntimeShaderSource(
|
||||
*pass,
|
||||
ShaderBackend::D3D12,
|
||||
*fragmentVariant);
|
||||
EXPECT_NE(
|
||||
d3d12Source.find("StructuredBuffer<float4> InputBuffer: register(t0);"),
|
||||
std::string::npos);
|
||||
EXPECT_NE(
|
||||
d3d12Source.find("ByteAddressBuffer RawInput: register(t1);"),
|
||||
std::string::npos);
|
||||
EXPECT_NE(
|
||||
d3d12Source.find("RWStructuredBuffer<uint> OutputBuffer: register(u0);"),
|
||||
std::string::npos);
|
||||
EXPECT_NE(
|
||||
d3d12Source.find("RWByteAddressBuffer OutputRaw: register(u1);"),
|
||||
std::string::npos);
|
||||
|
||||
const ShaderStageVariant* vulkanFragment =
|
||||
shader->FindVariant("Volume", ShaderType::Fragment, ShaderBackend::Vulkan);
|
||||
ASSERT_NE(vulkanFragment, nullptr);
|
||||
const std::string vulkanSource =
|
||||
::XCEngine::Rendering::Detail::BuildRuntimeShaderSource(
|
||||
*pass,
|
||||
ShaderBackend::Vulkan,
|
||||
*vulkanFragment);
|
||||
EXPECT_NE(
|
||||
vulkanSource.find("StructuredBuffer<float4> InputBuffer: register(t0, space2);"),
|
||||
std::string::npos);
|
||||
EXPECT_NE(
|
||||
vulkanSource.find("ByteAddressBuffer RawInput: register(t1, space2);"),
|
||||
std::string::npos);
|
||||
EXPECT_NE(
|
||||
vulkanSource.find("RWStructuredBuffer<uint> OutputBuffer: register(u0, space4);"),
|
||||
std::string::npos);
|
||||
EXPECT_NE(
|
||||
vulkanSource.find("RWByteAddressBuffer OutputRaw: register(u1, space4);"),
|
||||
std::string::npos);
|
||||
|
||||
delete shader;
|
||||
fs::remove_all(shaderRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, LoadShaderAuthoringParsesPassStateAndFallback) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user