Skip to content

Commit e8950fc

Browse files
committed
(D3D9 HLSL) Implement all XMB shader pipeline effects
Add support for all 6 XMB animated background effects to the D3D9 HLSL driver: Ribbon (MENU), Ribbon Simple (MENU_2), Snow Simple (MENU_3), Snow (MENU_4), Bokeh (MENU_5), and Snowflake (MENU_6). Previously only Ribbon and Ribbon Simple were partially wired up using the stock shader, and Snow Simple was stubbed. New SM3 shader files (ps_3_0/vs_3_0): ribbon_sm3.hlsl.h — ddx/ddy normals, /13.0 divisor ribbon_simple_sm3.hlsl.h — flat color ribbon simple_snow_sm3.hlsl.h — snow simple with frac() scroll fix snow_sm3.hlsl.h — snow with frac() scroll fix bokeh_sm3.hlsl.h — bokeh particle effect snowflake_sm3.hlsl.h — snowflake with atan2 SM3 porting notes vs SM4: - SV_POSITION/SV_TARGET replaced with POSITION/COLOR semantics - Screen position passed via TEXCOORD0 since SM3 pixel shaders cannot read VPOS reliably; vertex shader computes screen coords from clip position - Vertex input changed to float3 to match existing D3D9 vertex declaration (FLOAT3 POSITION) - Global float constants (baseScale, density, speed) inlined as literals to avoid D3DCompile creating CTAB uniform entries that lose their initial values - Snowflake atime moved from static const global to local variable d3d9hlsl.c changes: - Added pipeline_shaders[6] and pipeline_data[6] to renderchain struct for compiled shader programs and CTAB bytecode - All 6 shaders compiled at renderchain init via d3d9_hlsl_load_program_ex with bytecode stored for CTAB uniform lookup - draw_pipeline: creates fullscreen quad VBO for MENU_3-6 using separate blank_coords to avoid mutating the shared ca->coords vertex count; sets time/OutputSize/alpha uniforms via CTAB register lookup; ribbon uses DESTCOLOR+ONE blend matching D3D11 - draw: selects pipeline shader per effect, sets vertex declaration, sets MVP ortho matrix for snow/bokeh/snowflake, restores stock shader after draw to prevent constant register corruption for subsequent non-pipeline draws - Pipeline index computed as VIDEO_SHADER_MENU - pipeline_id (enum values decrease: MENU=62, MENU_6=57) - Range checks use <= MENU / >= MENU_6 for the same reason - Cleanup: releases fullscreen VBO on destroy
1 parent dd9d7fc commit e8950fc

7 files changed

Lines changed: 661 additions & 12 deletions

File tree

gfx/drivers/d3d9hlsl.c

Lines changed: 221 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@
8484
#include "../../retroarch.h"
8585

8686
#include "d3d_shaders/shaders_common.h"
87+
#include "d3d_shaders/ribbon_sm3.hlsl.h"
88+
#include "d3d_shaders/ribbon_simple_sm3.hlsl.h"
89+
#include "d3d_shaders/simple_snow_sm3.hlsl.h"
90+
#include "d3d_shaders/snow_sm3.hlsl.h"
91+
#include "d3d_shaders/bokeh_sm3.hlsl.h"
92+
#include "d3d_shaders/snowflake_sm3.hlsl.h"
8793

8894
static const char *stock_hlsl_program = CG(
8995
void main_vertex
@@ -645,12 +651,18 @@ typedef struct hlsl_renderchain
645651
hlsl_pass_data_t pass_data[GFX_MAX_SHADERS + 1];
646652
unsigned pass_data_count;
647653
hlsl_pass_data_t stock_data;
654+
/* XMB pipeline shaders */
655+
struct shader_pass pipeline_shaders[6]; /* MENU..MENU_6 */
656+
hlsl_pass_data_t pipeline_data[6];
657+
bool pipeline_inited;
648658
} hlsl_renderchain_t;
649659

650660
/* Pipeline vertex buffer for menu shader effects (VIDEO_SHADER_MENU, etc.).
651661
* Stored as a file-static since the d3d9 menu_display struct
652662
* does not have a pipeline_vbo member like d3d10_video_t does. */
653663
static LPDIRECT3DVERTEXBUFFER9 d3d9_hlsl_menu_pipeline_vbo = NULL;
664+
static LPDIRECT3DVERTEXBUFFER9 d3d9_hlsl_menu_fullscreen_vbo = NULL;
665+
static LPDIRECT3DVERTEXDECLARATION9 d3d9_hlsl_pipeline_decl = NULL;
654666
static LPDIRECT3DVERTEXDECLARATION9 d3d9_hlsl_overlay_decl = NULL;
655667

656668
static void d3d9_vertex_buffer_free(void *vertex_data, void *vertex_declaration)
@@ -788,21 +800,60 @@ static void gfx_display_d3d9_hlsl_draw(gfx_display_ctx_draw_t *draw,
788800
case VIDEO_SHADER_MENU:
789801
case VIDEO_SHADER_MENU_2:
790802
case VIDEO_SHADER_MENU_3:
803+
case VIDEO_SHADER_MENU_4:
804+
case VIDEO_SHADER_MENU_5:
805+
case VIDEO_SHADER_MENU_6:
791806
{
792-
/* Draw the pipeline vertices using the stock shader,
793-
* then restore blend state and menu display vertex state.
794-
* Adapted from d3d10 gfx_display_d3d10_draw pipeline path. */
795807
hlsl_renderchain_t *_chain = (hlsl_renderchain_t*)d3d->renderchain_data;
796808

797-
if (_chain)
809+
if (_chain && _chain->pipeline_inited)
798810
{
799-
IDirect3DDevice9_SetVertexShader(dev, (LPDIRECT3DVERTEXSHADER9)(&_chain->stock_shader)->vprg);
800-
801-
IDirect3DDevice9_SetPixelShader(dev, (LPDIRECT3DPIXELSHADER9)(&_chain->stock_shader)->fprg);
811+
unsigned idx = VIDEO_SHADER_MENU - draw->pipeline_id;
812+
struct shader_pass *shader = &_chain->pipeline_shaders[idx];
813+
814+
IDirect3DDevice9_SetVertexShader(dev,
815+
(LPDIRECT3DVERTEXSHADER9)shader->vprg);
816+
IDirect3DDevice9_SetPixelShader(dev,
817+
(LPDIRECT3DPIXELSHADER9)shader->fprg);
818+
IDirect3DDevice9_SetVertexDeclaration(dev,
819+
(LPDIRECT3DVERTEXDECLARATION9)d3d->menu_display.decl);
820+
821+
/* Snow/bokeh/snowflake need MVP matrix set */
822+
if (draw->pipeline_id <= VIDEO_SHADER_MENU_3)
823+
{
824+
hlsl_pass_data_t *pd = &_chain->pipeline_data[idx];
825+
/* Row-major ortho for mul(matrix, vector) in shader.
826+
* D3D9 SetVertexShaderConstantF stores rows sequentially.
827+
* mul(M, v) computes dot(row[i], v) — correct with row-major.
828+
* Maps X [0,1]→[-1,1], Y [0,1]→[-1,1], Z pass-through. */
829+
static const float ortho_mvp[16] = {
830+
2.0f, 0.0f, 0.0f, 0.0f,
831+
0.0f, 2.0f, 0.0f, 0.0f,
832+
0.0f, 0.0f, 1.0f, 0.0f,
833+
-1.0f, -1.0f, 0.0f, 1.0f
834+
};
835+
{
836+
int reg = d3d9_hlsl_ctab_find_register(
837+
pd->vs_bytecode, pd->vs_bytecode_dwords,
838+
"modelViewProj", NULL, NULL);
839+
if (reg >= 0)
840+
d3d9_hlsl_set_vs_const(dev, reg, ortho_mvp, 4);
841+
}
842+
}
802843

803844
IDirect3DDevice9_DrawPrimitive(dev, D3DPT_TRIANGLESTRIP,
804845
0, draw->coords->vertices - 2);
805846
}
847+
else if (_chain)
848+
{
849+
/* Fallback to stock shader */
850+
IDirect3DDevice9_SetVertexShader(dev,
851+
(LPDIRECT3DVERTEXSHADER9)(&_chain->stock_shader)->vprg);
852+
IDirect3DDevice9_SetPixelShader(dev,
853+
(LPDIRECT3DPIXELSHADER9)(&_chain->stock_shader)->fprg);
854+
IDirect3DDevice9_DrawPrimitive(dev, D3DPT_TRIANGLESTRIP,
855+
0, draw->coords->vertices - 2);
856+
}
806857

807858
/* Re-enable alpha blending after pipeline draw */
808859
IDirect3DDevice9_SetRenderState(dev,
@@ -818,6 +869,16 @@ static void gfx_display_d3d9_hlsl_draw(gfx_display_ctx_draw_t *draw,
818869
0, sizeof(Vertex));
819870
IDirect3DDevice9_SetVertexDeclaration(dev,
820871
(LPDIRECT3DVERTEXDECLARATION9)d3d->menu_display.decl);
872+
873+
/* Restore stock shader and its constants so subsequent
874+
* non-pipeline draws (text, icons) work correctly. */
875+
if (_chain)
876+
{
877+
IDirect3DDevice9_SetVertexShader(dev,
878+
(LPDIRECT3DVERTEXSHADER9)(&_chain->stock_shader)->vprg);
879+
IDirect3DDevice9_SetPixelShader(dev,
880+
(LPDIRECT3DPIXELSHADER9)(&_chain->stock_shader)->fprg);
881+
}
821882
return;
822883
}
823884
default:
@@ -1080,19 +1141,79 @@ static void gfx_display_d3d9_hlsl_draw_pipeline(
10801141

10811142
draw->coords->vertices = ca->coords.vertices;
10821143

1083-
/* Set pipeline blend state */
1144+
/* Set pipeline blend state — ribbon uses multiplicative blend
1145+
* (DESTCOLOR + ONE) matching D3D11's blend_pipeline. */
10841146
IDirect3DDevice9_SetRenderState(d3d->dev,
1085-
D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
1147+
D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
10861148
IDirect3DDevice9_SetRenderState(d3d->dev,
1087-
D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
1149+
D3DRS_DESTBLEND, D3DBLEND_ONE);
10881150
IDirect3DDevice9_SetRenderState(d3d->dev,
10891151
D3DRS_ALPHABLENDENABLE, true);
10901152
break;
10911153
}
10921154

10931155
case VIDEO_SHADER_MENU_3:
1156+
case VIDEO_SHADER_MENU_4:
1157+
case VIDEO_SHADER_MENU_5:
1158+
case VIDEO_SHADER_MENU_6:
10941159
{
1095-
draw->coords->vertices = 4;
1160+
static struct video_coords blank_coords = {0};
1161+
1162+
/* Fullscreen quad for snow/bokeh/snowflake effects */
1163+
if (!d3d9_hlsl_menu_fullscreen_vbo)
1164+
{
1165+
Vertex verts[4];
1166+
verts[0].x = 0.0f; verts[0].y = 0.0f; verts[0].z = 0.0f;
1167+
verts[0].u = 0.0f; verts[0].v = 1.0f;
1168+
verts[0].color = D3DCOLOR_ARGB(0xFF, 0xFF, 0xFF, 0xFF);
1169+
verts[1].x = 1.0f; verts[1].y = 0.0f; verts[1].z = 0.0f;
1170+
verts[1].u = 1.0f; verts[1].v = 1.0f;
1171+
verts[1].color = D3DCOLOR_ARGB(0xFF, 0xFF, 0xFF, 0xFF);
1172+
verts[2].x = 0.0f; verts[2].y = 1.0f; verts[2].z = 0.0f;
1173+
verts[2].u = 0.0f; verts[2].v = 0.0f;
1174+
verts[2].color = D3DCOLOR_ARGB(0xFF, 0xFF, 0xFF, 0xFF);
1175+
verts[3].x = 1.0f; verts[3].y = 1.0f; verts[3].z = 0.0f;
1176+
verts[3].u = 1.0f; verts[3].v = 0.0f;
1177+
verts[3].color = D3DCOLOR_ARGB(0xFF, 0xFF, 0xFF, 0xFF);
1178+
1179+
if (SUCCEEDED(IDirect3DDevice9_CreateVertexBuffer(
1180+
d3d->dev, 4 * sizeof(Vertex),
1181+
D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT,
1182+
(LPDIRECT3DVERTEXBUFFER9*)&d3d9_hlsl_menu_fullscreen_vbo, NULL)))
1183+
{
1184+
void *lock = NULL;
1185+
IDirect3DVertexBuffer9_Lock(
1186+
(LPDIRECT3DVERTEXBUFFER9)d3d9_hlsl_menu_fullscreen_vbo,
1187+
0, 0, &lock, 0);
1188+
if (lock)
1189+
{
1190+
memcpy(lock, verts, sizeof(verts));
1191+
IDirect3DVertexBuffer9_Unlock(
1192+
(LPDIRECT3DVERTEXBUFFER9)d3d9_hlsl_menu_fullscreen_vbo);
1193+
}
1194+
}
1195+
}
1196+
1197+
if (d3d9_hlsl_menu_fullscreen_vbo)
1198+
{
1199+
IDirect3DDevice9_SetStreamSource(d3d->dev, 0,
1200+
(LPDIRECT3DVERTEXBUFFER9)d3d9_hlsl_menu_fullscreen_vbo,
1201+
0, sizeof(Vertex));
1202+
}
1203+
1204+
/* Use separate coords to avoid mutating the shared
1205+
* ca->coords.vertices (which ribbon needs at 8064). */
1206+
blank_coords.vertices = 4;
1207+
draw->coords = &blank_coords;
1208+
draw->prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
1209+
1210+
/* Set blend state for particle effects */
1211+
IDirect3DDevice9_SetRenderState(d3d->dev,
1212+
D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
1213+
IDirect3DDevice9_SetRenderState(d3d->dev,
1214+
D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
1215+
IDirect3DDevice9_SetRenderState(d3d->dev,
1216+
D3DRS_ALPHABLENDENABLE, true);
10961217
break;
10971218
}
10981219

@@ -1105,7 +1226,56 @@ static void gfx_display_d3d9_hlsl_draw_pipeline(
11051226

11061227
{
11071228
hlsl_renderchain_t *_chain = (hlsl_renderchain_t*)d3d->renderchain_data;
1108-
if (_chain)
1229+
if (_chain && _chain->pipeline_inited
1230+
&& draw->pipeline_id <= VIDEO_SHADER_MENU
1231+
&& draw->pipeline_id >= VIDEO_SHADER_MENU_6)
1232+
{
1233+
unsigned idx = VIDEO_SHADER_MENU - draw->pipeline_id;
1234+
hlsl_pass_data_t *pd = &_chain->pipeline_data[idx];
1235+
1236+
/* Set time uniform for all pipeline shaders */
1237+
d3d9_hlsl_set_param_1f(pd->vs_bytecode, pd->vs_bytecode_dwords,
1238+
true, d3d->dev, "time", &t);
1239+
d3d9_hlsl_set_param_1f(pd->ps_bytecode, pd->ps_bytecode_dwords,
1240+
false, d3d->dev, "time", &t);
1241+
1242+
/* Set OutputSize for snow/bokeh/snowflake shaders */
1243+
if (draw->pipeline_id <= VIDEO_SHADER_MENU_3)
1244+
{
1245+
D3DVIEWPORT9 vp;
1246+
IDirect3DDevice9_GetViewport(d3d->dev, &vp);
1247+
{
1248+
float output_size[4];
1249+
int reg;
1250+
output_size[0] = (float)vp.Width;
1251+
output_size[1] = (float)vp.Height;
1252+
output_size[2] = 0.0f;
1253+
output_size[3] = 0.0f;
1254+
/* Set OutputSize as a full register (float2 occupies one register) */
1255+
reg = d3d9_hlsl_ctab_find_register(
1256+
pd->vs_bytecode, pd->vs_bytecode_dwords,
1257+
"OutputSize", NULL, NULL);
1258+
if (reg >= 0)
1259+
d3d9_hlsl_set_vs_const(d3d->dev, reg, output_size, 1);
1260+
reg = d3d9_hlsl_ctab_find_register(
1261+
pd->ps_bytecode, pd->ps_bytecode_dwords,
1262+
"OutputSize", NULL, NULL);
1263+
if (reg >= 0)
1264+
d3d9_hlsl_set_ps_const(d3d->dev, reg, output_size, 1);
1265+
}
1266+
}
1267+
1268+
/* Set alpha for ribbon shaders */
1269+
if (draw->pipeline_id >= VIDEO_SHADER_MENU_2)
1270+
{
1271+
float alpha_val = draw->color ? draw->color[3] : 1.0f;
1272+
d3d9_hlsl_set_param_1f(pd->vs_bytecode, pd->vs_bytecode_dwords,
1273+
true, d3d->dev, "alpha", &alpha_val);
1274+
d3d9_hlsl_set_param_1f(pd->ps_bytecode, pd->ps_bytecode_dwords,
1275+
false, d3d->dev, "alpha", &alpha_val);
1276+
}
1277+
}
1278+
else if (_chain)
11091279
{
11101280
d3d9_hlsl_set_param_1f(
11111281
_chain->stock_data.vs_bytecode,
@@ -2546,6 +2716,35 @@ static bool hlsl_d3d9_renderchain_init(
25462716
IDirect3DDevice9_SetVertexShader(dev, (LPDIRECT3DVERTEXSHADER9)(&chain->stock_shader)->vprg);
25472717
IDirect3DDevice9_SetPixelShader(dev, (LPDIRECT3DPIXELSHADER9)(&chain->stock_shader)->fprg);
25482718

2719+
/* Compile XMB pipeline shaders */
2720+
{
2721+
const char *pipeline_sources[6];
2722+
unsigned i;
2723+
pipeline_sources[0] = hlsl_ribbon_program;
2724+
pipeline_sources[1] = hlsl_ribbon_simple_program;
2725+
pipeline_sources[2] = hlsl_simple_snow_program;
2726+
pipeline_sources[3] = hlsl_snow_program;
2727+
pipeline_sources[4] = hlsl_bokeh_program;
2728+
pipeline_sources[5] = hlsl_snowflake_program;
2729+
chain->pipeline_inited = true;
2730+
for (i = 0; i < 6; i++)
2731+
{
2732+
hlsl_uniform_map_init(&chain->pipeline_data[i].vs_map);
2733+
hlsl_uniform_map_init(&chain->pipeline_data[i].ps_map);
2734+
chain->pipeline_data[i].vs_bytecode = NULL;
2735+
chain->pipeline_data[i].ps_bytecode = NULL;
2736+
if (!d3d9_hlsl_load_program_ex(dev,
2737+
&chain->pipeline_shaders[i],
2738+
pipeline_sources[i],
2739+
&chain->pipeline_data[i]))
2740+
{
2741+
RARCH_WARN("[D3D9 HLSL] Could not compile XMB pipeline shader %u, effects disabled.\n", i);
2742+
chain->pipeline_inited = false;
2743+
break;
2744+
}
2745+
}
2746+
}
2747+
25492748
return true;
25502749
}
25512750

@@ -6222,6 +6421,16 @@ static void d3d9_hlsl_deinitialize(d3d9_video_t *d3d)
62226421
d3d9_vertex_buffer_free(d3d->menu_display.buffer,
62236422
d3d->menu_display.decl);
62246423

6424+
if (d3d9_hlsl_menu_fullscreen_vbo)
6425+
{
6426+
IDirect3DVertexBuffer9_Release(d3d9_hlsl_menu_fullscreen_vbo);
6427+
d3d9_hlsl_menu_fullscreen_vbo = NULL;
6428+
}
6429+
if (d3d9_hlsl_pipeline_decl)
6430+
{
6431+
IDirect3DVertexDeclaration9_Release(d3d9_hlsl_pipeline_decl);
6432+
d3d9_hlsl_pipeline_decl = NULL;
6433+
}
62256434
if (d3d9_hlsl_menu_pipeline_vbo)
62266435
{
62276436
IDirect3DVertexBuffer9_Release(d3d9_hlsl_menu_pipeline_vbo);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
static const char *hlsl_bokeh_program = CG(
3+
4+
uniform float4x4 modelViewProj;
5+
uniform float2 OutputSize;
6+
uniform float time;
7+
8+
void main_vertex(
9+
float4 position : POSITION,
10+
float2 texcoord : TEXCOORD0,
11+
out float4 oPosition : POSITION,
12+
out float2 vpos : TEXCOORD0
13+
)
14+
{
15+
oPosition = mul(modelViewProj, position);
16+
vpos = float2(
17+
(oPosition.x / oPosition.w * 0.5 + 0.5) * OutputSize.x,
18+
(0.5 - oPosition.y / oPosition.w * 0.5) * OutputSize.y);
19+
}
20+
21+
struct output
22+
{
23+
float4 color : COLOR;
24+
};
25+
26+
output main_fragment(float2 vpos : TEXCOORD0)
27+
{
28+
output OUT;
29+
float speed = time * 4.0;
30+
float2 uv = -1.0 + 2.0 * vpos.xy / OutputSize;
31+
uv.x *= OutputSize.x / OutputSize.y;
32+
float3 color = float3(0.0, 0.0, 0.0);
33+
34+
for (int i = 0; i < 8; i++)
35+
{
36+
float pha = sin(float(i) * 546.13 + 1.0) * 0.5 + 0.5;
37+
float siz = pow(sin(float(i) * 651.74 + 5.0) * 0.5 + 0.5, 4.0);
38+
float pox = sin(float(i) * 321.55 + 4.1) * OutputSize.x / OutputSize.y;
39+
float rad = 0.1 + 0.5 * siz + sin(pha + siz) / 4.0;
40+
float2 pos = float2(pox + sin(speed / 15. + pha + siz),
41+
-1.0 - rad + (2.0 + 2.0 * rad) * frac(pha + 0.3 * (speed / 7.) * (0.2 + 0.8 * siz)));
42+
float dis = length(uv - pos);
43+
if (dis < rad)
44+
{
45+
float3 col = lerp(
46+
float3(0.194 * sin(speed / 6.0) + 0.3, 0.2, 0.3 * pha),
47+
float3(1.1 * sin(speed / 9.0) + 0.3, 0.2 * pha, 0.4),
48+
0.5 + 0.5 * sin(float(i)));
49+
color += col.zyx * (1.0 - smoothstep(rad * 0.15, rad, dis));
50+
}
51+
}
52+
color *= sqrt(1.5 - 0.5 * length(uv));
53+
OUT.color = float4(color.r, color.g, color.b, 0.5);
54+
return OUT;
55+
}
56+
);

0 commit comments

Comments
 (0)