Commit 5ee05891 by LoopDawg

HLSL: add methods to track user structure in texture return type.

Some languages allow a restricted set of user structure types returned from texture sampling operations. Restrictions include the total vector size of all components may not exceed 4, and the basic types of all members must be identical. This adds underpinnings for that ability. Because storing a whole TType or even a simple TTypeList in the TSampler would be expensive, the structure definition is held in a table outside the TType. The TSampler contains a small bitfield index, currently 4 bits to support up to 15 separate texture template structure types, but that can be adjusted up or down. Vector returns are handled as before. There are abstraction methods accepting and returning a TType (such as may have been parsed from a grammar). The new methods will accept a texture template type and set the sampler to the structure if possible, checking a range of error conditions such as whether the total structure vector components exceed 4, or whether their basic types differe, or whether the struct contains non-vector-or-scalar members. Another query returns the appropriate TType for the sampler. High level summary of design: In the TSampler, this holds an index into the texture structure return type table: unsigned int structReturnIndex : structReturnIndexBits; These are the methods to set or get the return type from the TSampler. They work for vector or structure returns, and potentially could be expanded to handle other things (small arrays?) if ever needed. bool setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc); void getTextureReturnType(const TSampler& sampler, const TType& retType, const TSourceLoc& loc) const; The ``convertReturn`` lambda in ``HlslParseContext::decomposeSampleMethods`` is greatly expanded to know how to copy a vec4 sample return to whatever the structure type should be. This is a little awkward since it involves introducing a comma expression to return the proper aggregate value after a set of memberwise copies.
parent 03e63fa8
struct s1_t {
float c0;
float2 c1;
float c2;
};
struct s2_t {
float c0;
float3 c1;
};
struct s3_t {
float2 c0;
float1 c1;
};
struct s4_t {
int c0;
int2 c1;
int c2;
};
struct s5_t {
uint c0;
uint c1;
};
SamplerState g_sSamp;
Texture2D <s1_t> g_tTex2s1;
Texture2D <s2_t> g_tTex2s2;
Texture2D <s3_t> g_tTex2s3;
Texture2D <s4_t> g_tTex2s4;
Texture2D <s5_t> g_tTex2s5;
Texture2D <s1_t> g_tTex2s1a; // same type as g_tTex2s1, to test fn signature matching.
// function overloading to test name mangling with textures templatized on structs
s1_t fn1(Texture2D <s1_t> t1) { return t1 . Sample(g_sSamp, float2(0.6, 0.61)); }
s2_t fn1(Texture2D <s2_t> t2) { return t2 . Sample(g_sSamp, float2(0.6, 0.61)); }
float4 main() : SV_Target0
{
s1_t s1 = g_tTex2s1 . Sample(g_sSamp, float2(0.1, 0.11));
s2_t s2 = g_tTex2s2 . Sample(g_sSamp, float2(0.2, 0.21));
s3_t s3 = g_tTex2s3 . Sample(g_sSamp, float2(0.3, 0.31));
s4_t s4 = g_tTex2s4 . Sample(g_sSamp, float2(0.4, 0.41));
s5_t s5 = g_tTex2s5 . Sample(g_sSamp, float2(0.5, 0.51));
s1_t r0 = fn1(g_tTex2s1);
s2_t r1 = fn1(g_tTex2s2);
s1_t r2 = fn1(g_tTex2s1a);
return 0;
}
...@@ -11,7 +11,7 @@ Texture2D <float4> g_tTex2df4; ...@@ -11,7 +11,7 @@ Texture2D <float4> g_tTex2df4;
SamplerState g_sSamp; SamplerState g_sSamp;
float4 main() float4 main() : SV_Target0
{ {
uint MipLevel; uint MipLevel;
uint WidthU; uint WidthU;
......
...@@ -80,7 +80,19 @@ struct TSampler { // misnomer now; includes images, textures without sampler, ...@@ -80,7 +80,19 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler
bool sampler : 1; // true means a pure sampler, other fields should be clear() bool sampler : 1; // true means a pure sampler, other fields should be clear()
bool external : 1; // GL_OES_EGL_image_external bool external : 1; // GL_OES_EGL_image_external
unsigned int vectorSize : 3; // return vector size. TODO: support arbitrary types. unsigned int vectorSize : 3; // vector return type size.
// Some languages support structures as sample results. Storing the whole structure in the
// TSampler is too large, so there is an index to a separate table.
static const unsigned structReturnIndexBits = 4; // number of index bits to use.
static const unsigned structReturnSlots = (1<<structReturnIndexBits)-1; // number of valid values
static const unsigned noReturnStruct = structReturnSlots; // value if no return struct type.
// Index into a language specific table of texture return structures.
unsigned int structReturnIndex : structReturnIndexBits;
// Encapsulate getting members' vector sizes packed into the vectorSize bitfield.
unsigned int getVectorSize() const { return vectorSize; }
bool isImage() const { return image && dim != EsdSubpass; } bool isImage() const { return image && dim != EsdSubpass; }
bool isSubpass() const { return dim == EsdSubpass; } bool isSubpass() const { return dim == EsdSubpass; }
...@@ -90,6 +102,7 @@ struct TSampler { // misnomer now; includes images, textures without sampler, ...@@ -90,6 +102,7 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
bool isShadow() const { return shadow; } bool isShadow() const { return shadow; }
bool isArrayed() const { return arrayed; } bool isArrayed() const { return arrayed; }
bool isMultiSample() const { return ms; } bool isMultiSample() const { return ms; }
bool hasReturnStruct() const { return structReturnIndex != noReturnStruct; }
void clear() void clear()
{ {
...@@ -102,6 +115,9 @@ struct TSampler { // misnomer now; includes images, textures without sampler, ...@@ -102,6 +115,9 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
combined = false; combined = false;
sampler = false; sampler = false;
external = false; external = false;
structReturnIndex = noReturnStruct;
// by default, returns a single vec4;
vectorSize = 4; vectorSize = 4;
} }
...@@ -160,16 +176,17 @@ struct TSampler { // misnomer now; includes images, textures without sampler, ...@@ -160,16 +176,17 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
bool operator==(const TSampler& right) const bool operator==(const TSampler& right) const
{ {
return type == right.type && return type == right.type &&
dim == right.dim && dim == right.dim &&
arrayed == right.arrayed && arrayed == right.arrayed &&
shadow == right.shadow && shadow == right.shadow &&
ms == right.ms && ms == right.ms &&
image == right.image && image == right.image &&
combined == right.combined && combined == right.combined &&
sampler == right.sampler && sampler == right.sampler &&
external == right.external && external == right.external &&
vectorSize == right.vectorSize; vectorSize == right.vectorSize &&
structReturnIndex == right.structReturnIndex;
} }
bool operator!=(const TSampler& right) const bool operator!=(const TSampler& right) const
......
...@@ -104,11 +104,20 @@ void TType::buildMangledName(TString& mangledName) const ...@@ -104,11 +104,20 @@ void TType::buildMangledName(TString& mangledName) const
default: break; // some compilers want this default: break; // some compilers want this
} }
switch (sampler.vectorSize) { if (sampler.hasReturnStruct()) {
case 1: mangledName += "1"; break; // Name mangle for sampler return struct uses struct table index.
case 2: mangledName += "2"; break; mangledName += "-tx-struct";
case 3: mangledName += "3"; break;
case 4: break; // default to prior name mangle behavior char text[16]; // plenty enough space for the small integers.
snprintf(text, sizeof(text), "%d-", sampler.structReturnIndex);
mangledName += text;
} else {
switch (sampler.getVectorSize()) {
case 1: mangledName += "1"; break;
case 2: mangledName += "2"; break;
case 3: mangledName += "3"; break;
case 4: break; // default to prior name mangle behavior
}
} }
if (sampler.ms) if (sampler.ms)
......
...@@ -291,6 +291,7 @@ INSTANTIATE_TEST_CASE_P( ...@@ -291,6 +291,7 @@ INSTANTIATE_TEST_CASE_P(
{"hlsl.structIoFourWay.frag", "main"}, {"hlsl.structIoFourWay.frag", "main"},
{"hlsl.structStructName.frag", "main"}, {"hlsl.structStructName.frag", "main"},
{"hlsl.synthesizeInput.frag", "main"}, {"hlsl.synthesizeInput.frag", "main"},
{"hlsl.texture.struct.frag", "main"},
{"hlsl.texture.subvec4.frag", "main"}, {"hlsl.texture.subvec4.frag", "main"},
{"hlsl.this.frag", "main"}, {"hlsl.this.frag", "main"},
{"hlsl.intrinsics.vert", "VertexShaderFunction"}, {"hlsl.intrinsics.vert", "VertexShaderFunction"},
......
...@@ -1189,7 +1189,13 @@ bool HlslGrammar::acceptTextureType(TType& type) ...@@ -1189,7 +1189,13 @@ bool HlslGrammar::acceptTextureType(TType& type)
const TBasicType basicRetType = txType.getBasicType() ; const TBasicType basicRetType = txType.getBasicType() ;
if (basicRetType != EbtFloat && basicRetType != EbtUint && basicRetType != EbtInt) { switch (basicRetType) {
case EbtFloat:
case EbtUint:
case EbtInt:
case EbtStruct:
break;
default:
unimplemented("basic type in texture"); unimplemented("basic type in texture");
return false; return false;
} }
...@@ -1206,8 +1212,8 @@ bool HlslGrammar::acceptTextureType(TType& type) ...@@ -1206,8 +1212,8 @@ bool HlslGrammar::acceptTextureType(TType& type)
return false; return false;
} }
if (!txType.isScalar() && !txType.isVector()) { if (!txType.isScalar() && !txType.isVector() && !txType.isStruct()) {
expected("scalar or vector type"); expected("scalar, vector, or struct type");
return false; return false;
} }
...@@ -1244,20 +1250,24 @@ bool HlslGrammar::acceptTextureType(TType& type) ...@@ -1244,20 +1250,24 @@ bool HlslGrammar::acceptTextureType(TType& type)
if (image || dim == EsdBuffer) if (image || dim == EsdBuffer)
format = parseContext.getLayoutFromTxType(token.loc, txType); format = parseContext.getLayoutFromTxType(token.loc, txType);
const TBasicType txBasicType = txType.isStruct() ? (*txType.getStruct())[0].type->getBasicType()
: txType.getBasicType();
// Non-image Buffers are combined // Non-image Buffers are combined
if (dim == EsdBuffer && !image) { if (dim == EsdBuffer && !image) {
sampler.set(txType.getBasicType(), dim, array); sampler.set(txType.getBasicType(), dim, array);
} else { } else {
// DX10 textures are separated. TODO: DX9. // DX10 textures are separated. TODO: DX9.
if (image) { if (image) {
sampler.setImage(txType.getBasicType(), dim, array, shadow, ms); sampler.setImage(txBasicType, dim, array, shadow, ms);
} else { } else {
sampler.setTexture(txType.getBasicType(), dim, array, shadow, ms); sampler.setTexture(txBasicType, dim, array, shadow, ms);
} }
} }
// Remember the declared vector size. // Remember the declared return type. Function returns false on error.
sampler.vectorSize = txType.getVectorSize(); if (!parseContext.setTextureReturnType(sampler, txType, token.loc))
return false;
// Force uncombined, if necessary // Force uncombined, if necessary
if (!combined) if (!combined)
......
...@@ -213,6 +213,12 @@ public: ...@@ -213,6 +213,12 @@ public:
// Share struct buffer deep types // Share struct buffer deep types
void shareStructBufferType(TType&); void shareStructBufferType(TType&);
// Set texture return type of the given sampler. Returns success (not all types are valid).
bool setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc);
// Obtain the sampler return type of the given sampler in retType.
void getTextureReturnType(const TSampler& sampler, TType& retType) const;
protected: protected:
struct TFlattenData { struct TFlattenData {
TFlattenData() : nextBinding(TQualifier::layoutBindingEnd), TFlattenData() : nextBinding(TQualifier::layoutBindingEnd),
...@@ -388,6 +394,10 @@ protected: ...@@ -388,6 +394,10 @@ 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;
// This tracks texture sample user structure return types. Only a limited number are supported, as
// may fit in TSampler::structReturnIndex.
TVector<TTypeList*> textureReturnStruct;
TMap<TString, bool> structBufferCounter; TMap<TString, bool> structBufferCounter;
......
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