Skip to content

Commit 74a6e28

Browse files
committed
Improve ability to redeclare buffer formats from struct definitions
1 parent 7edcbfb commit 74a6e28

File tree

4 files changed

+234
-11
lines changed

4 files changed

+234
-11
lines changed

qrenderdoc/Code/BufferFormatter.cpp

+226-11
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ struct StructFormatData
4545

4646
// does this contain a member annotated with [[single]] ?
4747
bool singleMember = false;
48-
49-
bool signedEnum = false;
5048
};
5149

5250
GraphicsAPI BufferFormatter::m_API;
@@ -1042,7 +1040,9 @@ ParsedFormat BufferFormatter::ParseFormatString(const QString &formatString, uin
10421040
}
10431041

10441042
cur->structDef.type.matrixByteStride = VarTypeByteSize(tmp.type.baseType);
1045-
cur->signedEnum = (VarTypeCompType(tmp.type.baseType) == CompType::SInt);
1043+
cur->structDef.type.flags = (VarTypeCompType(tmp.type.baseType) == CompType::SInt)
1044+
? ShaderVariableFlags::SignedEnum
1045+
: ShaderVariableFlags::NoFlags;
10461046
}
10471047

10481048
continue;
@@ -1065,7 +1065,7 @@ ParsedFormat BufferFormatter::ParseFormatString(const QString &formatString, uin
10651065
QString valueNum = enumMatch.captured(2);
10661066

10671067
bool ok = false;
1068-
if(cur->signedEnum)
1068+
if(cur->structDef.type.flags & ShaderVariableFlags::SignedEnum)
10691069
{
10701070
int64_t val = valueNum.toLongLong(&ok, 0);
10711071

@@ -2844,17 +2844,43 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, ResourceId shader,
28442844
const ShaderConstantType &pointeeType =
28452845
PointerTypeRegistry::GetTypeDescriptor(shader, members[i].type.pointerTypeID);
28462846

2847-
varTypeName = MakeIdentifierName(pointeeType.name);
2847+
// don't declare pointer structs for plain scalar pointers (float *foo type)
2848+
if(pointeeType.name.empty() && pointeeType.members.size() == 1)
2849+
{
2850+
varTypeName = pointeeType.members[0].type.name;
2851+
}
2852+
else
2853+
{
2854+
varTypeName = MakeIdentifierName(pointeeType.name);
2855+
2856+
if(!declaredStructs.contains(varTypeName))
2857+
{
2858+
declaredStructs.push_back(varTypeName);
2859+
declarations += DeclareStruct(pack, shader, declaredStructs, anonStructs, varTypeName,
2860+
pointeeType.members, pointeeType.arrayByteStride, QString()) +
2861+
lit("\n");
2862+
}
2863+
}
28482864

2865+
varTypeName += lit("*");
2866+
}
2867+
else if(members[i].type.baseType == VarType::Enum)
2868+
{
28492869
if(!declaredStructs.contains(varTypeName))
28502870
{
28512871
declaredStructs.push_back(varTypeName);
2852-
declarations += DeclareStruct(pack, shader, declaredStructs, anonStructs, varTypeName,
2853-
pointeeType.members, pointeeType.arrayByteStride, QString()) +
2854-
lit("\n");
2855-
}
28562872

2857-
varTypeName += lit("*");
2873+
const bool signedEnum = bool(members[i].type.flags & ShaderVariableFlags::SignedEnum);
2874+
VarType enumType = signedEnum ? VarType::SInt : VarType::UInt;
2875+
if(members[i].type.arrayByteStride == 1)
2876+
enumType = signedEnum ? VarType::SByte : VarType::UByte;
2877+
else if(members[i].type.arrayByteStride == 2)
2878+
enumType = signedEnum ? VarType::SShort : VarType::UShort;
2879+
else if(members[i].type.arrayByteStride == 8)
2880+
enumType = signedEnum ? VarType::SLong : VarType::ULong;
2881+
2882+
declarations += DeclareEnum(varTypeName, members[i].type.members, enumType) + lit("\n");
2883+
}
28582884
}
28592885
else if(members[i].type.baseType == VarType::Struct)
28602886
{
@@ -2891,6 +2917,23 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, ResourceId shader,
28912917
if(varName.isEmpty())
28922918
varName = QFormatStr("_child%1").arg(i);
28932919

2920+
if(members[i].type.flags & ShaderVariableFlags::RGBDisplay)
2921+
varTypeName = lit("[[rgb]] ") + varTypeName;
2922+
if(members[i].type.flags & ShaderVariableFlags::SNorm)
2923+
varTypeName = lit("[[snorm]] ") + varTypeName;
2924+
if(members[i].type.flags & ShaderVariableFlags::UNorm)
2925+
varTypeName = lit("[[unorm]] ") + varTypeName;
2926+
if(members[i].type.flags & ShaderVariableFlags::R10G10B10A2)
2927+
varTypeName = lit("[[packed(r10g10b10a2)]] ") + varTypeName;
2928+
if(members[i].type.flags & ShaderVariableFlags::R11G11B10)
2929+
varTypeName = lit("[[packed(r11g11b10)]] ") + varTypeName;
2930+
// don't print the [[hex]] for pointer types
2931+
if(members[i].type.pointerTypeID == ~0U &&
2932+
(members[i].type.flags & ShaderVariableFlags::HexDisplay))
2933+
varTypeName = lit("[[hex]] ") + varTypeName;
2934+
if(members[i].type.flags & ShaderVariableFlags::BinaryDisplay)
2935+
varTypeName = lit("[[bin]] ") + varTypeName;
2936+
28942937
if(members[i].type.rows > 1)
28952938
{
28962939
if(members[i].type.RowMajor())
@@ -2947,6 +2990,26 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, ResourceId shader,
29472990
return declarations + ret;
29482991
}
29492992

2993+
QString BufferFormatter::DeclareEnum(const QString &name, const rdcarray<ShaderConstant> &members,
2994+
VarType baseType)
2995+
{
2996+
QString ret;
2997+
2998+
const bool signedEnum = VarTypeCompType(baseType) == CompType::SInt;
2999+
3000+
ShaderVariable tmp;
3001+
tmp.columns = 1;
3002+
3003+
ret += QFormatStr("enum %1 : %2\n{\n").arg(name).arg(ToQStr(baseType));
3004+
for(const ShaderConstant &c : members)
3005+
{
3006+
tmp.value.u64v[0] = c.defaultValue;
3007+
ret += QFormatStr(" %1 = %2,\n").arg(c.name).arg(RowString(tmp, 0, baseType));
3008+
}
3009+
ret += lit("}\n");
3010+
return ret;
3011+
}
3012+
29503013
QString BufferFormatter::DeclareStruct(Packing::Rules pack, ResourceId shader, const QString &name,
29513014
const rdcarray<ShaderConstant> &members,
29523015
uint32_t requiredByteStride)
@@ -3925,7 +3988,79 @@ QString RowTypeString(const ShaderVariable &v)
39253988

39263989
#include "3rdparty/catch/catch.hpp"
39273990

3928-
TEST_CASE("round-trip via format", "[formatter]")
3991+
TEST_CASE("Round-trip struct declarations", "[formatter]")
3992+
{
3993+
BufferFormatter::Init(GraphicsAPI::Vulkan);
3994+
3995+
QString fmt;
3996+
3997+
// things not tested here:
3998+
// comments, spacing, etc for obvious reasons
3999+
// [[pad]] because it's eliminated entirely and will not round-trip
4000+
// packing rules, they are tested below
4001+
// aliased typenames like float32_t or int16_t
4002+
4003+
fmt = lit(R"(#pack(structured)
4004+
4005+
struct ptr_struct
4006+
{
4007+
[[snorm]] byte4 a;
4008+
[[unorm]] byte4 a;
4009+
[[packed(r11g11b10)]] float3 b;
4010+
[[packed(r10g10b10a2)]] uint4 c;
4011+
}
4012+
4013+
struct nested
4014+
{
4015+
float x[4];
4016+
[[hex]] uint y;
4017+
[[bin]] uint z;
4018+
[[row_major]] float3x4 w;
4019+
[[rgb]] float4 col;
4020+
[[offset(128)]]
4021+
int* p;
4022+
ptr_struct* q;
4023+
}
4024+
4025+
enum MyEnum : uint
4026+
{
4027+
A = 1,
4028+
B = 2,
4029+
C = 10,
4030+
D = 10,
4031+
}
4032+
4033+
struct root
4034+
{
4035+
float a;
4036+
int b;
4037+
uint c;
4038+
int d;
4039+
double e;
4040+
short f;
4041+
byte g;
4042+
float3 h;
4043+
float2x2 i;
4044+
int3x4 j;
4045+
uint k : 5;
4046+
uint l : 6;
4047+
uint : 3;
4048+
uint m : 7;
4049+
nested n;
4050+
MyEnum o;
4051+
}
4052+
)");
4053+
4054+
ParsedFormat parsed = BufferFormatter::ParseFormatString(fmt, 0, true);
4055+
CHECK(parsed.errors.isEmpty());
4056+
QString regenerated =
4057+
BufferFormatter::DeclareStruct(parsed.packing, ResourceId(), parsed.fixed.type.name,
4058+
parsed.fixed.type.members, parsed.fixed.type.arrayByteStride);
4059+
4060+
CHECK((fmt == regenerated));
4061+
}
4062+
4063+
TEST_CASE("Packing rules respected when round-tripping", "[formatter]")
39294064
{
39304065
BufferFormatter::Init(GraphicsAPI::Vulkan);
39314066

@@ -5202,6 +5337,86 @@ uint inthird : 14;
52025337
CHECK(parsed.fixed.type.members[3].byteOffset == 8);
52035338
CHECK(parsed.fixed.type.members[3].bitFieldOffset == 0);
52045339
CHECK(parsed.fixed.type.members[3].bitFieldSize == 14);
5340+
5341+
// check that offsets are correctly processed after a bitfield that might have left some pending space
5342+
def = R"(
5343+
uint a : 16;
5344+
uint b : 14;
5345+
5346+
[[offset(12)]]
5347+
uint c;
5348+
)";
5349+
parsed = BufferFormatter::ParseFormatString(def, 0, true);
5350+
5351+
CHECK(parsed.errors.isEmpty());
5352+
REQUIRE(parsed.fixed.type.members.size() == 3);
5353+
CHECK(parsed.fixed.type.arrayByteStride == 16);
5354+
CHECK(parsed.fixed.type.members[0].name == "a");
5355+
CHECK(parsed.fixed.type.members[0].byteOffset == 0);
5356+
CHECK(parsed.fixed.type.members[0].bitFieldOffset == 0);
5357+
CHECK(parsed.fixed.type.members[0].bitFieldSize == 16);
5358+
CHECK(parsed.fixed.type.members[1].name == "b");
5359+
CHECK(parsed.fixed.type.members[1].byteOffset == 0);
5360+
CHECK(parsed.fixed.type.members[1].bitFieldOffset == 16);
5361+
CHECK(parsed.fixed.type.members[1].bitFieldSize == 14);
5362+
CHECK(parsed.fixed.type.members[2].name == "c");
5363+
CHECK(parsed.fixed.type.members[2].byteOffset == 12);
5364+
CHECK(parsed.fixed.type.members[2].bitFieldOffset == 0);
5365+
CHECK(parsed.fixed.type.members[2].bitFieldSize == 0);
5366+
5367+
// check that an offset mid-bitfield is properly respected
5368+
def = R"(
5369+
uint a : 13;
5370+
[[offset(8)]]
5371+
uint b : 14;
5372+
)";
5373+
parsed = BufferFormatter::ParseFormatString(def, 0, true);
5374+
5375+
CHECK(parsed.errors.isEmpty());
5376+
REQUIRE(parsed.fixed.type.members.size() == 2);
5377+
CHECK(parsed.fixed.type.arrayByteStride == 12);
5378+
CHECK(parsed.fixed.type.members[0].name == "a");
5379+
CHECK(parsed.fixed.type.members[0].byteOffset == 0);
5380+
CHECK(parsed.fixed.type.members[0].bitFieldOffset == 0);
5381+
CHECK(parsed.fixed.type.members[0].bitFieldSize == 13);
5382+
CHECK(parsed.fixed.type.members[1].name == "b");
5383+
CHECK(parsed.fixed.type.members[1].byteOffset == 8);
5384+
CHECK(parsed.fixed.type.members[1].bitFieldOffset == 0);
5385+
CHECK(parsed.fixed.type.members[1].bitFieldSize == 14);
5386+
5387+
// check that non-bitfield values after bitfields have the right offset calculated
5388+
def = R"(
5389+
struct str
5390+
{
5391+
int x;
5392+
};
5393+
5394+
uint a : 13;
5395+
uint b;
5396+
uint c : 14;
5397+
str d;
5398+
)";
5399+
parsed = BufferFormatter::ParseFormatString(def, 0, true);
5400+
5401+
CHECK(parsed.errors.isEmpty());
5402+
REQUIRE(parsed.fixed.type.members.size() == 4);
5403+
CHECK(parsed.fixed.type.arrayByteStride == 16);
5404+
CHECK(parsed.fixed.type.members[0].name == "a");
5405+
CHECK(parsed.fixed.type.members[0].byteOffset == 0);
5406+
CHECK(parsed.fixed.type.members[0].bitFieldOffset == 0);
5407+
CHECK(parsed.fixed.type.members[0].bitFieldSize == 13);
5408+
CHECK(parsed.fixed.type.members[1].name == "b");
5409+
CHECK(parsed.fixed.type.members[1].byteOffset == 4);
5410+
CHECK(parsed.fixed.type.members[1].bitFieldOffset == 0);
5411+
CHECK(parsed.fixed.type.members[1].bitFieldSize == 0);
5412+
CHECK(parsed.fixed.type.members[2].name == "c");
5413+
CHECK(parsed.fixed.type.members[2].byteOffset == 8);
5414+
CHECK(parsed.fixed.type.members[2].bitFieldOffset == 0);
5415+
CHECK(parsed.fixed.type.members[2].bitFieldSize == 14);
5416+
CHECK(parsed.fixed.type.members[3].name == "d");
5417+
CHECK(parsed.fixed.type.members[3].byteOffset == 12);
5418+
CHECK(parsed.fixed.type.members[3].bitFieldOffset == 0);
5419+
CHECK(parsed.fixed.type.members[3].bitFieldSize == 0);
52055420
};
52065421

52075422
SECTION("pointers")

qrenderdoc/Code/QRDUtils.h

+2
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ struct BufferFormatter
247247

248248
static QString DeclareStruct(Packing::Rules pack, ResourceId shader, const QString &name,
249249
const rdcarray<ShaderConstant> &members, uint32_t requiredByteStride);
250+
static QString DeclareEnum(const QString &name, const rdcarray<ShaderConstant> &members,
251+
VarType baseType);
250252
};
251253

252254
QVariantList GetVariants(ResourceFormat format, const ShaderConstant &var, const byte *&data,

renderdoc/api/replay/renderdoc_tostr.inl

+1
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,7 @@ rdcstr DoStringise(const ShaderVariableFlags &el)
12061206
STRINGISE_BITFIELD_CLASS_BIT(UNorm);
12071207
STRINGISE_BITFIELD_CLASS_BIT(SNorm);
12081208
STRINGISE_BITFIELD_CLASS_BIT(Truncated);
1209+
STRINGISE_BITFIELD_CLASS_BIT(SignedEnum);
12091210
}
12101211
END_BITFIELD_STRINGISE();
12111212
}

renderdoc/api/replay/replay_enums.h

+5
Original file line numberDiff line numberDiff line change
@@ -4929,6 +4929,10 @@ displayed
49294929
.. data:: Truncated
49304930
49314931
This value was truncated when reading - the available range was exhausted.
4932+
4933+
.. data:: SignedEnum
4934+
4935+
For enums, the base type is a signed integer allowing signed values.
49324936
)");
49334937
enum class ShaderVariableFlags : uint32_t
49344938
{
@@ -4942,6 +4946,7 @@ enum class ShaderVariableFlags : uint32_t
49424946
UNorm = 0x0040,
49434947
SNorm = 0x0080,
49444948
Truncated = 0x0100,
4949+
SignedEnum = 0x0200,
49454950
};
49464951

49474952
BITMASK_OPERATORS(ShaderVariableFlags);

0 commit comments

Comments
 (0)