Skip to content

Commit e6cf63e

Browse files
committed
Add GPU-side texture format channel fixup via shader constants
D3D11 lacks native luminance texture formats and requires CPU-side per-pixel conversion for some 32-bit channel orders. This commit adds a constant-driven channel swizzle in both pixel shader paths (Fixed Function and Programmatic/NV2A combiner) that handles format differences entirely on the GPU. New TEXFMTFIXUP per-texture-stage constant with five modes: 0 = identity (no fixup) 1 = .gbar (X_D3DFMT_B8G8R8A8 uploaded raw as R8G8B8A8_UNORM) 2 = .abgr (X_D3DFMT_R8G8B8A8 uploaded raw as R8G8B8A8_UNORM) 3 = luminance: (L,0,0,1) -> (L,L,L,1) for L8/L16 on D3D11 4 = alpha-luminance: (L,A,0,1) -> (L,L,L,A) for A8L8 on D3D11 Changes: - FixedFunctionPixelShader.hlsli: Add TEXFMTFIXUP field to PsTextureStageState and shared constant definitions - FixedFunctionPixelShader.hlsl: Apply fixup after texture sampling - CxbxPixelShaderTemplate.hlsl: Add TEXFMTFIXUP at c42, new ApplyTexFmtFixup() helper, apply in Sample2D/3D/6F - XbPixelShaderCompiler.cpp: Add CxbxGetTexFmtFixup() that detects Xbox texture format per stage; set constants for both FF and programmatic PS paths; bump PSH_XBOX_CONSTANT_MAX to 43 - HostResourceCreate.cpp: On D3D11, skip CPU conversion for B8G8R8A8 and R8G8B8A8 — upload raw bytes as R8G8B8A8_UNORM (the shader swizzle handles the channel reorder) - Backend_D3D11.h: Bump CXBX_D3D11_PS_CB_COUNT to 43 GPU-side benefits: - Luminance formats (L8, A8L8, L16) now produce correct colors instead of red-only output (fixes Halo lightmaps, etc.) - B8G8R8A8 and R8G8B8A8 textures skip CPU per-pixel conversion loop entirely — raw data stays on GPU with zero CPU touch - [branch] on uniform ensures zero-cost for the common identity case
1 parent 8ff1ce2 commit e6cf63e

6 files changed

Lines changed: 85 additions & 2 deletions

File tree

src/core/hle/D3D8/Rendering/Backend_D3D11.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ extern IDirect3DSurface *g_pD3DCurrentHostRenderTarget;
4545
static const UINT CXBX_D3D11_VS_CB_SLOT = 0;
4646
static const UINT CXBX_D3D11_VS_CB_COUNT = 256;
4747
static const UINT CXBX_D3D11_PS_CB_SLOT = 0;
48-
static const UINT CXBX_D3D11_PS_CB_COUNT = 42;
48+
static const UINT CXBX_D3D11_PS_CB_COUNT = 43;
4949

5050
// Constant buffers (created in RenderGlobals.cpp device init, used by Backend_D3D11.cpp)
5151
extern ID3D11Buffer *g_pD3D11VSConstantBuffer;

src/core/hle/D3D8/Rendering/CxbxPixelShaderTemplate.hlsl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ uniform const float4 LUM[4] : register(c35); // Note : PSH_XBOX_CONSTANT_LUM for
7676
uniform const float FRONTFACE_FACTOR : register(c39); // Note : PSH_XBOX_CONSTANT_FRONTFACE_FACTOR
7777
uniform const float4 FOGINFO : register(c40);
7878
uniform const float FOGENABLE : register(c41);
79+
uniform const float4 TEXFMTFIXUP : register(c42); // D3D11: per-stage texture format channel fixup (0=identity, 1=.gbar, 2=.abgr, 3=luminance, 4=alpha-luminance)
7980

8081
#define CM_LT(c) if(c < 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_LT
8182
#define CM_GE(c) if(c >= 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_GE
@@ -324,8 +325,20 @@ void PerformAlphaKill(const float AlphaKill, float4 t)
324325
}
325326
#endif
326327

328+
float4 ApplyTexFmtFixup(float4 t, float fixup)
329+
{
330+
[branch] if (fixup != 0) {
331+
if (fixup == 1) return t.gbar; // B8G8R8A8 uploaded as R8G8B8A8
332+
if (fixup == 2) return t.abgr; // R8G8B8A8 uploaded as R8G8B8A8
333+
if (fixup == 3) return float4(t.r, t.r, t.r, t.a); // Luminance: R→(R,R,R,A)
334+
if (fixup == 4) return float4(t.r, t.r, t.r, t.g); // Alpha-luminance: RG→(R,R,R,G)
335+
}
336+
return t;
337+
}
338+
327339
float4 PostProcessTexel(const int ts, float4 t)
328340
{
341+
t = ApplyTexFmtFixup(t, TEXFMTFIXUP[ts]);
329342
// TODO : Figure out in which order the following operations should be performed :
330343
t = PerformColorSign(COLORSIGN[ts], t);
331344
t = PerformColorKeyOp(COLORKEYOP[ts].x, COLORKEYCOLOR[ts], t);

src/core/hle/D3D8/Rendering/FixedFunctionPixelShader.hlsl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,14 @@ TextureArgs ExecuteTextureStage(
243243
else if (type == SAMPLE_CUBE)
244244
t = texCUBE(samplers[i], TexCoord.xyz);
245245

246+
// Apply texture format channel fixup (D3D11: luminance replication, channel swizzle)
247+
[branch] if (stage.TEXFMTFIXUP != TEXFMTFIXUP_IDENTITY) {
248+
if (stage.TEXFMTFIXUP == TEXFMTFIXUP_GBAR) t = t.gbar;
249+
else if (stage.TEXFMTFIXUP == TEXFMTFIXUP_ABGR) t = t.abgr;
250+
else if (stage.TEXFMTFIXUP == TEXFMTFIXUP_LUM) t = float4(t.r, t.r, t.r, t.a);
251+
else if (stage.TEXFMTFIXUP == TEXFMTFIXUP_ALUM) t = float4(t.r, t.r, t.r, t.g);
252+
}
253+
246254
// Bump environment mapping with luminance special case
247255
if (previousOp == X_D3DTOP_BUMPENVMAPLUMINANCE) {
248256
// Multiply sampled texture rgb values by L'

src/core/hle/D3D8/Rendering/FixedFunctionPixelShader.hlsli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,16 @@ namespace FixedFunctionPixelShader {
115115
// BORDERCOLOR // = 29; set on sampler
116116
alignas(16) float4 COLORKEYCOLOR; // = 30; Xbox extension!
117117
// UNSUPPORTED // = 31; // Note : Somehow, this one comes through D3DDevice_SetTextureStageStateNotInline sometimes
118+
alignas(16) float TEXFMTFIXUP; // D3D11: texture format channel fixup (0=identity, 1=.gbar, 2=.abgr, 3=luminance .rrra, 4=alpha-luminance .rrrg)
118119
};
119120

121+
// Texture format fixup constants (shared between C++ and HLSL)
122+
const float TEXFMTFIXUP_IDENTITY = 0;
123+
const float TEXFMTFIXUP_GBAR = 1; // B8G8R8A8 uploaded as R8G8B8A8
124+
const float TEXFMTFIXUP_ABGR = 2; // R8G8B8A8 uploaded as R8G8B8A8
125+
const float TEXFMTFIXUP_LUM = 3; // Luminance: R8→(R,R,R,1)
126+
const float TEXFMTFIXUP_ALUM = 4; // Alpha-luminance: R8G8→(R,R,R,G);
127+
120128
// This state is compiled into the shader
121129
// Values correspond to XD3D8 version of D3DTEXTURESTAGESTATETYPE
122130
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtexturestagestatetype

src/core/hle/D3D8/Rendering/HostResourceCreate.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,17 @@ EMUFORMAT PCFormat;
263263
// Unset D3DUSAGE_DEPTHSTENCIL: It's not possible for ARGB textures to be depth stencils
264264
// Fixes CreateTexture error in Virtua Cop 3 (Chihiro)
265265
D3DUsage &= ~D3DUSAGE_DEPTHSTENCIL;
266+
267+
#ifdef CXBX_USE_D3D11
268+
// D3D11: For 32-bit formats that differ only in channel order,
269+
// skip CPU conversion and upload raw bytes as R8G8B8A8_UNORM.
270+
// The pixel shader applies the correct channel swizzle via TEXFMTFIXUP.
271+
if (X_Format == xbox::X_D3DFMT_B8G8R8A8 || X_Format == xbox::X_D3DFMT_LIN_B8G8R8A8 ||
272+
X_Format == xbox::X_D3DFMT_R8G8B8A8 || X_Format == xbox::X_D3DFMT_LIN_R8G8B8A8) {
273+
bConvertTextureFormat = false;
274+
PCFormat = EMUFMT_A8B8G8R8; // = DXGI_FORMAT_R8G8B8A8_UNORM (raw byte order)
275+
}
276+
#endif
266277
}
267278
else {
268279
// Does host CheckDeviceFormat() succeed on this format?

src/core/hle/D3D8/XbPixelShaderCompiler.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,10 @@ constexpr int PSH_XBOX_CONSTANT_FRONTFACE_FACTOR = PSH_XBOX_CONSTANT_LUM + 4; //
371371
constexpr int CXBX_D3DPS_CONSTREG_FOGINFO = PSH_XBOX_CONSTANT_FRONTFACE_FACTOR + 1; // = 40
372372
//Fog enable flag
373373
constexpr int PSH_XBOX_CONSTANT_FOGENABLE = CXBX_D3DPS_CONSTREG_FOGINFO + 1; // = 41
374+
// D3D11: Per-stage texture format channel fixup (packed float4, one component per stage)
375+
constexpr int PSH_XBOX_CONSTANT_TEXFMTFIXUP = PSH_XBOX_CONSTANT_FOGENABLE + 1; // = 42
374376
// This concludes the set of constants that need to be set on host :
375-
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_FOGENABLE + 1; // = 42
377+
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_TEXFMTFIXUP + 1; // = 43
376378

377379
std::string_view GetD3DTOPString(int d3dtop) {
378380
static constexpr std::string_view opToString[] = {
@@ -671,6 +673,40 @@ float CxbxComponentColorSignFromXboxAndHost(bool XboxMarksComponentSigned, bool
671673
return -1.0f; // Mark the component for scaling from signed_to_unsigned
672674
}
673675

676+
float CxbxGetTexFmtFixup(int stage_nr)
677+
{
678+
using namespace FixedFunctionPixelShader;
679+
680+
#ifdef CXBX_USE_D3D11
681+
auto pXboxTex = g_pXbox_SetTexture[stage_nr];
682+
if (pXboxTex == xbox::zeroptr)
683+
return TEXFMTFIXUP_IDENTITY;
684+
685+
xbox::X_D3DFORMAT xboxFmt = GetXboxPixelContainerFormat((xbox::X_D3DPixelContainer*)pXboxTex);
686+
switch (xboxFmt) {
687+
case xbox::X_D3DFMT_L8:
688+
case xbox::X_D3DFMT_LIN_L8:
689+
case xbox::X_D3DFMT_L16:
690+
case xbox::X_D3DFMT_LIN_L16:
691+
return TEXFMTFIXUP_LUM;
692+
case xbox::X_D3DFMT_A8L8:
693+
case xbox::X_D3DFMT_LIN_A8L8:
694+
return TEXFMTFIXUP_ALUM;
695+
// B8G8R8A8 and R8G8B8A8 use GBAR/ABGR swizzles when uploaded raw
696+
// (requires corresponding skip of CPU conversion in HostResourceCreate.cpp)
697+
case xbox::X_D3DFMT_B8G8R8A8:
698+
case xbox::X_D3DFMT_LIN_B8G8R8A8:
699+
return TEXFMTFIXUP_GBAR;
700+
case xbox::X_D3DFMT_R8G8B8A8:
701+
case xbox::X_D3DFMT_LIN_R8G8B8A8:
702+
return TEXFMTFIXUP_ABGR;
703+
default:
704+
break;
705+
}
706+
#endif
707+
return TEXFMTFIXUP_IDENTITY;
708+
}
709+
674710
D3DXCOLOR CxbxCalcColorSign(int stage_nr)
675711
{
676712
// Initially use what the running executable put in COLORSIGN :
@@ -762,6 +798,7 @@ void UpdateFixedFunctionPixelShaderState()
762798
stage->BUMPENVLSCALE = AsFloat(XboxTextureStates.Get(i, xbox::X_D3DTSS_BUMPENVLSCALE));
763799
stage->BUMPENVLOFFSET = AsFloat(XboxTextureStates.Get(i, xbox::X_D3DTSS_BUMPENVLOFFSET));
764800
{ D3DXCOLOR c(XboxTextureStates.Get(i, xbox::X_D3DTSS_COLORKEYCOLOR)); stage->COLORKEYCOLOR = D3DXVECTOR4(c.r, c.g, c.b, c.a); }
801+
stage->TEXFMTFIXUP = CxbxGetTexFmtFixup(i);
765802
}
766803

767804
const int size = (sizeof(FixedFunctionPixelShaderState) + 16 - 1) / 16;
@@ -989,6 +1026,12 @@ void CxbxUpdateActivePixelShader() // NOPATCH
9891026
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].a = fogEnd;
9901027
fColor[PSH_XBOX_CONSTANT_FOGENABLE].r = fogEnable;
9911028

1029+
// D3D11: Compute per-stage texture format channel fixup
1030+
fColor[PSH_XBOX_CONSTANT_TEXFMTFIXUP].r = CxbxGetTexFmtFixup(0);
1031+
fColor[PSH_XBOX_CONSTANT_TEXFMTFIXUP].g = CxbxGetTexFmtFixup(1);
1032+
fColor[PSH_XBOX_CONSTANT_TEXFMTFIXUP].b = CxbxGetTexFmtFixup(2);
1033+
fColor[PSH_XBOX_CONSTANT_TEXFMTFIXUP].a = CxbxGetTexFmtFixup(3);
1034+
9921035
// Assume all constants are in use (this is much easier than tracking them for no other purpose than to skip a few here)
9931036
// Read the color from the corresponding render state slot :
9941037
// Set all host constant values using a single call:

0 commit comments

Comments
 (0)