Merge pull request #14789 from phire/optimise_vs_uid

Optimize vertex_shader_uid_data down to 28 bytes
This commit is contained in:
Scott Mansell
2026-09-06 14:37:57 +12:00
committed by GitHub
6 changed files with 164 additions and 132 deletions

View File

@@ -19,7 +19,7 @@ namespace VideoCommon
// As pipelines encompass both shader UIDs and render states, changes to either of these should
// also increment the pipeline UID version. Incrementing the UID version will cause all UID
// caches to be invalidated.
constexpr u32 GX_PIPELINE_UID_VERSION = 8; // Last changed in PR 12185
constexpr u32 GX_PIPELINE_UID_VERSION = 9; // Last changed in PR 14789
struct GXPipelineUid
{

View File

@@ -12,36 +12,38 @@
// m_components
enum : u32
{
VB_HAS_POSMTXIDX = (1 << 1),
VB_HAS_TEXMTXIDX0 = (1 << 2),
VB_HAS_TEXMTXIDX1 = (1 << 3),
VB_HAS_TEXMTXIDX2 = (1 << 4),
VB_HAS_TEXMTXIDX3 = (1 << 5),
VB_HAS_TEXMTXIDX4 = (1 << 6),
VB_HAS_TEXMTXIDX5 = (1 << 7),
VB_HAS_TEXMTXIDX6 = (1 << 8),
VB_HAS_TEXMTXIDX7 = (1 << 9),
VB_HAS_TEXMTXIDXALL = (0xff << 2),
VB_HAS_TEXMTXIDX0 = (1 << 0),
VB_HAS_TEXMTXIDX1 = (1 << 1),
VB_HAS_TEXMTXIDX2 = (1 << 2),
VB_HAS_TEXMTXIDX3 = (1 << 3),
VB_HAS_TEXMTXIDX4 = (1 << 4),
VB_HAS_TEXMTXIDX5 = (1 << 5),
VB_HAS_TEXMTXIDX6 = (1 << 6),
VB_HAS_TEXMTXIDX7 = (1 << 7),
VB_HAS_TEXMTXIDXALL = (0xff << 0),
VB_HAS_POSMTXIDX = (1 << 8),
// VB_HAS_POS=0, // Implied, it always has pos! don't bother testing
VB_HAS_NORMAL = (1 << 10),
VB_HAS_TANGENT = (1 << 11),
VB_HAS_BINORMAL = (1 << 12),
VB_HAS_NORMAL = (1 << 9),
VB_HAS_TANGENT = (1 << 10),
VB_HAS_BINORMAL = (1 << 11),
VB_COL_SHIFT = 13,
VB_HAS_COL0 = (1 << 13),
VB_HAS_COL1 = (1 << 14),
VB_COL_SHIFT = 12,
VB_HAS_COL0 = (1 << 12),
VB_HAS_COL1 = (1 << 13),
VB_HAS_SHARED = 0x3f << 8,
VB_HAS_UV0 = (1 << 15),
VB_HAS_UV1 = (1 << 16),
VB_HAS_UV2 = (1 << 17),
VB_HAS_UV3 = (1 << 18),
VB_HAS_UV4 = (1 << 19),
VB_HAS_UV5 = (1 << 20),
VB_HAS_UV6 = (1 << 21),
VB_HAS_UV7 = (1 << 22),
VB_HAS_UVALL = (0xff << 15),
VB_HAS_UVTEXMTXSHIFT = 13,
VB_HAS_UV0 = (1 << 16),
VB_HAS_UV1 = (1 << 17),
VB_HAS_UV2 = (1 << 18),
VB_HAS_UV3 = (1 << 19),
VB_HAS_UV4 = (1 << 20),
VB_HAS_UV5 = (1 << 21),
VB_HAS_UV6 = (1 << 22),
VB_HAS_UV7 = (1 << 23),
VB_HAS_UVALL = (0xff << 16),
VB_HAS_UVTEXMTXSHIFT = 16,
};
struct AttributeFormat

View File

@@ -125,13 +125,12 @@ GXPipelineUid ApplyDriverBugs(const GXPipelineUid& in)
vertex_shader_uid_data* vs = out.vs_uid.GetUidData();
const PortableVertexDeclaration& decl = out.vertex_format->GetVertexDeclaration();
vs->position_has_3_elems = decl.position.components >= 3;
vs->texcoord_elem_count = 0;
for (int i = 0; i < 8; i++)
{
if (decl.texcoords[i].enable)
{
ASSERT(decl.texcoords[i].components <= 3);
vs->texcoord_elem_count |= decl.texcoords[i].components << (i * 2);
vs->texGenInfo[i].texcoord_elem_count = decl.texcoords[i].components;
}
}
out.vertex_format = nullptr;

View File

@@ -22,52 +22,68 @@ VertexShaderUid GetVertexShaderUid()
VertexShaderUid out;
vertex_shader_uid_data* const uid_data = out.GetUidData();
uid_data->numTexGens = xfmem.numTexGen.numTexGens;
uid_data->components = VertexLoaderManager::g_current_components;
uid_data->numColorChans = xfmem.numChan.numColorChans;
uid_data->components =
VertexLoaderManager::g_current_components & (VB_HAS_SHARED | VB_HAS_TEXMTXIDXALL);
// Move UV components into texcoord_elem_count
for (u32 i = 0; i < 8; i++)
{
if (VertexLoaderManager::g_current_components & (VB_HAS_UV0 << i))
{
// Hardcode to 2 components (ApplyDriverBugs will replace this with an exact count if needed)
uid_data->texGenInfo[i].texcoord_elem_count = 2;
}
}
GetLightingShaderUid(uid_data->lighting);
// transform texcoords
for (u32 i = 0; i < uid_data->numTexGens; ++i)
{
auto& texinfo = uid_data->texMtxInfo[i];
auto& texinfo = uid_data->texGenInfo[i];
// sourcerow, inputform and texgentype each have an extra bit that gets ignored later.
// Validate and eliminate them now to save space in the UID.
ASSERT(xfmem.texMtxInfo[i].sourcerow <= SourceRow::Tex7);
texinfo.sourcerow = xfmem.texMtxInfo[i].sourcerow;
texinfo.texgentype = xfmem.texMtxInfo[i].texgentype;
texinfo.inputform = xfmem.texMtxInfo[i].inputform;
texinfo.inputform = xfmem.texMtxInfo[i].inputform == TexInputForm::ABC1 ? TexInputForm::ABC1 :
TexInputForm::AB11;
auto texgentype = xfmem.texMtxInfo[i].texgentype;
// first transformation
switch (texinfo.texgentype)
switch (texgentype)
{
default:
case TexGenType::Regular:
texinfo.is_regular_texgen = true;
texinfo.regular.projection = xfmem.texMtxInfo[i].projection;
// only put dualTexTrans_enabled in UID if we have at least one regular texgen.
uid_data->dualTexTrans_enabled = xfmem.dualTexTrans.enabled;
// CHECKME: does this only work for regular tex gen types?
if (uid_data->dualTexTrans_enabled)
{
texinfo.regular.postmtx_index = xfmem.postMtxInfo[i].index;
texinfo.regular.postmtx_normalize = xfmem.postMtxInfo[i].normalize;
}
break;
case TexGenType::EmbossMap: // calculate tex coords into bump map
texinfo.is_regular_texgen = false;
texinfo.other.texgentype = texgentype;
// transform the light dir into tangent space
texinfo.other.emboss_sourceshift = xfmem.texMtxInfo[i].embosssourceshift;
if ((uid_data->components & (VB_HAS_TANGENT | VB_HAS_BINORMAL)) != 0)
{
// transform the light dir into tangent space
texinfo.embosslightshift = xfmem.texMtxInfo[i].embosslightshift;
texinfo.embosssourceshift = xfmem.texMtxInfo[i].embosssourceshift;
}
else
{
texinfo.embosssourceshift = xfmem.texMtxInfo[i].embosssourceshift;
}
texinfo.other.emboss_lightshift = xfmem.texMtxInfo[i].embosslightshift;
break;
case TexGenType::Color0:
case TexGenType::Color1:
texinfo.is_regular_texgen = false;
texinfo.other.texgentype = texgentype;
break;
case TexGenType::Regular:
default:
uid_data->texMtxInfo_n_projection |= static_cast<u32>(xfmem.texMtxInfo[i].projection.Value())
<< i;
break;
}
uid_data->dualTexTrans_enabled = xfmem.dualTexTrans.enabled;
// CHECKME: does this only work for regular tex gen types?
if (uid_data->dualTexTrans_enabled && texinfo.texgentype == TexGenType::Regular)
{
auto& postInfo = uid_data->postMtxInfo[i];
postInfo.index = xfmem.postMtxInfo[i].index;
postInfo.normalize = xfmem.postMtxInfo[i].normalize;
}
}
@@ -163,10 +179,10 @@ static void WriteTexCoordTransforms(APIType api_type, const ShaderHostConfig& ho
{
for (u32 i = 0; i < uid_data->numTexGens; ++i)
{
auto& texinfo = uid_data->texMtxInfo[i];
auto& texinfo = uid_data->texGenInfo[i];
out.Write("vec3 dolphin_transform_texcoord{}(vec4 coord)\n", i);
out.Write("{{\n");
if (texinfo.texgentype != TexGenType::Regular)
if (!texinfo.is_regular_texgen)
{
out.Write("\treturn vec3(coord.xyz);\n");
}
@@ -176,7 +192,7 @@ static void WriteTexCoordTransforms(APIType api_type, const ShaderHostConfig& ho
if ((uid_data->components & (VB_HAS_TEXMTXIDX0 << i)) != 0)
{
out.Write("\tint tmp = int(rawtex{}.z);\n", i);
if (static_cast<TexSize>((uid_data->texMtxInfo_n_projection >> i) & 1) == TexSize::STQ)
if (texinfo.regular.projection == TexSize::STQ)
{
out.Write("\tresult = vec3(dot(coord, " I_TRANSFORMMATRICES
"[tmp]), dot(coord, " I_TRANSFORMMATRICES
@@ -190,7 +206,7 @@ static void WriteTexCoordTransforms(APIType api_type, const ShaderHostConfig& ho
}
else
{
if (static_cast<TexSize>((uid_data->texMtxInfo_n_projection >> i) & 1) == TexSize::STQ)
if (texinfo.regular.projection == TexSize::STQ)
{
out.Write("\tresult = vec3(dot(coord, " I_TEXMATRICES "[{}]), dot(coord, " I_TEXMATRICES
"[{}]), dot(coord, " I_TEXMATRICES "[{}]));\n",
@@ -206,14 +222,14 @@ static void WriteTexCoordTransforms(APIType api_type, const ShaderHostConfig& ho
// CHECKME: does this only work for regular tex gen types?
if (uid_data->dualTexTrans_enabled)
{
auto& postInfo = uid_data->postMtxInfo[i];
auto postmtx_index = texinfo.regular.postmtx_index;
out.Write("\tvec4 P0 = " I_POSTTRANSFORMMATRICES "[{}];\n"
"\tvec4 P1 = " I_POSTTRANSFORMMATRICES "[{}];\n"
"\tvec4 P2 = " I_POSTTRANSFORMMATRICES "[{}];\n",
postInfo.index & 0x3f, (postInfo.index + 1) & 0x3f, (postInfo.index + 2) & 0x3f);
postmtx_index & 0x3f, (postmtx_index + 1) & 0x3f, (postmtx_index + 2) & 0x3f);
if (postInfo.normalize)
if (texinfo.regular.postmtx_normalize)
out.Write("\tresult = normalize(result);\n");
// multiply by postmatrix
@@ -313,7 +329,7 @@ static void WriteVertexDefines(APIType, const ShaderHostConfig&,
for (u32 i = 0; i < uid_data->numTexGens; i++)
{
if ((uid_data->components & (VB_HAS_UV0 << i)) != 0)
if (uid_data->texGenInfo[i].texcoord_elem_count != 0)
{
out.Write("#define HAS_TEXTURE_COORD_{} 1\n", i);
}
@@ -415,7 +431,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
{
const u32 has_texmtx = (uid_data->components & (VB_HAS_TEXMTXIDX0 << i));
if ((uid_data->components & (VB_HAS_UV0 << i)) != 0 || has_texmtx != 0)
if (uid_data->texGenInfo[i].texcoord_elem_count != 0 || has_texmtx != 0)
{
out.Write("ATTRIBUTE_LOCATION({:s}) in float{} rawtex{};\n", ShaderAttrib::TexCoord0 + i,
has_texmtx != 0 ? 3 : 2, i);
@@ -472,30 +488,25 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
}
for (int i = 0; i < 8; i++)
{
if (uid_data->components & (VB_HAS_UV0 << i))
switch (uid_data->texGenInfo[i].texcoord_elem_count)
{
u32 ncomponents = (uid_data->texcoord_elem_count >> (2 * i)) & 3;
if (ncomponents < 2)
{
out.Write(" float tex{};\n", i);
input_extract.Write("float3 rawtex{0} = float3(i.tex{0}, 0.0f, 0.0f);\n", i);
}
else if (ncomponents == 2)
{
out.Write(" float tex{0}_0;\n"
" float tex{0}_1;\n",
i);
input_extract.Write("float3 rawtex{0} = float3(i.tex{0}_0, i.tex{0}_1, 0.0f);\n", i);
}
else
{
out.Write(" float tex{0}_0;\n"
" float tex{0}_1;\n"
" float tex{0}_2;\n",
i);
input_extract.Write("float3 rawtex{0} = float3(i.tex{0}_0, i.tex{0}_1, i.tex{0}_2);\n",
i);
}
case 1:
out.Write(" float tex{};\n", i);
input_extract.Write("float3 rawtex{0} = float3(i.tex{0}, 0.0f, 0.0f);\n", i);
break;
case 2:
out.Write(" float tex{0}_0;\n"
" float tex{0}_1;\n",
i);
input_extract.Write("float3 rawtex{0} = float3(i.tex{0}_0, i.tex{0}_1, 0.0f);\n", i);
break;
case 3:
out.Write(" float tex{0}_0;\n"
" float tex{0}_1;\n"
" float tex{0}_2;\n",
i);
input_extract.Write("float3 rawtex{0} = float3(i.tex{0}_0, i.tex{0}_1, i.tex{0}_2);\n", i);
break;
}
}
out.Write("}};\n\n"
@@ -633,7 +644,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
for (u32 i = 0; i < uid_data->numTexGens; ++i)
{
auto& texinfo = uid_data->texMtxInfo[i];
auto& texinfo = uid_data->texGenInfo[i];
out.Write("\t{{\n");
out.Write("\t\tvec4 coord = vec4(0.0, 0.0, 1.0, 1.0);\n");
@@ -649,7 +660,9 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
}
break;
case SourceRow::Colors:
ASSERT(texinfo.texgentype == TexGenType::Color0 || texinfo.texgentype == TexGenType::Color1);
ASSERT(!texinfo.is_regular_texgen);
ASSERT(texinfo.other.texgentype == TexGenType::Color0 ||
texinfo.other.texgentype == TexGenType::Color1);
break;
case SourceRow::BinormalT:
if ((uid_data->components & VB_HAS_TANGENT) != 0)
@@ -664,9 +677,8 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
}
break;
default:
ASSERT(texinfo.sourcerow >= SourceRow::Tex0 && texinfo.sourcerow <= SourceRow::Tex7);
u32 texnum = static_cast<u32>(texinfo.sourcerow) - static_cast<u32>(SourceRow::Tex0);
if ((uid_data->components & (VB_HAS_UV0 << (texnum))) != 0)
if (uid_data->texGenInfo[texnum].texcoord_elem_count != 0)
{
out.Write("\t\tcoord = vec4(rawtex{}.x, rawtex{}.y, 1.0, 1.0);\n", texnum, texnum);
}
@@ -917,23 +929,25 @@ void WriteVertexBody(APIType api_type, const ShaderHostConfig& host_config,
for (u32 i = 0; i < uid_data->numTexGens; ++i)
{
auto& texinfo = uid_data->texMtxInfo[i];
auto& texinfo = uid_data->texGenInfo[i];
switch (texinfo.texgentype)
auto texgentype = texinfo.is_regular_texgen ? TexGenType::Regular : texinfo.other.texgentype;
switch (texgentype)
{
case TexGenType::EmbossMap: // calculate tex coords into bump map
out.Write("\t{{\n");
// transform the light dir into tangent space
out.Write("\t\tvec3 ldir = normalize(" LIGHT_POS ".xyz - vertex_output.position.xyz);\n",
LIGHT_POS_PARAMS(texinfo.embosslightshift));
LIGHT_POS_PARAMS(texinfo.other.emboss_lightshift));
out.Write("\t\tvec3 tangent = vertex_input.tangent * dolphin_normal_matrix();\n");
out.Write("\t\tvec3 binormal = vertex_input.binormal * dolphin_normal_matrix();\n");
out.Write("\t\tvertex_output.texture_coord_{}.xyz = vertex_output.texture_coord_{}.xyz + "
"vec3(dot(ldir, tangent), "
"dot(ldir, binormal), 0.0);\n",
i, texinfo.embosssourceshift);
i, texinfo.other.emboss_sourceshift);
out.Write("\t}}\n");
break;
case TexGenType::Color0:

View File

@@ -10,9 +10,10 @@
#include "VideoCommon/ShaderGenCommon.h"
enum class APIType;
enum class TexInputForm : u32;
enum class TexGenType : u32;
enum class SourceRow : u32;
enum class TexInputForm : u8;
enum class TexGenType : u8;
enum class SourceRow : u8;
enum class TexSize : u8;
enum class VSExpand : u32;
// TODO should be reordered
@@ -50,41 +51,57 @@ constexpr ShaderAttrib operator+(ShaderAttrib attrib, int offset)
return static_cast<ShaderAttrib>(static_cast<u8>(attrib) + offset);
}
#pragma pack(1)
struct vertex_shader_uid_data
// Currently optimized to 28 bytes (with 8 bits spare)
// The smaller (and less redundant) this is, the better.
// Though, it probably does like to be somewhat aligned.
struct alignas(4) vertex_shader_uid_data
{
u32 NumValues() const { return sizeof(vertex_shader_uid_data); }
u32 components : 23;
u32 numTexGens : 4;
u32 numColorChans : 2;
u32 dualTexTrans_enabled : 1;
VSExpand vs_expand : 2;
u32 components : 14;
u32 position_has_3_elems : 1;
u32 dualTexTrans_enabled : 1;
u32 numTexGens : 4; // if more bits are needed, this could be eliminated by somehow marking the
// first unused texGen as empty.
u32 numColorChans : 2; // Output color channels.
VSExpand vs_expand : 2;
u16 texcoord_elem_count; // 2 bits per texcoord input
u16 texMtxInfo_n_projection; // Stored separately to guarantee that the texMtxInfo struct is
// 8 bits wide
u32 pad : 8;
// texInfo is optimized to fit all per-texgen config into just 16-bits.
// But it did require a union
struct
{
TexInputForm inputform : 2;
TexGenType texgentype : 3;
SourceRow sourcerow : 5;
u32 embosssourceshift : 3;
u32 embosslightshift : 3;
} texMtxInfo[8];
u8 texcoord_elem_count : 2;
TexInputForm inputform : 1;
SourceRow sourcerow : 4;
bool is_regular_texgen : 1; // union tag
union
{
struct
{
u8 postmtx_index : 6;
u8 postmtx_normalize : 1;
TexSize projection : 1;
} regular;
struct
{
TexGenType texgentype : 2;
// these are only used by EmbossMap texgen.
u8 emboss_sourceshift : 3;
u8 emboss_lightshift : 3;
} other;
};
} texGenInfo[8];
struct
{
u32 index : 6;
u32 normalize : 1;
u32 pad : 1;
} postMtxInfo[8];
static_assert(sizeof(texGenInfo[0]) == 2, "texGenInfo should be 2 bytes per texgen");
LightingUidData lighting;
};
#pragma pack()
// We do need to make sure lighting is correctly aligned
static_assert(offsetof(vertex_shader_uid_data, lighting) % alignof(LightingUidData) == 0);
static_assert(offsetof(vertex_shader_uid_data, texGenInfo) % alignof(u32) == 0);
static_assert(sizeof(vertex_shader_uid_data) == 28, "vertex_shader_uid_data should be 28 bytes");
using VertexShaderUid = ShaderUid<vertex_shader_uid_data>;

View File

@@ -18,7 +18,7 @@ constexpr size_t NUM_XF_COLOR_CHANNELS = 2;
// Lighting
// Projection
enum class TexSize : u32
enum class TexSize : u8
{
ST = 0,
STQ = 1
@@ -30,7 +30,7 @@ struct fmt::formatter<TexSize> : EnumFormatter<TexSize::STQ>
};
// Input form
enum class TexInputForm : u32
enum class TexInputForm : u8
{
AB11 = 0,
ABC1 = 1
@@ -63,7 +63,7 @@ struct fmt::formatter<NormalCount> : EnumFormatter<NormalCount::Invalid>
};
// Texture generation type
enum class TexGenType : u32
enum class TexGenType : u8
{
Regular = 0,
EmbossMap = 1, // Used when bump mapping
@@ -83,7 +83,7 @@ struct fmt::formatter<TexGenType> : EnumFormatter<TexGenType::Color1>
};
// Source row
enum class SourceRow : u32
enum class SourceRow : u8
{
Geom = 0, // Input is abc
Normal = 1, // Input is abc
@@ -318,11 +318,11 @@ struct fmt::formatter<INVTXSPEC>
union TexMtxInfo
{
BitField<0, 1, u32> unknown;
BitField<1, 1, TexSize> projection;
BitField<2, 1, TexInputForm> inputform;
BitField<1, 1, TexSize, u32> projection;
BitField<2, 1, TexInputForm, u32> inputform;
BitField<3, 1, u32> unknown2;
BitField<4, 3, TexGenType> texgentype;
BitField<7, 5, SourceRow> sourcerow;
BitField<4, 3, TexGenType, u32> texgentype;
BitField<7, 5, SourceRow, u32> sourcerow;
BitField<12, 3, u32> embosssourceshift; // what generated texcoord to use
BitField<15, 3, u32> embosslightshift; // light index that is used
u32 hex;