HLSL: implement 4 (of 6) structuredbuffer types

This is a partial implemention of structurebuffers supporting:

* structured buffer types of:
*   StructuredBuffer
*   RWStructuredBuffer
*   ByteAddressBuffer
*   RWByteAddressBuffer

* Atomic operations on RWByteAddressBuffer

* Load/Load[234], Store/Store[234], GetDimensions methods (where allowed by type)

* globallycoherent flag

But NOT yet supporting:

* AppendStructuredBuffer / ConsumeStructuredBuffer types
* IncrementCounter/DecrementCounter methods

Please note: the stride returned by GetDimensions is as calculated by glslang for std430,
and may not match other environments in all cases.
This commit is contained in:
steve-lunarg
2017-02-12 17:50:28 -07:00
parent c8aed91524
commit 5da1f038d8
22 changed files with 3993 additions and 37 deletions

View File

@@ -406,6 +406,27 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& node, TIntermNode*& node2)
}
}
TString* blockName = idToken.string;
// For structbuffers, we couldn't create the block type while accepting the
// template type, because we need the identifier name. Now that we have that,
// we can create the buffer type.
// TODO: how to determine this without looking for implicit array sizes?
if (variableType.getBasicType() == EbtBlock) {
const int memberCount = variableType.getStruct()->size();
assert(memberCount > 0);
TType* contentType = (*variableType.getStruct())[memberCount-1].type;
// Set the field name and qualifier from the declaration, now that we know it.
if (contentType->isRuntimeSizedArray()) {
contentType->getQualifier() = variableType.getQualifier();
blockName = nullptr; // this will be an anonymous block...
contentType->setFieldName(*idToken.string); // field name is declaration name
variableType.setTypeName(*idToken.string);
}
}
// Hand off the actual declaration
// TODO: things scoped within an annotation need their own name space;
@@ -414,7 +435,7 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& node, TIntermNode*& node2)
if (typedefDecl)
parseContext.declareTypedef(idToken.loc, *idToken.string, variableType);
else if (variableType.getBasicType() == EbtBlock)
parseContext.declareBlock(idToken.loc, variableType, idToken.string);
parseContext.declareBlock(idToken.loc, variableType, blockName);
else {
if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) {
// this isn't really an individual variable, but a member of the $Global buffer
@@ -533,8 +554,11 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type)
qualifier.layoutFormat = type.getQualifier().layoutFormat;
qualifier.precision = type.getQualifier().precision;
if (type.getQualifier().storage == EvqVaryingOut)
if (type.getQualifier().storage == EvqVaryingOut ||
type.getQualifier().storage == EvqBuffer) {
qualifier.storage = type.getQualifier().storage;
qualifier.readonly = type.getQualifier().readonly;
}
type.getQualifier() = qualifier;
}
@@ -609,6 +633,9 @@ bool HlslGrammar::acceptQualifier(TQualifier& qualifier)
if (! acceptLayoutQualifierList(qualifier))
return false;
continue;
case EHTokGloballyCoherent:
qualifier.coherent = true;
break;
// GS geometries: these are specified on stage input variables, and are an error (not verified here)
// for output variables.
@@ -1246,11 +1273,19 @@ bool HlslGrammar::acceptType(TType& type)
return acceptTextureType(type);
break;
case EHTokAppendStructuredBuffer:
case EHTokByteAddressBuffer:
case EHTokConsumeStructuredBuffer:
case EHTokRWByteAddressBuffer:
case EHTokRWStructuredBuffer:
case EHTokStructuredBuffer:
return acceptStructBufferType(type);
break;
case EHTokStruct:
case EHTokCBuffer:
case EHTokTBuffer:
return acceptStruct(type);
break;
case EHTokIdentifier:
// An identifier could be for a user-defined type.
@@ -1783,6 +1818,93 @@ bool HlslGrammar::acceptStruct(TType& type)
return true;
}
// struct_buffer
// : APPENDSTRUCTUREDBUFFER
// | BYTEADDRESSBUFFER
// | CONSUMESTRUCTUREDBUFFER
// | RWBYTEADDRESSBUFFER
// | RWSTRUCTUREDBUFFER
// | STRUCTUREDBUFFER
bool HlslGrammar::acceptStructBufferType(TType& type)
{
const EHlslTokenClass structBuffType = peek();
// TODO: globallycoherent
bool hasTemplateType = true;
bool readonly = false;
TStorageQualifier storage = EvqBuffer;
switch (structBuffType) {
case EHTokAppendStructuredBuffer:
unimplemented("AppendStructuredBuffer");
return false;
case EHTokByteAddressBuffer:
hasTemplateType = false;
readonly = true;
break;
case EHTokConsumeStructuredBuffer:
unimplemented("ConsumeStructuredBuffer");
return false;
case EHTokRWByteAddressBuffer:
hasTemplateType = false;
break;
case EHTokRWStructuredBuffer:
break;
case EHTokStructuredBuffer:
readonly = true;
break;
default:
return false; // not a structure buffer type
}
advanceToken(); // consume the structure keyword
// type on which this StructedBuffer is templatized. E.g, StructedBuffer<MyStruct> ==> MyStruct
TType* templateType = new TType;
if (hasTemplateType) {
if (! acceptTokenClass(EHTokLeftAngle)) {
expected("left angle bracket");
return false;
}
if (! acceptType(*templateType)) {
expected("type");
return false;
}
if (! acceptTokenClass(EHTokRightAngle)) {
expected("right angle bracket");
return false;
}
} else {
// byte address buffers have no explicit type.
TType uintType(EbtUint, storage);
templateType->shallowCopy(uintType);
}
// Create an unsized array out of that type.
// TODO: does this work if it's already an array type?
TArraySizes unsizedArray;
unsizedArray.addInnerSize(UnsizedArraySize);
templateType->newArraySizes(unsizedArray);
templateType->getQualifier().storage = storage;
templateType->getQualifier().readonly = readonly;
// Create block type. TODO: hidden internal uint member when needed
TTypeList* blockStruct = new TTypeList;
TTypeLoc member = { templateType, token.loc };
blockStruct->push_back(member);
TType blockType(blockStruct, "", templateType->getQualifier());
// It's not until we see the name during declaration that we can set the
// field name. That happens in HlslGrammar::acceptDeclaration.
type.shallowCopy(blockType);
return true;
}
// struct_declaration_list
// : struct_declaration SEMI_COLON struct_declaration SEMI_COLON ...
//