Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple unbounded arrays within a single SRG #25

Open
wants to merge 1 commit into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ bin/*
src/generated/*.interp
src/generated/*.tokens
src/generated/java/
src/dist
.vs
build
/src/.antlr/azslLexer.interp
Expand All @@ -14,4 +15,4 @@ build
/src/.antlr/azslParser.interp
/src/.antlr/azslParser.java
/src/.antlr/azslParser.tokens
/tests/testfuncs.pyc
/tests/testfuncs.pyc
6 changes: 4 additions & 2 deletions Platform/Windows/src/DirectX12PlatformEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,14 @@ namespace AZ::ShaderCompiler
{
if (param.m_type != RootParamType::SrgConstantCB && param.m_type != RootParamType::RootConstantCB) // already treated in the previous loop
{
// In DX12, unbounded arrays are assigned their own space instead of using the space assigned to the SRG
int space = param.m_isUnboundedArray ? param.m_spillSpace : param.m_registerBinding.m_pair[querySet].m_logicalSpace;

std::stringstream rootParam;
rootParam << RootParamType::ToStr(param.m_type)
<< "(" << ToLower(BindingType::ToStr(RootParamTypeToBindingType(param.m_type))); // eg "CBV(b" or "SRV(t" or "Sampler(s"
rootParam << std::to_string(param.m_registerBinding.m_pair[querySet].m_registerIndex)
<< ", space = " << std::to_string(param.m_registerBinding.m_pair[querySet].m_logicalSpace)
<< ", space = " << std::to_string(space)
<< ", numDescriptors = " << std::to_string(param.m_registerRange) << ")";

const auto* memberInfo = codeEmitter.GetIR()->GetSymbolSubAs<VarInfo>(param.m_uid.m_name);
Expand Down Expand Up @@ -125,5 +128,4 @@ namespace AZ::ShaderCompiler

return Decorate("#define sig ", Join(rootAttrList.begin(), rootAttrList.end(), ", \" \\\n"), "\"\n\n");
}

}
2 changes: 2 additions & 0 deletions Platform/Windows/src/DirectX12PlatformEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ namespace AZ::ShaderCompiler
[[nodiscard]]
string GetRootSig(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const override final;

bool UnboundedArraysUseSpillSpace() const override { return true; }

private:
DirectX12PlatformEmitter() : PlatformEmitter {} {};
};
Expand Down
28 changes: 24 additions & 4 deletions src/AzslcBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,17 @@ namespace AZ::ShaderCompiler
}
}

void MultiBindingLocationMaker::SignalRegisterIncrement(BindingType regType, int count /* = 1*/)
void MultiBindingLocationMaker::SignalRegisterIncrement(BindingType regType, int count, bool isUnbounded)
{
m_untainted.m_registerPos[regType] += count;
m_merged.m_registerPos[regType] += count;

if (isUnbounded)
{
m_untainted.m_unboundedSpillSpace = m_unboundedSpillSpace;
m_merged.m_unboundedSpillSpace = m_unboundedSpillSpace;
++m_unboundedSpillSpace;
}
}

BindingPair MultiBindingLocationMaker::GetCurrent(BindingType regType)
Expand Down Expand Up @@ -573,9 +580,19 @@ namespace AZ::ShaderCompiler
}

auto regType = RootParamTypeToBindingType(paramType);
auto srgElementDesc = RootSigDesc::SrgParamDesc{ id, paramType, bindInfo.GetCurrent(regType), count, -1, isUnboundedArray};

auto srgElementDesc = RootSigDesc::SrgParamDesc{
id,
paramType,
bindInfo.GetCurrent(regType),
count,
-1,
bindInfo.m_unboundedSpillSpace,
isUnboundedArray };

rootSig.m_descriptorMap.emplace(id, srgElementDesc);
bindInfo.SignalRegisterIncrement(regType, count);
bindInfo.SignalRegisterIncrement(regType, count, isUnboundedArray);

return srgElementDesc;
}

Expand All @@ -588,7 +605,10 @@ namespace AZ::ShaderCompiler

RootSigDesc Backend::BuildSignatureDescription(const Options& options, int num32BitConst) const
{
MultiBindingLocationMaker bindInfo{ options };
// We start at an arbitrarily high number to avoid conflicts with spaces occupied by any user declarations
m_unboundedSpillSpace = 1000;
Comment on lines +608 to +609
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that m_unboundedSpillSpace is a mutable member, so it can be assigned here in a const function. Why can't this just be a local variable instead? Is it used after the BuildSignatureDescription function closes? If so, please add a code comment about what's going on, as this is unexpected.


MultiBindingLocationMaker bindInfo{ options, m_unboundedSpillSpace };
RootSigDesc rootSig;

auto allSrgs = m_ir->m_symbols.GetOrderedSymbolsOfSubType_2<SRGInfo>();
Expand Down
23 changes: 20 additions & 3 deletions src/AzslcBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ namespace AZ::ShaderCompiler
BindingPair m_registerBinding;
int m_registerRange = -1;
int m_num32BitConstants = -1;

int m_spillSpace = -1;

// This flag is added so m_registerRange can take the value
// of 1 and at the same time We do not forget that m_uid refers
// to an unbounded array.
Expand Down Expand Up @@ -98,6 +101,10 @@ namespace AZ::ShaderCompiler
int GetAccumulated(BindingType r) const;

int m_space = 0; //<! logical space

// See: MultiBindingLocationMarker::m_unboundedSpillSpace
int m_unboundedSpillSpace;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's initialize this just to be safe.


int m_registerPos[BindingType::EndEnumeratorSentinel_] = {0}; //!< register index, one by type.
int m_accumulated[BindingType::EndEnumeratorSentinel_] = {0}; //!< Counter for total usage independently from space increments
int m_accumulatedUnused[BindingType::EndEnumeratorSentinel_] = {0}; //!< Counter for holes created by indices unification
Expand All @@ -109,21 +116,28 @@ namespace AZ::ShaderCompiler
class MultiBindingLocationMaker
{
public:
MultiBindingLocationMaker(const Options& options)
: m_options(options)
MultiBindingLocationMaker(const Options& options, int& unboundedSpillSpace)
: m_options{ options }
, m_unboundedSpillSpace{ unboundedSpillSpace }
Comment on lines +119 to +121
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer unboundedSpillSpace be a pointer instead of a reference, to make it obvious what's going on at the call site. Calling "MultiBindingLocationMaker(options, &unboundedSpillSpaceValue)" is a more clear than calling "MultiBindingLocationMaker(options, unboundedSpillSpaceValue)"

{}

void SignalIncrementSpace(std::function<void(int, int)> warningMessageFunctionForMinDescOvershoot);

void SignalUnifyIndices();

void SignalRegisterIncrement(BindingType regType, int count = 1);
void SignalRegisterIncrement(BindingType regType, int count, bool isUnbounded);

BindingPair GetCurrent(BindingType regType);

SingleBindingLocationTracker m_untainted;
SingleBindingLocationTracker m_merged;
Options m_options;

// On some platforms (DX12), descriptor arrays occupy an individual register slot, and spaces are used
// to prevent overlapping ranges. When an unbounded array is encountered, we immediately assign it to
// the value of this member variable and increment. This is initialized in the constructor because the
// space we spill to must not collide with any other SRG declared in the shader.
int& m_unboundedSpillSpace;
};

//! This class intends to be a base umbrella for compiler back-end services.
Expand Down Expand Up @@ -174,6 +188,9 @@ namespace AZ::ShaderCompiler
std::ostream& m_out;
IntermediateRepresentation* m_ir;
TokenStream* m_tokens;

// See MultiBindingLocationMaker::m_unboundedSpillSpace
mutable int m_unboundedSpillSpace;
};

// independent utility functions
Expand Down
20 changes: 17 additions & 3 deletions src/AzslcEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ namespace AZ::ShaderCompiler
// note: instead of redoing this work ad-hoc, EmitText could be used directly on the ext type.
const auto genericType = "<" + GetTranslatedName(varInfo->m_typeInfoExt.m_genericParameter, UsageContext::ReferenceSite) + ">";

const string spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace) : "";
const string spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(ResolveBindingSpace(bindInfo, bindSet)) : "";
m_out << "ConstantBuffer " << genericType << " " << cbName;
if (bindInfo.m_isUnboundedArray)
{
Expand All @@ -1066,7 +1066,7 @@ namespace AZ::ShaderCompiler

EmitEmptyLinesToLineNumber(varInfo->GetOriginalLineNumber());

const string spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace) : "";
const string spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(ResolveBindingSpace(bindInfo, bindSet)) : "";
m_out << (varInfo->m_samplerState->m_isComparison ? "SamplerComparisonState " : "SamplerState ")
<< ReplaceSeparators(sId.m_name, Underscore);
if (bindInfo.m_isUnboundedArray)
Expand Down Expand Up @@ -1112,9 +1112,10 @@ namespace AZ::ShaderCompiler
string varType = GetTranslatedName(varInfo->m_typeInfoExt, UsageContext::DeclarationSite);
auto registerTypeLetter = ToLower(BindingType::ToStr(RootParamTypeToBindingType(bindInfo.m_type)));
optional<string> stringifiedLogicalSpace;

if (options.m_useLogicalSpaces)
{
stringifiedLogicalSpace = std::to_string(bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace);
stringifiedLogicalSpace = std::to_string(ResolveBindingSpace(bindInfo, bindSet));
}

EmitEmptyLinesToLineNumber(varInfo->GetOriginalLineNumber());
Expand Down Expand Up @@ -1363,4 +1364,17 @@ namespace AZ::ShaderCompiler
m_out << "\n";
}
}

int CodeEmitter::ResolveBindingSpace(const RootSigDesc::SrgParamDesc& bindInfo, BindingPair::Set bindSet) const
{
if (bindInfo.m_isUnboundedArray && GetPlatformEmitter().UnboundedArraysUseSpillSpace())
{
return bindInfo.m_spillSpace;
}
else
{
return bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace;
}
}

}
3 changes: 3 additions & 0 deletions src/AzslcEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,5 +228,8 @@ namespace AZ::ShaderCompiler
// whether We are using a regular std::ostream or an instance of NewLineCounterStream.
template <class StreamLike>
void GetTextInStreamInternal(misc::Interval interval, StreamLike& output, bool emitNewLines) const;

// Given an SRG parameter, determines the space it belongs to based on the platform
int ResolveBindingSpace(const RootSigDesc::SrgParamDesc& bindInfo, BindingPair::Set bindSet) const;
};
}
7 changes: 0 additions & 7 deletions src/AzslcMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,13 +526,6 @@ int main(int argc, const char* argv[])
std::for_each(namespaces.begin(), namespaces.end(),
[&](const string& space) { ir.AddAttributeNamespaceFilter(space); });

UnboundedArraysValidator::Options unboundedArraysValidationOptions = { useSpaces, uniqueIdx };
if (*maxSpacesOpt)
{
unboundedArraysValidationOptions.m_maxSpaces = maxSpaces;
}
ir.m_sema.m_unboundedArraysValidator.SetOptions(unboundedArraysValidationOptions);

// semantic logic and validation
walker.walk(&semanticListener, tree);

Expand Down
2 changes: 2 additions & 0 deletions src/AzslcPlatformEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,7 @@ namespace AZ::ShaderCompiler
//! Aligns the size for the data containing the root constants.
//! @param size The size of stride
virtual uint32_t AlignRootConstants(uint32_t size) const;

virtual bool UnboundedArraysUseSpillSpace() const { return false; }
};
}
19 changes: 15 additions & 4 deletions src/AzslcReflection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,13 +631,23 @@ namespace AZ::ShaderCompiler
m_out << GetVariantList(options);
}

static void ReflectBinding(Json::Value& output, const RootSigDesc::SrgParamDesc& bindInfo)
void CodeReflection::ReflectBinding(Json::Value& output, const RootSigDesc::SrgParamDesc& bindInfo) const
{
output["count"] = (bindInfo.m_isUnboundedArray) ? -1 : bindInfo.m_registerRange;
output["index"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Untainted].m_registerIndex;
output["space"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Untainted].m_logicalSpace;
output["index-merged"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Merged].m_registerIndex;
output["space-merged"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Merged].m_logicalSpace;

if (bindInfo.m_isUnboundedArray && GetPlatformEmitter().UnboundedArraysUseSpillSpace())
{
output["space"] = bindInfo.m_spillSpace;
output["space-merged"] = bindInfo.m_spillSpace;
}
else
{
output["space"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Untainted].m_logicalSpace;
output["space-merged"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Merged].m_logicalSpace;
}

}

void CodeReflection::DumpSRGLayout(const Options& options) const
Expand Down Expand Up @@ -724,6 +734,7 @@ namespace AZ::ShaderCompiler
dataView["id"] = ExtractLeaf(tId.m_name).data();
dataView["type"] = viewName;
dataView["usage"] = (isReadWriteView) ? "ReadWrite" : "Read";

ReflectBinding(dataView, bindInfo);
dataView["stride"] = strideSize;

Expand Down Expand Up @@ -946,7 +957,7 @@ namespace AZ::ShaderCompiler
return allEntriesJson;
};

auto makeJsonNodeForOneResource = [&dependencyListToJson](const set<IdentifierUID>& dependencyList,
auto makeJsonNodeForOneResource = [this, &dependencyListToJson](const set<IdentifierUID>& dependencyList,
const RootSigDesc::SrgParamDesc& binding,
const Json::Value& allConstants)
{
Expand Down
2 changes: 2 additions & 0 deletions src/AzslcReflection.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ namespace AZ::ShaderCompiler
void DumpResourceBindingDependencies(const Options& options) const;

private:
// Serialize a binding to Json
void ReflectBinding(Json::Value& output, const RootSigDesc::SrgParamDesc& bindInfo) const;

//! Builds member variable packing information and adds it to the membersContainer
uint32_t BuildMemberLayout(Json::Value& membersContainer, string_view namePrefix, const IdentifierUID& memberId, const bool isArrayItr, const Options& options, const AZ::ShaderCompiler::Packing::Layout layoutPacking, uint32_t& offset) const;
Expand Down
6 changes: 0 additions & 6 deletions src/AzslcSemanticOrchestrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -690,12 +690,6 @@ namespace AZ::ShaderCompiler
TypeClass typeClass = varInfo.GetTypeClass();
assert(typeClass != TypeClass::Alias);

string errorMessage;
if (!m_unboundedArraysValidator.CheckFieldCanBeAddedToSrg(isUnboundedArray, srgUid, srgInfo, varUid, varInfo, typeClass, &errorMessage))
{
ThrowAzslcOrchestratorException(ORCHESTRATOR_UNBOUNDED_RESOURCE_ISSUE, ctx->start, errorMessage);
}

if (typeClass == TypeClass::ConstantBuffer)
{
srgInfo.m_CBs.push_back(varUid);
Expand Down
2 changes: 0 additions & 2 deletions src/AzslcSemanticOrchestrator.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

#include "AzslcScopeTracker.h"
#include "PreprocessorLineDirectiveFinder.h"
#include "AzslcUnboundedArraysValidator.h"

namespace AZ::ShaderCompiler
{
Expand Down Expand Up @@ -423,7 +422,6 @@ namespace AZ::ShaderCompiler
ScopeTracker* m_scope;
azslLexer* m_lexer;
PreprocessorLineDirectiveFinder* m_preprocessorLineDirectiveFinder;
UnboundedArraysValidator m_unboundedArraysValidator;

//! cached property informing of the presence of at least one input attachment use.
bool m_subpassInputSeen = false;
Expand Down
24 changes: 24 additions & 0 deletions tests/Samples/UnboundedArrays2.azsl_manual
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
ShaderResourceGroupSemantic slot1
{
FrequencyId = 1;
};

struct MyStruct
{
float4 m_a;
float4 m_b;
};


ShaderResourceGroup SRG1 : slot1
{

Texture2D<float4> m_texSRVa[]; // t0+, space1000
Texture2D<float4> m_texSRVb[]; // t0+, space1001
RWTexture2D<float4> m_texUAVa[]; // u0+, space1002
RWTexture2D<float4> m_texUAVb[]; // u0+, space1003
Sampler m_samplera[]; // s0+, space1004
Sampler m_samplerb[]; // s0+, space1005
ConstantBuffer<MyStruct> m_structArraya[]; // b0+, space1006
ConstantBuffer<MyStruct> m_structArrayb[]; // b0+, space1007
Comment on lines +16 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an Emission test instead, so we can check the appropriate spaces are used in the generated output?

};