Commit a8d3db6b by John Kessenich Committed by GitHub

Merge pull request #835 from steve-lunarg/sb-counters

HLSL: structuredbuffer counter functionality
parents d5d9ffbd 350b9485
AppendStructuredBuffer<float4> sbuf_a;
ConsumeStructuredBuffer<float4> sbuf_c;
AppendStructuredBuffer<float4> sbuf_unused;
float4 main(uint pos : FOO) : SV_Target0
{
sbuf_a.Append(float4(1,2,3,4));
return sbuf_c.Consume();
}
RWStructuredBuffer<uint4> sbuf_rw_i;
RWStructuredBuffer<uint4> sbuf_rw_d;
RWStructuredBuffer<uint4> sbuf_rw_nocounter; // doesn't use inc or dec
float4 main(uint pos : FOO) : SV_Target0
{
uint4 result = 0;
sbuf_rw_i[7];
sbuf_rw_d[7];
sbuf_rw_nocounter[5] = 2;
uint c1 = sbuf_rw_i.IncrementCounter();
uint c2 = sbuf_rw_d.DecrementCounter();
return float4(result.x, result.y, c1, c2);
}
...@@ -223,6 +223,13 @@ enum TBuiltInVariable { ...@@ -223,6 +223,13 @@ enum TBuiltInVariable {
EbvOutputPatch, EbvOutputPatch,
EbvInputPatch, EbvInputPatch,
// structbuffer types
EbvAppendConsume, // no need to differentiate append and consume
EbvRWStructuredBuffer,
EbvStructuredBuffer,
EbvByteAddressBuffer,
EbvRWByteAddressBuffer,
EbvLast EbvLast
}; };
......
...@@ -633,6 +633,10 @@ enum TOperator { ...@@ -633,6 +633,10 @@ enum TOperator {
EOpMethodStore2, // ... EOpMethodStore2, // ...
EOpMethodStore3, // ... EOpMethodStore3, // ...
EOpMethodStore4, // ... EOpMethodStore4, // ...
EOpMethodIncrementCounter, // ...
EOpMethodDecrementCounter, // ...
// EOpMethodAppend is defined for geo shaders below
EOpMethodConsume,
// SM5 texture methods // SM5 texture methods
EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about
......
...@@ -1778,6 +1778,7 @@ const char* TProgram::getUniformBlockName(int index) const { return reflection ...@@ -1778,6 +1778,7 @@ const char* TProgram::getUniformBlockName(int index) const { return reflection
int TProgram::getUniformBlockSize(int index) const { return reflection->getUniformBlock(index).size; } int TProgram::getUniformBlockSize(int index) const { return reflection->getUniformBlock(index).size; }
int TProgram::getUniformIndex(const char* name) const { return reflection->getIndex(name); } int TProgram::getUniformIndex(const char* name) const { return reflection->getIndex(name); }
int TProgram::getUniformBlockIndex(int index) const { return reflection->getUniform(index).index; } int TProgram::getUniformBlockIndex(int index) const { return reflection->getUniform(index).index; }
int TProgram::getUniformBlockCounterIndex(int index) const { return reflection->getUniformBlock(index).counterIndex; }
int TProgram::getUniformType(int index) const { return reflection->getUniform(index).glDefineType; } int TProgram::getUniformType(int index) const { return reflection->getUniform(index).glDefineType; }
int TProgram::getUniformBufferOffset(int index) const { return reflection->getUniform(index).offset; } int TProgram::getUniformBufferOffset(int index) const { return reflection->getUniform(index).offset; }
int TProgram::getUniformArraySize(int index) const { return reflection->getUniform(index).size; } int TProgram::getUniformArraySize(int index) const { return reflection->getUniform(index).size; }
......
...@@ -707,6 +707,19 @@ void TReflection::buildAttributeReflection(EShLanguage stage, const TIntermediat ...@@ -707,6 +707,19 @@ void TReflection::buildAttributeReflection(EShLanguage stage, const TIntermediat
} }
} }
// build counter block index associations for buffers
void TReflection::buildCounterIndices()
{
// search for ones that have counters
for (int i = 0; i < int(indexToUniformBlock.size()); ++i) {
const TString counterName(indexToUniformBlock[i].name + "@count");
const int index = getIndex(counterName);
if (index >= 0)
indexToUniformBlock[i].counterIndex = index;
}
}
// Merge live symbols from 'intermediate' into the existing reflection database. // Merge live symbols from 'intermediate' into the existing reflection database.
// //
// Returns false if the input is too malformed to do this. // Returns false if the input is too malformed to do this.
...@@ -729,6 +742,8 @@ bool TReflection::addStage(EShLanguage stage, const TIntermediate& intermediate) ...@@ -729,6 +742,8 @@ bool TReflection::addStage(EShLanguage stage, const TIntermediate& intermediate)
function->traverse(&it); function->traverse(&it);
} }
buildCounterIndices();
return true; return true;
} }
......
...@@ -57,11 +57,16 @@ class TObjectReflection { ...@@ -57,11 +57,16 @@ class TObjectReflection {
public: public:
TObjectReflection(const TString& pName, const TType& pType, int pOffset, int pGLDefineType, int pSize, int pIndex) : TObjectReflection(const TString& pName, const TType& pType, int pOffset, int pGLDefineType, int pSize, int pIndex) :
name(pName), offset(pOffset), name(pName), offset(pOffset),
glDefineType(pGLDefineType), size(pSize), index(pIndex), type(pType.clone()) { } glDefineType(pGLDefineType), size(pSize), index(pIndex), counterIndex(-1), type(pType.clone()) { }
void dump() const { void dump() const {
printf("%s: offset %d, type %x, size %d, index %d, binding %d\n", printf("%s: offset %d, type %x, size %d, index %d, binding %d",
name.c_str(), offset, glDefineType, size, index, getBinding() ); name.c_str(), offset, glDefineType, size, index, getBinding() );
if (counterIndex != -1)
printf(", counter %d", counterIndex);
printf("\n");
} }
const TType* const getType() const { return type; } const TType* const getType() const { return type; }
...@@ -71,6 +76,7 @@ public: ...@@ -71,6 +76,7 @@ public:
int glDefineType; int glDefineType;
int size; // data size in bytes for a block, array size for a (non-block) object that's an array int size; // data size in bytes for a block, array size for a (non-block) object that's an array
int index; int index;
int counterIndex;
static TObjectReflection badReflection() { return TObjectReflection(); } static TObjectReflection badReflection() { return TObjectReflection(); }
...@@ -140,6 +146,9 @@ public: ...@@ -140,6 +146,9 @@ public:
return it->second; return it->second;
} }
// see getIndex(const char*)
int getIndex(const TString& name) const { return getIndex(name.c_str()); }
// Thread local size // Thread local size
unsigned getLocalSize(int dim) const { return dim <= 2 ? localSize[dim] : 0; } unsigned getLocalSize(int dim) const { return dim <= 2 ? localSize[dim] : 0; }
...@@ -148,6 +157,7 @@ public: ...@@ -148,6 +157,7 @@ public:
protected: protected:
friend class glslang::TReflectionTraverser; friend class glslang::TReflectionTraverser;
void buildCounterIndices();
void buildAttributeReflection(EShLanguage, const TIntermediate&); void buildAttributeReflection(EShLanguage, const TIntermediate&);
// Need a TString hash: typedef std::unordered_map<TString, int> TNameToIndex; // Need a TString hash: typedef std::unordered_map<TString, int> TNameToIndex;
......
...@@ -518,6 +518,7 @@ public: ...@@ -518,6 +518,7 @@ public:
int getUniformBlockSize(int blockIndex) const; // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE) int getUniformBlockSize(int blockIndex) const; // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE)
int getUniformIndex(const char* name) const; // can be used for glGetUniformIndices() int getUniformIndex(const char* name) const; // can be used for glGetUniformIndices()
int getUniformBlockIndex(int index) const; // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX) int getUniformBlockIndex(int index) const; // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX)
int getUniformBlockCounterIndex(int index) const; // returns block index of associated counter.
int getUniformType(int index) const; // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE) int getUniformType(int index) const; // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE)
int getUniformBufferOffset(int index) const; // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET) int getUniformBufferOffset(int index) const; // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET)
int getUniformArraySize(int index) const; // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE) int getUniformArraySize(int index) const; // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE)
......
...@@ -245,9 +245,11 @@ INSTANTIATE_TEST_CASE_P( ...@@ -245,9 +245,11 @@ INSTANTIATE_TEST_CASE_P(
{"hlsl.structarray.flatten.frag", "main"}, {"hlsl.structarray.flatten.frag", "main"},
{"hlsl.structarray.flatten.geom", "main"}, {"hlsl.structarray.flatten.geom", "main"},
{"hlsl.structbuffer.frag", "main"}, {"hlsl.structbuffer.frag", "main"},
{"hlsl.structbuffer.append.frag", "main"},
{"hlsl.structbuffer.atomics.frag", "main"}, {"hlsl.structbuffer.atomics.frag", "main"},
{"hlsl.structbuffer.byte.frag", "main"}, {"hlsl.structbuffer.byte.frag", "main"},
{"hlsl.structbuffer.coherent.frag", "main"}, {"hlsl.structbuffer.coherent.frag", "main"},
{"hlsl.structbuffer.incdec.frag", "main"},
{"hlsl.structbuffer.fn.frag", "main"}, {"hlsl.structbuffer.fn.frag", "main"},
{"hlsl.structbuffer.rw.frag", "main"}, {"hlsl.structbuffer.rw.frag", "main"},
{"hlsl.structbuffer.rwbyte.frag", "main"}, {"hlsl.structbuffer.rwbyte.frag", "main"},
......
...@@ -475,9 +475,10 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) ...@@ -475,9 +475,10 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) { if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) {
if (typedefDecl) if (typedefDecl)
parseContext.declareTypedef(idToken.loc, *fullName, variableType); parseContext.declareTypedef(idToken.loc, *fullName, variableType);
else if (variableType.getBasicType() == EbtBlock) else if (variableType.getBasicType() == EbtBlock) {
parseContext.declareBlock(idToken.loc, variableType, fullName); parseContext.declareBlock(idToken.loc, variableType, fullName);
else { parseContext.declareStructBufferCounter(idToken.loc, variableType, *fullName);
} else {
if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) { if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) {
// this isn't really an individual variable, but a member of the $Global buffer // this isn't really an individual variable, but a member of the $Global buffer
parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName); parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName);
...@@ -1955,24 +1956,29 @@ bool HlslGrammar::acceptStructBufferType(TType& type) ...@@ -1955,24 +1956,29 @@ bool HlslGrammar::acceptStructBufferType(TType& type)
bool readonly = false; bool readonly = false;
TStorageQualifier storage = EvqBuffer; TStorageQualifier storage = EvqBuffer;
TBuiltInVariable builtinType = EbvNone;
switch (structBuffType) { switch (structBuffType) {
case EHTokAppendStructuredBuffer: case EHTokAppendStructuredBuffer:
unimplemented("AppendStructuredBuffer"); builtinType = EbvAppendConsume;
return false; break;
case EHTokByteAddressBuffer: case EHTokByteAddressBuffer:
hasTemplateType = false; hasTemplateType = false;
readonly = true; readonly = true;
builtinType = EbvByteAddressBuffer;
break; break;
case EHTokConsumeStructuredBuffer: case EHTokConsumeStructuredBuffer:
unimplemented("ConsumeStructuredBuffer"); builtinType = EbvAppendConsume;
return false; break;
case EHTokRWByteAddressBuffer: case EHTokRWByteAddressBuffer:
hasTemplateType = false; hasTemplateType = false;
builtinType = EbvRWByteAddressBuffer;
break; break;
case EHTokRWStructuredBuffer: case EHTokRWStructuredBuffer:
builtinType = EbvRWStructuredBuffer;
break; break;
case EHTokStructuredBuffer: case EHTokStructuredBuffer:
builtinType = EbvStructuredBuffer;
readonly = true; readonly = true;
break; break;
default: default:
...@@ -2014,8 +2020,6 @@ bool HlslGrammar::acceptStructBufferType(TType& type) ...@@ -2014,8 +2020,6 @@ bool HlslGrammar::acceptStructBufferType(TType& type)
// field name is canonical for all structbuffers // field name is canonical for all structbuffers
templateType->setFieldName("@data"); templateType->setFieldName("@data");
// Create block type. TODO: hidden internal uint member when needed
TTypeList* blockStruct = new TTypeList; TTypeList* blockStruct = new TTypeList;
TTypeLoc member = { templateType, token.loc }; TTypeLoc member = { templateType, token.loc };
blockStruct->push_back(member); blockStruct->push_back(member);
...@@ -2025,6 +2029,7 @@ bool HlslGrammar::acceptStructBufferType(TType& type) ...@@ -2025,6 +2029,7 @@ bool HlslGrammar::acceptStructBufferType(TType& type)
blockType.getQualifier().storage = storage; blockType.getQualifier().storage = storage;
blockType.getQualifier().readonly = readonly; blockType.getQualifier().readonly = readonly;
blockType.getQualifier().builtIn = builtinType;
// We may have created an equivalent type before, in which case we should use its // We may have created an equivalent type before, in which case we should use its
// deep structure. // deep structure.
......
...@@ -146,6 +146,7 @@ public: ...@@ -146,6 +146,7 @@ public:
TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&); TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&);
TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0, TArraySizes* arraySizes = 0); void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
void declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name);
void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation); void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
void fixBlockXfbOffsets(TQualifier&, TTypeList&); void fixBlockXfbOffsets(TQualifier&, TTypeList&);
void fixBlockUniformOffsets(const TQualifier&, TTypeList&); void fixBlockUniformOffsets(const TQualifier&, TTypeList&);
...@@ -274,11 +275,19 @@ protected: ...@@ -274,11 +275,19 @@ protected:
TType* getStructBufferContentType(const TType& type) const; TType* getStructBufferContentType(const TType& type) const;
bool isStructBufferType(const TType& type) const { return getStructBufferContentType(type) != nullptr; } bool isStructBufferType(const TType& type) const { return getStructBufferContentType(type) != nullptr; }
TIntermTyped* indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const; TIntermTyped* indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const;
TIntermTyped* getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer);
// Return true if this type is a reference. This is not currently a type method in case that's // Return true if this type is a reference. This is not currently a type method in case that's
// a language specific answer. // a language specific answer.
bool isReference(const TType& type) const { return isStructBufferType(type); } bool isReference(const TType& type) const { return isStructBufferType(type); }
// Return true if this a buffer type that has an associated counter buffer.
bool hasStructBuffCounter(const TString& name) const;
// Finalization step: remove unused buffer blocks from linkage (we don't know until the
// shader is entirely compiled)
void removeUnusedStructBufferCounters();
// Pass through to base class after remembering builtin mappings. // Pass through to base class after remembering builtin mappings.
using TParseContextBase::trackLinkage; using TParseContextBase::trackLinkage;
void trackLinkage(TSymbol& variable) override; void trackLinkage(TSymbol& variable) override;
...@@ -367,6 +376,9 @@ protected: ...@@ -367,6 +376,9 @@ protected:
// Structuredbuffer shared types. Typically there are only a few. // Structuredbuffer shared types. Typically there are only a few.
TVector<TType*> structBufferTypes; TVector<TType*> structBufferTypes;
TMap<TString, TBuiltInVariable> structBufferBuiltIn;
TMap<TString, bool> structBufferCounter;
// The builtin interstage IO map considers e.g, EvqPosition on input and output separately, so that we // The builtin interstage IO map considers e.g, EvqPosition on input and output separately, so that we
// can build the linkage correctly if position appears on both sides. Otherwise, multiple positions // can build the linkage correctly if position appears on both sides. Otherwise, multiple positions
// are considered identical. // are considered identical.
......
...@@ -871,6 +871,9 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c ...@@ -871,6 +871,9 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
{ "InterlockedMin", nullptr, nullptr, "-", "-", EShLangAll, true }, { "InterlockedMin", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "InterlockedOr", nullptr, nullptr, "-", "-", EShLangAll, true }, { "InterlockedOr", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "InterlockedXor", nullptr, nullptr, "-", "-", EShLangAll, true }, { "InterlockedXor", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "IncrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "DecrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true },
{ "Consume", nullptr, nullptr, "-", "-", EShLangAll, true },
// Mark end of list, since we want to avoid a range-based for, as some compilers don't handle it yet. // Mark end of list, since we want to avoid a range-based for, as some compilers don't handle it yet.
{ nullptr, nullptr, nullptr, nullptr, nullptr, 0, false }, { nullptr, nullptr, nullptr, nullptr, nullptr, 0, false },
...@@ -1180,6 +1183,10 @@ void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profil ...@@ -1180,6 +1183,10 @@ void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profil
symbolTable.relateToOperator(BUILTIN_PREFIX "Store2", EOpMethodStore2); symbolTable.relateToOperator(BUILTIN_PREFIX "Store2", EOpMethodStore2);
symbolTable.relateToOperator(BUILTIN_PREFIX "Store3", EOpMethodStore3); symbolTable.relateToOperator(BUILTIN_PREFIX "Store3", EOpMethodStore3);
symbolTable.relateToOperator(BUILTIN_PREFIX "Store4", EOpMethodStore4); symbolTable.relateToOperator(BUILTIN_PREFIX "Store4", EOpMethodStore4);
symbolTable.relateToOperator(BUILTIN_PREFIX "IncrementCounter", EOpMethodIncrementCounter);
symbolTable.relateToOperator(BUILTIN_PREFIX "DecrementCounter", EOpMethodDecrementCounter);
// Append is also a GS method: we don't add it twice
symbolTable.relateToOperator(BUILTIN_PREFIX "Consume", EOpMethodConsume);
symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAdd", EOpInterlockedAdd); symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAdd", EOpInterlockedAdd);
symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAnd", EOpInterlockedAnd); symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAnd", EOpInterlockedAnd);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment