HLSL: add subpass input types and methods
Add support for Subpass Input proposal of issue #1069. Subpass input types are given as: layout(input_attachment_index = 1) SubpassInput<float4> subpass_f; layout(input_attachment_index = 2) SubpassInput<int4> subpass_i; layout(input_attachment_index = 3) SubpassInput<uint4> subpass_u; layout(input_attachment_index = 1) SubpassInputMS<float4> subpass_ms_f; layout(input_attachment_index = 2) SubpassInputMS<int4> subpass_ms_i; layout(input_attachment_index = 3) SubpassInputMS<uint4> subpass_ms_u; The input attachment may also be specified using attribute syntax: [[vk::input_attachment_index(7)]] SubpassInput subpass_2; The template type may be a shorter-than-vec4 vector, but currently user structs are not supported. (An unimplemented error will be issued). The load operations are methods on objects of the above type: float4 result = subpass_f.SubpassLoad(); int4 result = subpass_i.SubpassLoad(); uint4 result = subpass_u.SubpassLoad(); float4 result = subpass_ms_f.SubpassLoad(samp); int4 result = subpass_ms_i.SubpassLoad(samp); uint4 result = subpass_ms_u.SubpassLoad(samp); Additionally, the AST printer could not print EOpSubpass* nodes. Now it can. Fixes #1069
This commit is contained in:
@@ -1088,6 +1088,69 @@ bool HlslGrammar::acceptAnnotations(TQualifier&)
|
||||
return true;
|
||||
}
|
||||
|
||||
// subpass input type
|
||||
// : SUBPASSINPUT
|
||||
// | SUBPASSINPUT VECTOR LEFT_ANGLE template_type RIGHT_ANGLE
|
||||
// | SUBPASSINPUTMS
|
||||
// | SUBPASSINPUTMS VECTOR LEFT_ANGLE template_type RIGHT_ANGLE
|
||||
bool HlslGrammar::acceptSubpassInputType(TType& type)
|
||||
{
|
||||
// read subpass type
|
||||
const EHlslTokenClass subpassInputType = peek();
|
||||
|
||||
bool multisample;
|
||||
|
||||
switch (subpassInputType) {
|
||||
case EHTokSubpassInput: multisample = false; break;
|
||||
case EHTokSubpassInputMS: multisample = true; break;
|
||||
default:
|
||||
return false; // not a subpass input declaration
|
||||
}
|
||||
|
||||
advanceToken(); // consume the sampler type keyword
|
||||
|
||||
TType subpassType(EbtFloat, EvqUniform, 4); // default type is float4
|
||||
|
||||
if (acceptTokenClass(EHTokLeftAngle)) {
|
||||
if (! acceptType(subpassType)) {
|
||||
expected("scalar or vector type");
|
||||
return false;
|
||||
}
|
||||
|
||||
const TBasicType basicRetType = subpassType.getBasicType() ;
|
||||
|
||||
switch (basicRetType) {
|
||||
case EbtFloat:
|
||||
case EbtUint:
|
||||
case EbtInt:
|
||||
case EbtStruct:
|
||||
break;
|
||||
default:
|
||||
unimplemented("basic type in subpass input");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! acceptTokenClass(EHTokRightAngle)) {
|
||||
expected("right angle bracket");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const TBasicType subpassBasicType = subpassType.isStruct() ? (*subpassType.getStruct())[0].type->getBasicType()
|
||||
: subpassType.getBasicType();
|
||||
|
||||
TSampler sampler;
|
||||
sampler.setSubpass(subpassBasicType, multisample);
|
||||
|
||||
// Remember the declared return type. Function returns false on error.
|
||||
if (!parseContext.setTextureReturnType(sampler, subpassType, token.loc))
|
||||
return false;
|
||||
|
||||
type.shallowCopy(TType(sampler, EvqUniform));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// sampler_type
|
||||
// : SAMPLER
|
||||
// | SAMPLER1D
|
||||
@@ -1357,6 +1420,11 @@ bool HlslGrammar::acceptType(TType& type, TIntermNode*& nodeList)
|
||||
return acceptSamplerType(type);
|
||||
break;
|
||||
|
||||
case EHTokSubpassInput: // fall through
|
||||
case EHTokSubpassInputMS: // ...
|
||||
return acceptSubpassInputType(type);
|
||||
break;
|
||||
|
||||
case EHTokBuffer: // fall through
|
||||
case EHTokTexture1d: // ...
|
||||
case EHTokTexture1darray: // ...
|
||||
|
||||
@@ -87,6 +87,7 @@ namespace glslang {
|
||||
bool acceptAnnotations(TQualifier&);
|
||||
bool acceptSamplerType(TType&);
|
||||
bool acceptTextureType(TType&);
|
||||
bool acceptSubpassInputType(TType&);
|
||||
bool acceptStructBufferType(TType&);
|
||||
bool acceptConstantBufferType(TType&);
|
||||
bool acceptStruct(TType&, TIntermNode*& nodeList);
|
||||
|
||||
@@ -4215,6 +4215,25 @@ void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermType
|
||||
break;
|
||||
}
|
||||
|
||||
case EOpSubpassLoad:
|
||||
{
|
||||
const TIntermTyped* argSubpass =
|
||||
argAggregate ? argAggregate->getSequence()[0]->getAsTyped() :
|
||||
arguments->getAsTyped();
|
||||
|
||||
const TSampler& sampler = argSubpass->getType().getSampler();
|
||||
|
||||
// subpass load: the multisample form is overloaded. Here, we convert that to
|
||||
// the EOpSubpassLoadMS opcode.
|
||||
if (argAggregate != nullptr && argAggregate->getSequence().size() > 1)
|
||||
node->getAsOperator()->setOp(EOpSubpassLoadMS);
|
||||
|
||||
node = convertReturn(node, sampler);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
break; // most pass through unchanged
|
||||
}
|
||||
@@ -8952,6 +8971,12 @@ bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retT
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Subpass doesn't handle struct returns, due to some oddities with fn overloading.
|
||||
if (sampler.isSubpass()) {
|
||||
error(loc, "Unimplemented: structure template type in subpass input", "", "");
|
||||
return false;
|
||||
}
|
||||
|
||||
TTypeList* members = retType.getWritableStruct();
|
||||
|
||||
// Check for too many or not enough structure members.
|
||||
|
||||
@@ -68,14 +68,21 @@ const char* BaseTypeName(const char argOrder, const char* scalarName, const char
|
||||
}
|
||||
}
|
||||
|
||||
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 == '~'; }
|
||||
// arg order queries
|
||||
bool IsSamplerType(const char argType) { return argType == 'S' || argType == 's'; }
|
||||
bool IsArrayed(const char argOrder) { return argOrder == '@' || argOrder == '&' || argOrder == '#'; }
|
||||
bool IsTextureNonMS(const char argOrder) { return argOrder == '%'; }
|
||||
bool IsSubpassInput(const char argOrder) { return argOrder == '[' || argOrder == ']'; }
|
||||
bool IsArrayedTexture(const char argOrder) { return argOrder == '@'; }
|
||||
bool IsTextureMS(const char argOrder) { return argOrder == '$' || argOrder == '&'; }
|
||||
bool IsMS(const char argOrder) { return IsTextureMS(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) | IsImage(argOrder);
|
||||
return IsTextureNonMS(argOrder) || IsArrayedTexture(argOrder) ||
|
||||
IsTextureMS(argOrder) || IsBuffer(argOrder) || IsImage(argOrder);
|
||||
}
|
||||
|
||||
// Reject certain combinations that are illegal sample methods. For example,
|
||||
@@ -222,15 +229,16 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
|
||||
const bool isTexture = IsTextureType(argOrder[0]);
|
||||
const bool isArrayed = IsArrayed(argOrder[0]);
|
||||
const bool isSampler = IsSamplerType(argType[0]);
|
||||
const bool isMS = IsTextureMS(argOrder[0]);
|
||||
const bool isMS = IsMS(argOrder[0]);
|
||||
const bool isBuffer = IsBuffer(argOrder[0]);
|
||||
const bool isImage = IsImage(argOrder[0]);
|
||||
const bool isSubpass = IsSubpassInput(argOrder[0]);
|
||||
|
||||
char type = *argType;
|
||||
|
||||
if (isTranspose) { // Take transpose of matrix dimensions
|
||||
std::swap(dim0, dim1);
|
||||
} else if (isTexture) {
|
||||
} else if (isTexture || isSubpass) {
|
||||
if (type == 'F') // map base type to texture of that type.
|
||||
type = 'T'; // e.g, int -> itexture, uint -> utexture, etc.
|
||||
else if (type == 'I')
|
||||
@@ -255,16 +263,23 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
|
||||
case 'S': s += "sampler"; break;
|
||||
case 's': s += "SamplerComparisonState"; break;
|
||||
case 'T': s += ((isBuffer && isImage) ? "RWBuffer" :
|
||||
isSubpass ? "SubpassInput" :
|
||||
isBuffer ? "Buffer" :
|
||||
isImage ? "RWTexture" : "Texture"); break;
|
||||
case 'i': s += ((isBuffer && isImage) ? "RWBuffer" :
|
||||
isSubpass ? "SubpassInput" :
|
||||
isBuffer ? "Buffer" :
|
||||
isImage ? "RWTexture" : "Texture"); break;
|
||||
case 'u': s += ((isBuffer && isImage) ? "RWBuffer" :
|
||||
isSubpass ? "SubpassInput" :
|
||||
isBuffer ? "Buffer" :
|
||||
isImage ? "RWTexture" : "Texture"); break;
|
||||
default: s += "UNKNOWN_TYPE"; break;
|
||||
}
|
||||
|
||||
if (isSubpass && isMS)
|
||||
s += "MS";
|
||||
|
||||
} else {
|
||||
switch (type) {
|
||||
case '-': s += "void"; break;
|
||||
@@ -282,6 +297,7 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
|
||||
s += type;
|
||||
|
||||
s += ((isImage && isBuffer) ? "imageBuffer" :
|
||||
isSubpass ? "subpassInput" :
|
||||
isImage ? "image" :
|
||||
isBuffer ? "samplerBuffer" :
|
||||
"texture");
|
||||
@@ -296,6 +312,9 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
|
||||
if (fixedVecSize != 0)
|
||||
dim0 = dim1 = fixedVecSize;
|
||||
|
||||
const char dim0Char = ('0' + char(dim0));
|
||||
const char dim1Char = ('0' + char(dim1));
|
||||
|
||||
// Add sampler dimensions
|
||||
if (isSampler || isTexture) {
|
||||
if ((order == 'V' || isTexture) && !isBuffer) {
|
||||
@@ -320,12 +339,12 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
|
||||
case '-': break; // no dimensions for voids
|
||||
case 'S': break; // no dimensions on scalars
|
||||
case 'V':
|
||||
s += ('0' + char(dim0));
|
||||
s += dim0Char;
|
||||
break;
|
||||
case 'M':
|
||||
s += ('0' + char(dim0));
|
||||
s += dim0Char;
|
||||
s += 'x';
|
||||
s += ('0' + char(dim1));
|
||||
s += dim1Char;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -339,9 +358,9 @@ glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, cons
|
||||
// For HLSL, append return type for texture types
|
||||
if (UseHlslTypes) {
|
||||
switch (type) {
|
||||
case 'i': s += "<int4>"; break;
|
||||
case 'u': s += "<uint4>"; break;
|
||||
case 'T': s += "<float4>"; break;
|
||||
case 'i': s += "<int"; s += dim0Char; s += ">"; break;
|
||||
case 'u': s += "<uint"; s += dim0Char; s += ">"; break;
|
||||
case 'T': s += "<float"; s += dim0Char; s += ">"; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -414,7 +433,7 @@ inline void FindVectorMatrixBounds(const char* argOrder, int fixedVecSize, int&
|
||||
const char* nthArgOrder(NthArg(argOrder, arg));
|
||||
if (nthArgOrder == nullptr)
|
||||
break;
|
||||
else if (*nthArgOrder == 'V')
|
||||
else if (*nthArgOrder == 'V' || IsSubpassInput(*nthArgOrder))
|
||||
dim0Max = 4;
|
||||
else if (*nthArgOrder == 'M')
|
||||
dim0Max = dim1Max = 4;
|
||||
@@ -537,6 +556,7 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
|
||||
// '!' 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
|
||||
// '[' / ']' as first letter of order creates a SubpassInput/SubpassInputMS object
|
||||
|
||||
static const struct {
|
||||
const char* name; // intrinsic name
|
||||
@@ -882,6 +902,10 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
|
||||
{ "DecrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true },
|
||||
{ "Consume", nullptr, nullptr, "-", "-", EShLangAll, true },
|
||||
|
||||
// Methods for subpass input objects
|
||||
{ "SubpassLoad", "V4", nullptr, "[", "FIU", EShLangPS, true },
|
||||
{ "SubpassLoad", "V4", nullptr, "],S", "FIU,I", EShLangPS, true },
|
||||
|
||||
// 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 },
|
||||
};
|
||||
@@ -1219,6 +1243,10 @@ void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profil
|
||||
// GS methods
|
||||
symbolTable.relateToOperator(BUILTIN_PREFIX "Append", EOpMethodAppend);
|
||||
symbolTable.relateToOperator(BUILTIN_PREFIX "RestartStrip", EOpMethodRestartStrip);
|
||||
|
||||
// Subpass input methods
|
||||
symbolTable.relateToOperator(BUILTIN_PREFIX "SubpassLoad", EOpSubpassLoad);
|
||||
symbolTable.relateToOperator(BUILTIN_PREFIX "SubpassLoadMS", EOpSubpassLoadMS);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -336,6 +336,8 @@ void HlslScanContext::fillInKeywordMap()
|
||||
(*KeywordMap)["RWTexture2DArray"] = EHTokRWTexture2darray;
|
||||
(*KeywordMap)["RWTexture3D"] = EHTokRWTexture3d;
|
||||
(*KeywordMap)["RWBuffer"] = EHTokRWBuffer;
|
||||
(*KeywordMap)["SubpassInput"] = EHTokSubpassInput;
|
||||
(*KeywordMap)["SubpassInputMS"] = EHTokSubpassInputMS;
|
||||
|
||||
(*KeywordMap)["AppendStructuredBuffer"] = EHTokAppendStructuredBuffer;
|
||||
(*KeywordMap)["ByteAddressBuffer"] = EHTokByteAddressBuffer;
|
||||
@@ -827,6 +829,8 @@ EHlslTokenClass HlslScanContext::tokenizeIdentifier()
|
||||
case EHTokRWByteAddressBuffer:
|
||||
case EHTokRWStructuredBuffer:
|
||||
case EHTokStructuredBuffer:
|
||||
case EHTokSubpassInput:
|
||||
case EHTokSubpassInputMS:
|
||||
return keyword;
|
||||
|
||||
// variable, user type, ...
|
||||
|
||||
@@ -273,6 +273,8 @@ enum EHlslTokenClass {
|
||||
EHTokRWTexture2darray,
|
||||
EHTokRWTexture3d,
|
||||
EHTokRWBuffer,
|
||||
EHTokSubpassInput,
|
||||
EHTokSubpassInputMS,
|
||||
|
||||
// Structure buffer variants
|
||||
EHTokAppendStructuredBuffer,
|
||||
|
||||
Reference in New Issue
Block a user