Fix GX indirect tile window rendering (#218)

Co-authored-by: Thomas Ryan <contact@thomasryan.ca>
This commit is contained in:
Thomas Ryan
2026-06-02 12:04:31 -04:00
committed by GitHub
parent 49e61d7dad
commit fb156d78ca
5 changed files with 70 additions and 46 deletions
+15 -24
View File
@@ -173,35 +173,26 @@ void GXSetTevIndBumpST(GXTevStageID tevStage, GXIndTexStageID indStage, GXIndTex
GX_ITW_OFF, GX_TRUE, GX_FALSE, GX_ITBA_OFF);
}
void GXSetTevIndTile(GXTevStageID tevStage, GXIndTexStageID indStage, u16 tileSizeS, u16 tileSizeT, u16 tileSizeS_exp,
u16 tileSizeT_exp, GXIndTexFormat fmt, GXIndTexMtxID matrixSel, GXIndTexBiasSel biasSel,
void GXSetTevIndTile(GXTevStageID tevStage, GXIndTexStageID indStage, u16 tileSizeS, u16 tileSizeT, u16 tileSpacingS,
u16 tileSpacingT, GXIndTexFormat fmt, GXIndTexMtxID matrixSel, GXIndTexBiasSel biasSel,
GXIndTexAlphaSel alphaSel) {
// Convert tile size exponents to wrap modes
auto sizeToWrap = [](u16 sizeExp) -> GXIndTexWrap {
switch (sizeExp) {
case 8: return GX_ITW_256;
case 7: return GX_ITW_128;
case 6: return GX_ITW_64;
case 5: return GX_ITW_32;
case 4: return GX_ITW_16;
auto sizeToWrap = [](u16 size) -> GXIndTexWrap {
switch (size) {
case 256: return GX_ITW_256;
case 128: return GX_ITW_128;
case 64: return GX_ITW_64;
case 32: return GX_ITW_32;
case 16: return GX_ITW_16;
default: return GX_ITW_0;
}
};
GXIndTexWrap wrapS = sizeToWrap(tileSizeS_exp);
GXIndTexWrap wrapT = sizeToWrap(tileSizeT_exp);
GXIndTexWrap wrapS = sizeToWrap(tileSizeS);
GXIndTexWrap wrapT = sizeToWrap(tileSizeT);
// Set up the indirect matrix for tiling
f32 mtx[2][3] = {};
s8 scaleExp = 0;
if (biasSel == GX_ITB_S || biasSel == GX_ITB_ST || biasSel == GX_ITB_SU || biasSel == GX_ITB_STU) {
mtx[0][0] = static_cast<f32>(tileSizeS);
scaleExp = tileSizeS_exp;
}
if (biasSel == GX_ITB_T || biasSel == GX_ITB_ST || biasSel == GX_ITB_TU || biasSel == GX_ITB_STU) {
mtx[1][1] = static_cast<f32>(tileSizeT);
scaleExp = tileSizeT_exp;
}
GXSetIndTexMtx(matrixSel, mtx, scaleExp);
GXSetTevIndirect(tevStage, indStage, fmt, biasSel, matrixSel, wrapS, wrapT, GX_FALSE, GX_FALSE, alphaSel);
mtx[0][0] = static_cast<f32>(tileSpacingS) / 1024.0f;
mtx[1][1] = static_cast<f32>(tileSpacingT) / 1024.0f;
GXSetIndTexMtx(matrixSel, mtx, 10);
GXSetTevIndirect(tevStage, indStage, fmt, biasSel, matrixSel, wrapS, wrapT, GX_FALSE, GX_TRUE, alphaSel);
}
}
+21 -20
View File
@@ -1191,18 +1191,19 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
continue;
}
const auto& indStage = config.indStages[i];
std::string scaleExpr;
if (indStage.scaleS == GX_ITS_1 && indStage.scaleT == GX_ITS_1) {
scaleExpr = fmt::format("tex{0}_uv", underlying(indStage.texCoordId));
} else {
scaleExpr = fmt::format("tex{0}_uv * vec2f({1}, {2})", underlying(indStage.texCoordId),
ind_scale(indStage.scaleS), ind_scale(indStage.scaleT));
}
const u32 texCoordId = underlying(indStage.texCoordId);
const u32 texMapId = underlying(indStage.texMapId);
// GX applies the SU texture-coordinate scale before the indirect stage scale.
// The shader carries normalized UVs, so convert that texel-space result back
// into normalized coordinates for the indirect texture sample.
const auto scaleExpr =
fmt::format("tex{0}_uv * ubuf.texcoord_scale[{0}].xy * vec2f({1}, {2}) / ubuf.tex{3}_size_bias.xy",
texCoordId, ind_scale(indStage.scaleS), ind_scale(indStage.scaleT), texMapId);
fragmentFnPre += fmt::format(
"\n // Indirect stage {0}"
"\n var t_IndTexCoord{0} = 255.0 * textureSampleBias(tex{1}, tex{1}_samp, {2}, "
"ubuf.tex{1}_size_bias.z).abg;",
i, underlying(indStage.texMapId), scaleExpr);
i, texMapId, scaleExpr);
}
if (info.usedIndStages.any()) {
fragmentFnPre += "\n var t_TexCoord = vec2f(0.0);";
@@ -1283,23 +1284,21 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
") * ind{0}_c1.z",
i);
} else if (stage.indTexMtxId >= GX_ITM_S0 && stage.indTexMtxId <= GX_ITM_S2 && hasBaseCoord) {
// Dynamic S: result = uv * texDim * ind_coord.x * scale / 256
// Dynamic S: result = scaled texcoord * ind_coord.x * scale / 256
u32 mtxIdx = stage.indTexMtxId - GX_ITM_S0;
u32 regTexCoord = underlying(stage.texCoordId);
u32 regTexMap = underlying(stage.texMapId);
indirectOffsetTexel = fmt::format(
"tex{1}_uv * ubuf.tex{2}_size_bias.xy * ind{0}_coord.x"
" * ubuf.ind_mtx[{3}][1][2] / 256.0",
i, regTexCoord, regTexMap, mtxIdx);
"tex{1}_uv * ubuf.texcoord_scale[{1}].xy * ind{0}_coord.x"
" * ubuf.ind_mtx[{2}][1][2] / 256.0",
i, regTexCoord, mtxIdx);
} else if (stage.indTexMtxId >= GX_ITM_T0 && stage.indTexMtxId <= GX_ITM_T2 && hasBaseCoord) {
// Dynamic T: result = uv * texDim * ind_coord.y * scale / 256
// Dynamic T: result = scaled texcoord * ind_coord.y * scale / 256
u32 mtxIdx = stage.indTexMtxId - GX_ITM_T0;
u32 regTexCoord = underlying(stage.texCoordId);
u32 regTexMap = underlying(stage.texMapId);
indirectOffsetTexel = fmt::format(
"tex{1}_uv * ubuf.tex{2}_size_bias.xy * ind{0}_coord.y"
" * ubuf.ind_mtx[{3}][1][2] / 256.0",
i, regTexCoord, regTexMap, mtxIdx);
"tex{1}_uv * ubuf.texcoord_scale[{1}].xy * ind{0}_coord.y"
" * ubuf.ind_mtx[{2}][1][2] / 256.0",
i, regTexCoord, mtxIdx);
}
}
@@ -1330,12 +1329,11 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
std::string baseCoordExpr;
if (hasBaseCoord) {
u32 texCoordId = underlying(stage.texCoordId);
u32 texMapId = underlying(stage.texMapId);
if (useSimpleCoords) {
baseCoordExpr = fmt::format("tex{}_uv", texCoordId);
} else {
fragmentFnPre +=
fmt::format("\n var ind{0}_texel = tex{1}_uv * ubuf.tex{2}_size_bias.xy;", i, texCoordId, texMapId);
fmt::format("\n var ind{0}_texel = tex{1}_uv * ubuf.texcoord_scale[{1}].xy;", i, texCoordId);
baseCoordExpr = fmt::format("ind{}_texel", i);
}
}
@@ -1430,6 +1428,9 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
}
fragmentFn += "\n prev = vec4f(mix(prev.rgb, ubuf.fog.color.rgb, clamp(fogZ, 0.0, 1.0)), prev.a);";
}
if (info.usedIndStages.any()) {
uniBufAttrs += fmt::format("\n texcoord_scale: array<vec4f, {}>,", MaxTexCoord);
}
if (info.usedIndTexMtxs.any()) {
uniBufAttrs += "\n ind_mtx: array<mat2x4f, 3>,";
}
+9
View File
@@ -306,6 +306,9 @@ ShaderInfo build_shader_info(const ShaderConfig& config) noexcept {
info.usesFog = true;
info.uniformSize += sizeof(Fog);
}
if (info.usedIndStages.any()) {
info.uniformSize += MaxTexCoord * sizeof(Vec4<float>);
}
if (info.usedIndTexMtxs.any()) {
info.uniformSize += MaxIndTexMtxs * sizeof(Mat2x4<float>);
}
@@ -446,6 +449,12 @@ gfx::Range build_uniform(const ShaderInfo& info, u32 vtxStart, const BindGroupRa
Fog fog{.color = state.color, .a = state.a, .b = state.b, .c = state.c};
buf.append(fog);
}
if (info.usedIndStages.any()) {
for (const auto& scale : g_gxState.texCoordScales) {
buf.append(
Vec4{static_cast<f32>(scale.scaleS) + 1.0f, static_cast<f32>(scale.scaleT) + 1.0f, 0.0f, 0.0f});
}
}
if (info.usedIndTexMtxs.any()) {
for (int i = 0; i < MaxIndTexMtxs; ++i) {
const auto& mtx = g_gxState.indTexMtxs[i];