Skip to content

Commit ace9c9f

Browse files
committed
gfx_widgets+d3d: animate task-message hourglass on all drivers
The hourglass icon on the task-progress notification widget never visibly rotated. The animation system was correctly easing msg->hourglass_rotation through 0 -> -2*pi every six seconds, but the rendered icon stayed static across every backend. Three independent bugs, each blocking a different family of drivers: 1. gfx/gfx_widgets.c - the call site that draws the hourglass forwarded the live `radians` value to gfx_widgets_draw_icon but left `cosine`/`sine` at their cos(0)/sin(0) defaults. The matrix-rotation path (gl, gl1, glcore, vulkan, d3d8) consumes those trig values via gfx_display_rotate_z, so it was always building an identity rotation regardless of `radians`. Fix: recompute cosf(radians)/sinf(radians) alongside `radians` in the in-progress branch. 2. gfx/drivers/d3d_shaders/sprite_sm4.hlsl.h - the sprite shader used by the d3d10 / d3d11 / d3d12 menu display drivers. The C code on those drivers dutifully writes `sprite->params.rotation = draw->rotation` into the vertex stream, but the shader pipeline never reads it: the GSInput struct doesn't have a `params` field, so rotation is dropped at the VS->GS boundary, and the geometry shader builds the four corner vertices straight from `position.xy + position.zw * cornerOffset` with no rotation applied. Fix: forward `params` from VS to GS (params.x is already consumed by the VS for scaling; we need params.y in the GS for rotation), and rewrite the GS to compute corner offsets by rotating per-corner pixel-space sign vectors `(sx, sy) in {+/-1}^2` and scaling them back by `position.z * 0.5` (X) and `position.w * 0.5` (Y). Because position.z = 2W/vw and position.w = 2W/vh both encode the same pixel-space length W for a square icon, this preserves the icon's shape under rotation on a non-square viewport. Non-square icons would skew, but the only caller of rotation today (the hourglass) is square. 3. gfx/drivers/d3d9hlsl.c, gfx/drivers/d3d9cg.c - the d3d9 menu display drivers' single-sprite path (no explicit vertex array supplied by caller, which is what gfx_widgets_draw_icon does) builds an axis-aligned quad from draw->x / draw->y / draw->width / draw->height directly, applies scale_factor, and never references draw->rotation or draw->matrix_data at all - so rotation was silently dropped on both shader paths. Fix: when draw->rotation is non-zero, rotate each of the four pixel-space corner offsets around the quad centre, then normalise back to [0,1] via 1/video_width and 1/video_height. Same aspect-correct approach as the d3d_shaders fix above. Add <math.h> to d3d9hlsl.c (d3d9cg.c already had it). The d3d9 multi-vertex path uses the matrix_data route, so it's handled by fix (1) alone and needs no driver-side change. Tested on gl / gl1 / glcore / vulkan / d3d9_cg / d3d9_hlsl / d3d10 / d3d11 / d3d12.
1 parent 832250b commit ace9c9f

4 files changed

Lines changed: 179 additions & 34 deletions

File tree

gfx/drivers/d3d9cg.c

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -870,17 +870,63 @@ static void gfx_display_d3d9_cg_draw(gfx_display_ctx_draw_t *draw,
870870
y2 = cy + hh;
871871
}
872872

873-
quad[0].x = x1; quad[0].y = y1; quad[0].z = 0.5f;
874-
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
873+
/* Apply draw->rotation around the quad center. Rotation is
874+
* computed in pixel space (using draw->width / draw->height as
875+
* the icon's true square extents) and then converted back to
876+
* normalised [0,1], so a non-square viewport does not skew the
877+
* rotated icon. */
878+
if (draw->rotation != 0.0f)
879+
{
880+
float cx = (x1 + x2) * 0.5f;
881+
float cy = (y1 + y2) * 0.5f;
882+
float half_w = draw->width * 0.5f;
883+
float half_h = draw->height * 0.5f;
884+
if (draw->scale_factor && draw->scale_factor != 1.0f)
885+
{
886+
half_w *= draw->scale_factor;
887+
half_h *= draw->scale_factor;
888+
}
889+
{
890+
float c = cosf(draw->rotation);
891+
float s = sinf(draw->rotation);
892+
float inv_vw = 1.0f / (float)video_width;
893+
float inv_vh = 1.0f / (float)video_height;
894+
float ox[4] = { -half_w, half_w, -half_w, half_w };
895+
float oy[4] = { -half_h, -half_h, half_h, half_h };
896+
float rx[4], ry[4];
897+
int k;
898+
for (k = 0; k < 4; k++)
899+
{
900+
rx[k] = (ox[k] * c - oy[k] * s) * inv_vw;
901+
ry[k] = (ox[k] * s + oy[k] * c) * inv_vh;
902+
}
903+
quad[0].x = cx + rx[0]; quad[0].y = cy + ry[0]; quad[0].z = 0.5f;
904+
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
905+
906+
quad[1].x = cx + rx[1]; quad[1].y = cy + ry[1]; quad[1].z = 0.5f;
907+
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
875908

876-
quad[1].x = x2; quad[1].y = y1; quad[1].z = 0.5f;
877-
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
909+
quad[2].x = cx + rx[2]; quad[2].y = cy + ry[2]; quad[2].z = 0.5f;
910+
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
878911

879-
quad[2].x = x1; quad[2].y = y2; quad[2].z = 0.5f;
880-
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
912+
quad[3].x = cx + rx[3]; quad[3].y = cy + ry[3]; quad[3].z = 0.5f;
913+
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
914+
}
915+
}
916+
else
917+
{
918+
quad[0].x = x1; quad[0].y = y1; quad[0].z = 0.5f;
919+
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
881920

882-
quad[3].x = x2; quad[3].y = y2; quad[3].z = 0.5f;
883-
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
921+
quad[1].x = x2; quad[1].y = y1; quad[1].z = 0.5f;
922+
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
923+
924+
quad[2].x = x1; quad[2].y = y2; quad[2].z = 0.5f;
925+
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
926+
927+
quad[3].x = x2; quad[3].y = y2; quad[3].z = 0.5f;
928+
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
929+
}
884930

885931
/* Top-down ortho: maps X [0,1]->[-1,1], Y [0,1]->[1,-1] (Y=0 at top).
886932
* Column-major (transposed) layout for the Cg stock shader which

gfx/drivers/d3d9hlsl.c

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <string.h>
4343
#include <retro_inline.h>
4444
#include <retro_math.h>
45+
#include <math.h>
4546

4647
#ifndef _XBOX
4748
#define WIN32_LEAN_AND_MEAN
@@ -944,17 +945,66 @@ static void gfx_display_d3d9_hlsl_draw(gfx_display_ctx_draw_t *draw,
944945
y2 = cy + hh;
945946
}
946947

947-
quad[0].x = x1; quad[0].y = y1; quad[0].z = 0.5f;
948-
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
948+
/* Apply draw->rotation around the quad center. Rotation is
949+
* computed in pixel space (using draw->width / draw->height as
950+
* the icon's true square extents) and then converted back to
951+
* normalised [0,1], so a non-square viewport does not skew the
952+
* rotated icon. */
953+
if (draw->rotation != 0.0f)
954+
{
955+
float cx = (x1 + x2) * 0.5f;
956+
float cy = (y1 + y2) * 0.5f;
957+
float half_w = draw->width * 0.5f;
958+
float half_h = draw->height * 0.5f;
959+
if (draw->scale_factor && draw->scale_factor != 1.0f)
960+
{
961+
half_w *= draw->scale_factor;
962+
half_h *= draw->scale_factor;
963+
}
964+
{
965+
float c = cosf(draw->rotation);
966+
float s = sinf(draw->rotation);
967+
float inv_vw = 1.0f / (float)video_width;
968+
float inv_vh = 1.0f / (float)video_height;
969+
/* Pixel-space corner offsets (dx, dy) for each of the
970+
* four quad vertices. Order matches the (x1,y1)..
971+
* (x2,y2) layout used immediately below. */
972+
float ox[4] = { -half_w, half_w, -half_w, half_w };
973+
float oy[4] = { -half_h, -half_h, half_h, half_h };
974+
float rx[4], ry[4];
975+
int k;
976+
for (k = 0; k < 4; k++)
977+
{
978+
rx[k] = (ox[k] * c - oy[k] * s) * inv_vw;
979+
ry[k] = (ox[k] * s + oy[k] * c) * inv_vh;
980+
}
981+
quad[0].x = cx + rx[0]; quad[0].y = cy + ry[0]; quad[0].z = 0.5f;
982+
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
983+
984+
quad[1].x = cx + rx[1]; quad[1].y = cy + ry[1]; quad[1].z = 0.5f;
985+
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
949986

950-
quad[1].x = x2; quad[1].y = y1; quad[1].z = 0.5f;
951-
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
987+
quad[2].x = cx + rx[2]; quad[2].y = cy + ry[2]; quad[2].z = 0.5f;
988+
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
952989

953-
quad[2].x = x1; quad[2].y = y2; quad[2].z = 0.5f;
954-
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
990+
quad[3].x = cx + rx[3]; quad[3].y = cy + ry[3]; quad[3].z = 0.5f;
991+
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
992+
}
993+
}
994+
else
995+
{
996+
quad[0].x = x1; quad[0].y = y1; quad[0].z = 0.5f;
997+
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
955998

956-
quad[3].x = x2; quad[3].y = y2; quad[3].z = 0.5f;
957-
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
999+
quad[1].x = x2; quad[1].y = y1; quad[1].z = 0.5f;
1000+
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
1001+
1002+
quad[2].x = x1; quad[2].y = y2; quad[2].z = 0.5f;
1003+
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
1004+
1005+
quad[3].x = x2; quad[3].y = y2; quad[3].z = 0.5f;
1006+
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
1007+
}
9581008

9591009
/* Top-down ortho: maps X [0,1]→[-1,1], Y [0,1]→[1,-1] (Y=0 at top).
9601010
* Row-major layout for mul(vector, matrix) in the stock HLSL vertex shader.

gfx/drivers/d3d_shaders/sprite_sm4.hlsl.h

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ SRC(
2020
float4 color1 : COLOR1;
2121
float4 color2 : COLOR2;
2222
float4 color3 : COLOR3;
23+
float2 params : PARAMS;
2324
};
2425
struct PSInput
2526
{
@@ -42,34 +43,75 @@ SRC(
4243
output.color1 = input.color1;
4344
output.color2 = input.color2;
4445
output.color3 = input.color3;
46+
/* params.x is consumed in the VS (scaling, above);
47+
* forward params.y (rotation, in radians) to the GS. */
48+
output.params = input.params;
4549
return output;
4650
}
4751

4852
[maxvertexcount(4)]
4953
void GSMain(point GSInput input[1], inout TriangleStream<PSInput> triStream)
5054
{
55+
/* Apply rotation around the sprite centre.
56+
*
57+
* input[0].position.xy = top-left corner of the quad (clip space)
58+
* input[0].position.zw = quad size (clip space)
59+
*
60+
* The four corners are at `position.xy + position.zw * c`
61+
* with `c` in {(0,0), (1,0), (0,1), (1,1)}. We rewrite as
62+
* `centre + position.zw * (c - 0.5)` and rotate the
63+
* `(c - 0.5)`-scaled offset by `params.y` radians around
64+
* the centre. The rotation is computed in pixel space and
65+
* then mapped back to clip space via the per-axis `.zw`
66+
* sizes; this keeps a square pixel-space icon square on
67+
* screen even on a non-square viewport, because position.z
68+
* and position.w both carry the same pixel-space length W
69+
* (z = 2W/vw, w = 2W/vh). Non-square icons would skew, but
70+
* the only caller of rotation today (the task-message
71+
* hourglass) draws a square. */
5172
PSInput output;
52-
output.position.zw = float2(0.0f, 1.0f);
73+
float cosT = cos(input[0].params.y);
74+
float sinT = sin(input[0].params.y);
75+
float2 centre = input[0].position.xy + input[0].position.zw * 0.5;
5376

54-
output.position.xy = input[0].position.xy + input[0].position.zw * float2(1.0, 0.0);
55-
output.texcoord = input[0].texcoord.xy + input[0].texcoord.zw * float2(1.0, 0.0);
56-
output.color = input[0].color0;
57-
triStream.Append(output);
77+
/* Corner sign ordering matches the four triStream.Append
78+
* calls below; (sx, sy) ∈ {±1} are the corner-relative-to-
79+
* centre signs in pixel space. */
80+
float2 corner_signs[4] = {
81+
float2( 1.0, -1.0), /* top-right -> color0 */
82+
float2(-1.0, -1.0), /* top-left -> color1 */
83+
float2( 1.0, 1.0), /* bottom-right -> color2 */
84+
float2(-1.0, 1.0), /* bottom-left -> color3 */
85+
};
86+
float2 corner_uv[4] = {
87+
float2(1.0, 0.0),
88+
float2(0.0, 0.0),
89+
float2(1.0, 1.0),
90+
float2(0.0, 1.0),
91+
};
92+
float4 corner_colour[4] = {
93+
input[0].color0, input[0].color1,
94+
input[0].color2, input[0].color3,
95+
};
5896

59-
output.position.xy = input[0].position.xy + input[0].position.zw * float2(0.0, 0.0);
60-
output.texcoord = input[0].texcoord.xy + input[0].texcoord.zw * float2(0.0, 0.0);
61-
output.color = input[0].color1;
62-
triStream.Append(output);
63-
64-
output.position.xy = input[0].position.xy + input[0].position.zw * float2(1.0, 1.0);
65-
output.texcoord = input[0].texcoord.xy + input[0].texcoord.zw * float2(1.0, 1.0);
66-
output.color = input[0].color2;
67-
triStream.Append(output);
97+
output.position.zw = float2(0.0f, 1.0f);
6898

69-
output.position.xy = input[0].position.xy + input[0].position.zw * float2(0.0, 1.0);
70-
output.texcoord = input[0].texcoord.xy + input[0].texcoord.zw * float2(0.0, 1.0);
71-
output.color = input[0].color3;
72-
triStream.Append(output);
99+
[unroll]
100+
for (int i = 0; i < 4; i++)
101+
{
102+
float sx = corner_signs[i].x;
103+
float sy = corner_signs[i].y;
104+
/* Rotate the half-extent corner offset, then scale back
105+
* by the per-axis clip-space half-size. See block
106+
* comment above for why this preserves square icons. */
107+
float dx = (sx * cosT - sy * sinT) * input[0].position.z * 0.5;
108+
float dy = (sx * sinT + sy * cosT) * input[0].position.w * 0.5;
109+
output.position.xy = centre + float2(dx, dy);
110+
output.texcoord = input[0].texcoord.xy
111+
+ input[0].texcoord.zw * corner_uv[i];
112+
output.color = corner_colour[i];
113+
triStream.Append(output);
114+
}
73115
}
74116

75117
uniform sampler s0;

gfx/gfx_widgets.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,13 @@ static void gfx_widgets_draw_task_msg(
12781278
texture = MENU_WIDGETS_ICON_HOURGLASS;
12791279
color = msg_queue_bar;
12801280
radians = msg->hourglass_rotation;
1281+
/* The display drivers that consume this rotation via the
1282+
* cosine/sine pair (gl, gl1, glcore, vulkan) need the
1283+
* trig values recomputed to match the live rotation -
1284+
* leaving them at the zero-rotation defaults above pins
1285+
* the hourglass icon to its starting orientation. */
1286+
cosine = cosf(radians);
1287+
sine = sinf(radians);
12811288
}
12821289
else if (msg->flags & DISPWIDG_FLAG_POSITIVE)
12831290
{

0 commit comments

Comments
 (0)