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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user