Add Unity-style buffer shader resource support
This commit is contained in:
@@ -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