HLSL: phase 1: add RWTexture and RWBuffer

There's a lot to do for RWTexture and RWBuffer, so it will be broken up into
several PRs.  This is #1.

This adds RWTexture and RWBuffer support, with the following limitations:
  * Only 4 component formats supported
  * No operator[] yet

Those will be added in other PRs.

This PR supports declarations and the Load & GetDimensions methods.  New tests are
added.
This commit is contained in:
steve-lunarg 2016-10-04 16:58:14 -06:00
parent 9065ed83b8
commit bb0183f817
14 changed files with 2483 additions and 39 deletions

View file

@ -460,8 +460,14 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type)
// further, it can create an anonymous instance of the block
if (peekTokenClass(EHTokSemicolon))
parseContext.declareBlock(loc, type);
} else
} else {
// Some qualifiers are set when parsing the type. Merge those with
// whatever comes from acceptQualifier.
assert(qualifier.layoutFormat == ElfNone);
qualifier.layoutFormat = type.getQualifier().layoutFormat;
type.getQualifier() = qualifier;
}
return true;
}
@ -827,6 +833,13 @@ bool HlslGrammar::acceptSamplerType(TType& type)
// | TEXTURECUBEARRAY
// | TEXTURE2DMS
// | TEXTURE2DMSARRAY
// | RWBUFFER
// | RWTEXTURE1D
// | RWTEXTURE1DARRAY
// | RWTEXTURE2D
// | RWTEXTURE2DARRAY
// | RWTEXTURE3D
bool HlslGrammar::acceptTextureType(TType& type)
{
const EHlslTokenClass textureType = peek();
@ -834,6 +847,7 @@ bool HlslGrammar::acceptTextureType(TType& type)
TSamplerDim dim = EsdNone;
bool array = false;
bool ms = false;
bool image = false;
switch (textureType) {
case EHTokBuffer: dim = EsdBuffer; break;
@ -846,6 +860,12 @@ bool HlslGrammar::acceptTextureType(TType& type)
case EHTokTextureCubearray: dim = EsdCube; array = true; break;
case EHTokTexture2DMS: dim = Esd2D; ms = true; break;
case EHTokTexture2DMSarray: dim = Esd2D; array = true; ms = true; break;
case EHTokRWBuffer: dim = EsdBuffer; image=true; break;
case EHTokRWTexture1d: dim = Esd1D; array=false; image=true; break;
case EHTokRWTexture1darray: dim = Esd1D; array=true; image=true; break;
case EHTokRWTexture2d: dim = Esd2D; array=false; image=true; break;
case EHTokRWTexture2darray: dim = Esd2D; array=true; image=true; break;
case EHTokRWTexture3d: dim = Esd3D; array=false; image=true; break;
default:
return false; // not a texture declaration
}
@ -856,7 +876,7 @@ bool HlslGrammar::acceptTextureType(TType& type)
TIntermTyped* msCount = nullptr;
// texture type: required for multisample types!
// texture type: required for multisample types and RWBuffer/RWTextures!
if (acceptTokenClass(EHTokLeftAngle)) {
if (! acceptType(txType)) {
expected("scalar or vector type");
@ -911,22 +931,45 @@ bool HlslGrammar::acceptTextureType(TType& type)
} else if (ms) {
expected("texture type for multisample");
return false;
} else if (image) {
expected("type for RWTexture/RWBuffer");
return false;
}
TArraySizes* arraySizes = nullptr;
const bool shadow = txType.isScalar() || (txType.isVector() && txType.getVectorSize() == 1);
const bool shadow = !image && (txType.isScalar() || (txType.isVector() && txType.getVectorSize() == 1));
TSampler sampler;
TLayoutFormat format = ElfNone;
// Buffers are combined.
if (dim == EsdBuffer) {
// RWBuffer and RWTexture (images) require a TLayoutFormat. We handle only a limit set.
if (image) {
if (txType.getVectorSize() != 4)
expected("4 component image");
switch (txType.getBasicType()) {
case EbtFloat: format = ElfRgba32f; break;
case EbtInt: format = ElfRgba32i; break;
case EbtUint: format = ElfRgba32ui; break;
default:
expected("unknown basic type in image format");
}
}
// Non-image Buffers are combined
if (dim == EsdBuffer && !image) {
sampler.set(txType.getBasicType(), dim, array);
} else {
// DX10 textures are separated. TODO: DX9.
sampler.setTexture(txType.getBasicType(), dim, array, shadow, ms);
if (image) {
sampler.setImage(txType.getBasicType(), dim, array, shadow, ms);
} else {
sampler.setTexture(txType.getBasicType(), dim, array, shadow, ms);
}
}
type.shallowCopy(TType(sampler, EvqUniform, arraySizes));
type.getQualifier().layoutFormat = format;
return true;
}
@ -966,6 +1009,12 @@ bool HlslGrammar::acceptType(TType& type)
case EHTokTextureCubearray: // ...
case EHTokTexture2DMS: // ...
case EHTokTexture2DMSarray: // ...
case EHTokRWTexture1d: // ...
case EHTokRWTexture1darray: // ...
case EHTokRWTexture2d: // ...
case EHTokRWTexture2darray: // ...
case EHTokRWTexture3d: // ...
case EHTokRWBuffer: // ...
return acceptTextureType(type);
break;

View file

@ -1252,15 +1252,17 @@ void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermType
const TSampler& texSampler = texType.getSampler();
const TSamplerDim dim = texSampler.dim;
const bool isImage = texSampler.isImage();
const int numArgs = (int)argAggregate->getSequence().size();
int numDims = 0;
switch (dim) {
case Esd1D: numDims = 1; break; // W
case Esd2D: numDims = 2; break; // W, H
case Esd3D: numDims = 3; break; // W, H, D
case EsdCube: numDims = 2; break; // W, H (cube)
case Esd1D: numDims = 1; break; // W
case Esd2D: numDims = 2; break; // W, H
case Esd3D: numDims = 3; break; // W, H, D
case EsdCube: numDims = 2; break; // W, H (cube)
case EsdBuffer: numDims = 1; break; // buffers
default:
assert(0 && "unhandled texture dimension");
}
@ -1273,7 +1275,7 @@ void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermType
const bool mipQuery = (numArgs > (numDims + 1)) && (!texSampler.isMultiSample());
// AST assumes integer return. Will be converted to float if required.
TIntermAggregate* sizeQuery = new TIntermAggregate(EOpTextureQuerySize);
TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize);
sizeQuery->getSequence().push_back(argTex);
// If we're querying an explicit LOD, add the LOD, which is always arg #1
if (mipQuery) {
@ -1419,11 +1421,12 @@ void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermType
const bool isMS = argTex->getType().getSampler().isMultiSample();
const bool isBuffer = argTex->getType().getSampler().dim == EsdBuffer;
const bool isImage = argTex->getType().getSampler().isImage();
const TBasicType coordBaseType = argCoord->getType().getBasicType();
// Last component of coordinate is the mip level, for non-MS. we separate them here:
if (isMS || isBuffer) {
// MS and Buffer have no LOD
if (isMS || isBuffer || isImage) {
// MS, Buffer, and Image have no LOD
coordSwizzle = argCoord;
} else {
// Extract coordinate
@ -1443,7 +1446,9 @@ void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermType
const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4));
// Create texel fetch
const TOperator fetchOp = (hasOffset ? EOpTextureFetchOffset : EOpTextureFetch);
const TOperator fetchOp = (isImage ? EOpImageLoad :
hasOffset ? EOpTextureFetchOffset :
EOpTextureFetch);
TIntermAggregate* txfetch = new TIntermAggregate(fetchOp);
// Build up the fetch
@ -1456,6 +1461,8 @@ void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermType
txfetch->getSequence().push_back(argSampleIdx);
} else if (isBuffer) {
// Nothing else to do for buffers.
} else if (isImage) {
// Nothing else to do for images.
} else {
// 2DMS and buffer have no LOD, but everything else does.
txfetch->getSequence().push_back(lodComponent);

View file

@ -67,20 +67,21 @@ const char* BaseTypeName(const char argOrder, const char* scalarName, const char
}
}
bool IsSamplerType(const char argType) { return argType == 'S' || argType == 's'; }
bool IsTextureArrayed(const char argOrder) { return argOrder == '@' || argOrder == '&'; }
bool IsTextureMS(const char argOrder) { return argOrder == '$' || argOrder == '&'; }
bool IsBuffer(const char argOrder) { return argOrder == '*'; }
bool IsSamplerType(const char argType) { return argType == 'S' || argType == 's'; }
bool IsArrayed(const char argOrder) { return argOrder == '@' || argOrder == '&' || argOrder == '#'; }
bool IsTextureMS(const char argOrder) { return argOrder == '$' || argOrder == '&'; }
bool IsBuffer(const char argOrder) { return argOrder == '*' || argOrder == '~'; }
bool IsImage(const char argOrder) { return argOrder == '!' || argOrder == '#' || argOrder == '~'; }
bool IsTextureType(const char argOrder)
{
return argOrder == '%' || argOrder == '@' || IsTextureMS(argOrder) || IsBuffer(argOrder);
return argOrder == '%' || argOrder == '@' || IsTextureMS(argOrder) || IsBuffer(argOrder) | IsImage(argOrder);
}
// Reject certain combinations that are illegal sample methods. For example,
// 3D arrays.
bool IsIllegalSample(const glslang::TString& name, const char* argOrder, int dim0)
{
const bool isArrayed = IsTextureArrayed(*argOrder);
const bool isArrayed = IsArrayed(*argOrder);
const bool isMS = IsTextureMS(*argOrder);
const bool isBuffer = IsBuffer(*argOrder);
@ -153,9 +154,9 @@ int CoordinateArgPos(const glslang::TString& name, bool isTexture)
}
// Some texture methods use an addition coordinate dimension for the mip
bool HasMipInCoord(const glslang::TString& name, bool isMS, bool isBuffer)
bool HasMipInCoord(const glslang::TString& name, bool isMS, bool isBuffer, bool isImage)
{
return name == "Load" && !isMS && !isBuffer;
return name == "Load" && !isMS && !isBuffer && !isImage;
}
// LOD calculations don't pass the array level in the coordinate.
@ -219,10 +220,11 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
{
const bool isTranspose = (argOrder[0] == '^');
const bool isTexture = IsTextureType(argOrder[0]);
const bool isArrayed = IsTextureArrayed(argOrder[0]);
const bool isArrayed = IsArrayed(argOrder[0]);
const bool isSampler = IsSamplerType(argType[0]);
const bool isMS = IsTextureMS(argOrder[0]);
const bool isBuffer = IsBuffer(argOrder[0]);
const bool isImage = IsImage(argOrder[0]);
char type = *argType;
@ -253,9 +255,15 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
case 'B': s += "bool"; break;
case 'S': s += "sampler"; break;
case 's': s += "SamplerComparisonState"; break;
case 'T': s += (isBuffer ? "Buffer" : "Texture"); break;
case 'i': s += (isBuffer ? "Buffer <int4>" : "Texture <int4>"); break;
case 'u': s += (isBuffer ? "Buffer <uint4>" : "Texture <uint4>"); break;
case 'T': s += ((isBuffer && isImage) ? "RWBuffer" :
isBuffer ? "Buffer" :
isImage ? "RWTexture" : "Texture"); break;
case 'i': s += ((isBuffer && isImage) ? "RWBuffer <int4>" :
isBuffer ? "Buffer <int4>" :
isImage ? "RWTexture <int4>" : "Texture <int4>"); break;
case 'u': s += ((isBuffer && isImage) ? "RWBuffer <uint4>" :
isBuffer ? "Buffer <uint4>" :
isImage ? "RWTexture <uint4>" : "Texture <uint4>");break;
default: s += "UNKNOWN_TYPE"; break;
}
} else {
@ -274,7 +282,10 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
if (type != 'T') // create itexture, utexture, etc
s += type;
s += (isBuffer ? "samplerBuffer" : "texture");
s += ((isImage && isBuffer) ? "imageBuffer" :
isImage ? "image" :
isBuffer ? "samplerBuffer" :
"texture");
break;
default: s += "UNKNOWN_TYPE"; break;
@ -507,6 +518,9 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
// '@' as first letter of order creates arrayed texture of given type
// '$' / '&' as first letter of order creates 2DMS / 2DMSArray textures
// '*' as first letter of order creates buffer object
// '!' as first letter of order creates image object
// '#' as first letter of order creates arrayed image object
// '~' as first letter of order creates an image buffer object
static const struct {
const char* name; // intrinsic name
@ -699,6 +713,11 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
{ "Load", /* +sampleidex*/ "V4", nullptr, "$&,V,S", "FIU,I,I", EShLangAll },
{ "Load", /* +samplindex, offset*/ "V4", nullptr, "$&,V,S,V", "FIU,I,I,I", EShLangAll },
// RWTexture loads
{ "Load", "V4", nullptr, "!#,V", "FIU,I", EShLangAll },
// RWBuffer loads
{ "Load", "V4", nullptr, "~1,V", "FIU,I", EShLangAll },
{ "Gather", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll },
{ "Gather", /* O*/ "V4", nullptr, "%@,S,V,V", "FIU,S,F,I", EShLangAll },
@ -710,36 +729,36 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
//
// UINT Width
// UINT MipLevel, UINT Width, UINT NumberOfLevels
{ "GetDimensions", /* 1D */ "-", "-", "%1,>S", "FUI,U", EShLangAll },
{ "GetDimensions", /* 1D */ "-", "-", "%1,>S", "FUI,F", EShLangAll },
{ "GetDimensions", /* 1D */ "-", "-", "%!~1,>S", "FUI,U", EShLangAll },
{ "GetDimensions", /* 1D */ "-", "-", "%!~1,>S", "FUI,F", EShLangAll },
{ "GetDimensions", /* 1D */ "-", "-", "%1,S,>S,", "FUI,U,,", EShLangAll },
{ "GetDimensions", /* 1D */ "-", "-", "%1,S,>S,", "FUI,U,F,", EShLangAll },
// UINT Width, UINT Elements
// UINT MipLevel, UINT Width, UINT Elements, UINT NumberOfLevels
{ "GetDimensions", /* 1DArray */ "-", "-", "@1,>S,", "FUI,U,", EShLangAll },
{ "GetDimensions", /* 1DArray */ "-", "-", "@1,>S,", "FUI,F,", EShLangAll },
{ "GetDimensions", /* 1DArray */ "-", "-", "@#1,>S,", "FUI,U,", EShLangAll },
{ "GetDimensions", /* 1DArray */ "-", "-", "@#1,>S,", "FUI,F,", EShLangAll },
{ "GetDimensions", /* 1DArray */ "-", "-", "@1,S,>S,,", "FUI,U,,,", EShLangAll },
{ "GetDimensions", /* 1DArray */ "-", "-", "@1,S,>S,,", "FUI,U,F,,", EShLangAll },
// UINT Width, UINT Height
// UINT MipLevel, UINT Width, UINT Height, UINT NumberOfLevels
{ "GetDimensions", /* 2D */ "-", "-", "%2,>S,", "FUI,U,", EShLangAll },
{ "GetDimensions", /* 2D */ "-", "-", "%2,>S,", "FUI,F,", EShLangAll },
{ "GetDimensions", /* 2D */ "-", "-", "%!2,>S,", "FUI,U,", EShLangAll },
{ "GetDimensions", /* 2D */ "-", "-", "%!2,>S,", "FUI,F,", EShLangAll },
{ "GetDimensions", /* 2D */ "-", "-", "%2,S,>S,,", "FUI,U,,,", EShLangAll },
{ "GetDimensions", /* 2D */ "-", "-", "%2,S,>S,,", "FUI,U,F,,", EShLangAll },
// UINT Width, UINT Height, UINT Elements
// UINT MipLevel, UINT Width, UINT Height, UINT Elements, UINT NumberOfLevels
{ "GetDimensions", /* 2DArray */ "-", "-", "@2,>S,,", "FUI,U,,", EShLangAll },
{ "GetDimensions", /* 2DArray */ "-", "-", "@2,>S,,", "FUI,F,F,F", EShLangAll },
{ "GetDimensions", /* 2DArray */ "-", "-", "@#2,>S,,", "FUI,U,,", EShLangAll },
{ "GetDimensions", /* 2DArray */ "-", "-", "@#2,>S,,", "FUI,F,F,F", EShLangAll },
{ "GetDimensions", /* 2DArray */ "-", "-", "@2,S,>S,,,", "FUI,U,,,,", EShLangAll },
{ "GetDimensions", /* 2DArray */ "-", "-", "@2,S,>S,,,", "FUI,U,F,,,", EShLangAll },
// UINT Width, UINT Height, UINT Depth
// UINT MipLevel, UINT Width, UINT Height, UINT Depth, UINT NumberOfLevels
{ "GetDimensions", /* 3D */ "-", "-", "%3,>S,,", "FUI,U,,", EShLangAll },
{ "GetDimensions", /* 3D */ "-", "-", "%3,>S,,", "FUI,F,,", EShLangAll },
{ "GetDimensions", /* 3D */ "-", "-", "%!3,>S,,", "FUI,U,,", EShLangAll },
{ "GetDimensions", /* 3D */ "-", "-", "%!3,>S,,", "FUI,F,,", EShLangAll },
{ "GetDimensions", /* 3D */ "-", "-", "%3,S,>S,,,", "FUI,U,,,,", EShLangAll },
{ "GetDimensions", /* 3D */ "-", "-", "%3,S,>S,,,", "FUI,U,F,,,", EShLangAll },
@ -836,10 +855,11 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
for (const char* argOrder = intrinsic.argOrder; !IsEndOfArg(argOrder); ++argOrder) { // for each order...
const bool isTexture = IsTextureType(*argOrder);
const bool isArrayed = IsTextureArrayed(*argOrder);
const bool isArrayed = IsArrayed(*argOrder);
const bool isMS = IsTextureMS(*argOrder);
const bool isBuffer = IsBuffer(*argOrder);
const bool mipInCoord = HasMipInCoord(intrinsic.name, isMS, isBuffer);
const bool isImage = IsImage(*argOrder);
const bool mipInCoord = HasMipInCoord(intrinsic.name, isMS, isBuffer, isImage);
const int fixedVecSize = FixedVecSize(argOrder);
const int coordArg = CoordinateArgPos(intrinsic.name, isTexture);

View file

@ -258,6 +258,13 @@ void HlslScanContext::fillInKeywordMap()
(*KeywordMap)["TextureCubeArray"] = EHTokTextureCubearray;
(*KeywordMap)["Texture2DMS"] = EHTokTexture2DMS;
(*KeywordMap)["Texture2DMSArray"] = EHTokTexture2DMSarray;
(*KeywordMap)["RWTexture1D"] = EHTokRWTexture1d;
(*KeywordMap)["RWTexture1DArray"] = EHTokRWTexture1darray;
(*KeywordMap)["RWTexture2D"] = EHTokRWTexture2d;
(*KeywordMap)["RWTexture2DArray"] = EHTokRWTexture2darray;
(*KeywordMap)["RWTexture3D"] = EHTokRWTexture3d;
(*KeywordMap)["RWBuffer"] = EHTokRWBuffer;
(*KeywordMap)["struct"] = EHTokStruct;
(*KeywordMap)["cbuffer"] = EHTokCBuffer;
@ -581,6 +588,12 @@ EHlslTokenClass HlslScanContext::tokenizeIdentifier()
case EHTokTextureCubearray:
case EHTokTexture2DMS:
case EHTokTexture2DMSarray:
case EHTokRWTexture1d:
case EHTokRWTexture1darray:
case EHTokRWTexture2d:
case EHTokRWTexture2darray:
case EHTokRWTexture3d:
case EHTokRWBuffer:
return keyword;
// variable, user type, ...

View file

@ -209,6 +209,13 @@ enum EHlslTokenClass {
EHTokTextureCubearray,
EHTokTexture2DMS,
EHTokTexture2DMSarray,
EHTokRWTexture1d,
EHTokRWTexture1darray,
EHTokRWTexture2d,
EHTokRWTexture2darray,
EHTokRWTexture3d,
EHTokRWBuffer,
// variable, user type, ...
EHTokIdentifier,