mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 17:28:49 -10:00
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.
This commit is contained in:
1 parent
832250be0a
commit
ace9c9f2f3
4 files changed
+180
-35
No files matched your search
+54
-8
@@ -870,17 +870,63 @@ static void gfx_display_d3d9_cg_draw(gfx_display_ctx_draw_t *draw,
|
||||
y2 = cy + hh;
|
||||
}
|
||||
|
||||
quad[0].x = x1; quad[0].y = y1; quad[0].z = 0.5f;
|
||||
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
|
||||
/* Apply draw->rotation around the quad center. Rotation is
|
||||
* computed in pixel space (using draw->width / draw->height as
|
||||
* the icon's true square extents) and then converted back to
|
||||
* normalised [0,1], so a non-square viewport does not skew the
|
||||
* rotated icon. */
|
||||
if (draw->rotation != 0.0f)
|
||||
{
|
||||
float cx = (x1 + x2) * 0.5f;
|
||||
float cy = (y1 + y2) * 0.5f;
|
||||
float half_w = draw->width * 0.5f;
|
||||
float half_h = draw->height * 0.5f;
|
||||
if (draw->scale_factor && draw->scale_factor != 1.0f)
|
||||
{
|
||||
half_w *= draw->scale_factor;
|
||||
half_h *= draw->scale_factor;
|
||||
}
|
||||
{
|
||||
float c = cosf(draw->rotation);
|
||||
float s = sinf(draw->rotation);
|
||||
float inv_vw = 1.0f / (float)video_width;
|
||||
float inv_vh = 1.0f / (float)video_height;
|
||||
float ox[4] = { -half_w, half_w, -half_w, half_w };
|
||||
float oy[4] = { -half_h, -half_h, half_h, half_h };
|
||||
float rx[4], ry[4];
|
||||
int k;
|
||||
for (k = 0; k < 4; k++)
|
||||
{
|
||||
rx[k] = (ox[k] * c - oy[k] * s) * inv_vw;
|
||||
ry[k] = (ox[k] * s + oy[k] * c) * inv_vh;
|
||||
}
|
||||
quad[0].x = cx + rx[0]; quad[0].y = cy + ry[0]; quad[0].z = 0.5f;
|
||||
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
|
||||
|
||||
quad[1].x = x2; quad[1].y = y1; quad[1].z = 0.5f;
|
||||
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
|
||||
quad[1].x = cx + rx[1]; quad[1].y = cy + ry[1]; quad[1].z = 0.5f;
|
||||
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
|
||||
|
||||
quad[2].x = x1; quad[2].y = y2; quad[2].z = 0.5f;
|
||||
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
|
||||
quad[2].x = cx + rx[2]; quad[2].y = cy + ry[2]; quad[2].z = 0.5f;
|
||||
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
|
||||
|
||||
quad[3].x = x2; quad[3].y = y2; quad[3].z = 0.5f;
|
||||
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
|
||||
quad[3].x = cx + rx[3]; quad[3].y = cy + ry[3]; quad[3].z = 0.5f;
|
||||
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
quad[0].x = x1; quad[0].y = y1; quad[0].z = 0.5f;
|
||||
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
|
||||
|
||||
quad[1].x = x2; quad[1].y = y1; quad[1].z = 0.5f;
|
||||
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
|
||||
|
||||
quad[2].x = x1; quad[2].y = y2; quad[2].z = 0.5f;
|
||||
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
|
||||
|
||||
quad[3].x = x2; quad[3].y = y2; quad[3].z = 0.5f;
|
||||
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
|
||||
}
|
||||
|
||||
/* Top-down ortho: maps X [0,1]->[-1,1], Y [0,1]->[1,-1] (Y=0 at top).
|
||||
* Column-major (transposed) layout for the Cg stock shader which
|
||||
|
||||
+58
-8
@@ -42,6 +42,7 @@
|
||||
#include <string.h>
|
||||
#include <retro_inline.h>
|
||||
#include <retro_math.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef _XBOX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
@@ -944,17 +945,66 @@ static void gfx_display_d3d9_hlsl_draw(gfx_display_ctx_draw_t *draw,
|
||||
y2 = cy + hh;
|
||||
}
|
||||
|
||||
quad[0].x = x1; quad[0].y = y1; quad[0].z = 0.5f;
|
||||
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
|
||||
/* Apply draw->rotation around the quad center. Rotation is
|
||||
* computed in pixel space (using draw->width / draw->height as
|
||||
* the icon's true square extents) and then converted back to
|
||||
* normalised [0,1], so a non-square viewport does not skew the
|
||||
* rotated icon. */
|
||||
if (draw->rotation != 0.0f)
|
||||
{
|
||||
float cx = (x1 + x2) * 0.5f;
|
||||
float cy = (y1 + y2) * 0.5f;
|
||||
float half_w = draw->width * 0.5f;
|
||||
float half_h = draw->height * 0.5f;
|
||||
if (draw->scale_factor && draw->scale_factor != 1.0f)
|
||||
{
|
||||
half_w *= draw->scale_factor;
|
||||
half_h *= draw->scale_factor;
|
||||
}
|
||||
{
|
||||
float c = cosf(draw->rotation);
|
||||
float s = sinf(draw->rotation);
|
||||
float inv_vw = 1.0f / (float)video_width;
|
||||
float inv_vh = 1.0f / (float)video_height;
|
||||
/* Pixel-space corner offsets (dx, dy) for each of the
|
||||
* four quad vertices. Order matches the (x1,y1)..
|
||||
* (x2,y2) layout used immediately below. */
|
||||
float ox[4] = { -half_w, half_w, -half_w, half_w };
|
||||
float oy[4] = { -half_h, -half_h, half_h, half_h };
|
||||
float rx[4], ry[4];
|
||||
int k;
|
||||
for (k = 0; k < 4; k++)
|
||||
{
|
||||
rx[k] = (ox[k] * c - oy[k] * s) * inv_vw;
|
||||
ry[k] = (ox[k] * s + oy[k] * c) * inv_vh;
|
||||
}
|
||||
quad[0].x = cx + rx[0]; quad[0].y = cy + ry[0]; quad[0].z = 0.5f;
|
||||
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
|
||||
|
||||
quad[1].x = x2; quad[1].y = y1; quad[1].z = 0.5f;
|
||||
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
|
||||
quad[1].x = cx + rx[1]; quad[1].y = cy + ry[1]; quad[1].z = 0.5f;
|
||||
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
|
||||
|
||||
quad[2].x = x1; quad[2].y = y2; quad[2].z = 0.5f;
|
||||
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
|
||||
quad[2].x = cx + rx[2]; quad[2].y = cy + ry[2]; quad[2].z = 0.5f;
|
||||
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
|
||||
|
||||
quad[3].x = x2; quad[3].y = y2; quad[3].z = 0.5f;
|
||||
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
|
||||
quad[3].x = cx + rx[3]; quad[3].y = cy + ry[3]; quad[3].z = 0.5f;
|
||||
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
quad[0].x = x1; quad[0].y = y1; quad[0].z = 0.5f;
|
||||
quad[0].u = 0.0f; quad[0].v = 0.0f; quad[0].color = col[0];
|
||||
|
||||
quad[1].x = x2; quad[1].y = y1; quad[1].z = 0.5f;
|
||||
quad[1].u = 1.0f; quad[1].v = 0.0f; quad[1].color = col[1];
|
||||
|
||||
quad[2].x = x1; quad[2].y = y2; quad[2].z = 0.5f;
|
||||
quad[2].u = 0.0f; quad[2].v = 1.0f; quad[2].color = col[2];
|
||||
|
||||
quad[3].x = x2; quad[3].y = y2; quad[3].z = 0.5f;
|
||||
quad[3].u = 1.0f; quad[3].v = 1.0f; quad[3].color = col[3];
|
||||
}
|
||||
|
||||
/* Top-down ortho: maps X [0,1]→[-1,1], Y [0,1]→[1,-1] (Y=0 at top).
|
||||
* Row-major layout for mul(vector, matrix) in the stock HLSL vertex shader.
|
||||
|
||||
@@ -20,6 +20,7 @@ SRC(
|
||||
float4 color1 : COLOR1;
|
||||
float4 color2 : COLOR2;
|
||||
float4 color3 : COLOR3;
|
||||
float2 params : PARAMS;
|
||||
};
|
||||
struct PSInput
|
||||
{
|
||||
@@ -42,34 +43,75 @@ SRC(
|
||||
output.color1 = input.color1;
|
||||
output.color2 = input.color2;
|
||||
output.color3 = input.color3;
|
||||
/* params.x is consumed in the VS (scaling, above);
|
||||
* forward params.y (rotation, in radians) to the GS. */
|
||||
output.params = input.params;
|
||||
return output;
|
||||
}
|
||||
|
||||
[maxvertexcount(4)]
|
||||
void GSMain(point GSInput input[1], inout TriangleStream<PSInput> triStream)
|
||||
{
|
||||
/* Apply rotation around the sprite centre.
|
||||
*
|
||||
* input[0].position.xy = top-left corner of the quad (clip space)
|
||||
* input[0].position.zw = quad size (clip space)
|
||||
*
|
||||
* The four corners are at `position.xy + position.zw * c`
|
||||
* with `c` in {(0,0), (1,0), (0,1), (1,1)}. We rewrite as
|
||||
* `centre + position.zw * (c - 0.5)` and rotate the
|
||||
* `(c - 0.5)`-scaled offset by `params.y` radians around
|
||||
* the centre. The rotation is computed in pixel space and
|
||||
* then mapped back to clip space via the per-axis `.zw`
|
||||
* sizes; this keeps a square pixel-space icon square on
|
||||
* screen even on a non-square viewport, because position.z
|
||||
* and position.w both carry the same pixel-space length W
|
||||
* (z = 2W/vw, w = 2W/vh). Non-square icons would skew, but
|
||||
* the only caller of rotation today (the task-message
|
||||
* hourglass) draws a square. */
|
||||
PSInput output;
|
||||
float cosT = cos(input[0].params.y);
|
||||
float sinT = sin(input[0].params.y);
|
||||
float2 centre = input[0].position.xy + input[0].position.zw * 0.5;
|
||||
|
||||
/* Corner sign ordering matches the four triStream.Append
|
||||
* calls below; (sx, sy) ∈ {±1} are the corner-relative-to-
|
||||
* centre signs in pixel space. */
|
||||
float2 corner_signs[4] = {
|
||||
float2( 1.0, -1.0), /* top-right -> color0 */
|
||||
float2(-1.0, -1.0), /* top-left -> color1 */
|
||||
float2( 1.0, 1.0), /* bottom-right -> color2 */
|
||||
float2(-1.0, 1.0), /* bottom-left -> color3 */
|
||||
};
|
||||
float2 corner_uv[4] = {
|
||||
float2(1.0, 0.0),
|
||||
float2(0.0, 0.0),
|
||||
float2(1.0, 1.0),
|
||||
float2(0.0, 1.0),
|
||||
};
|
||||
float4 corner_colour[4] = {
|
||||
input[0].color0, input[0].color1,
|
||||
input[0].color2, input[0].color3,
|
||||
};
|
||||
|
||||
output.position.zw = float2(0.0f, 1.0f);
|
||||
|
||||
output.position.xy = input[0].position.xy + input[0].position.zw * float2(1.0, 0.0);
|
||||
output.texcoord = input[0].texcoord.xy + input[0].texcoord.zw * float2(1.0, 0.0);
|
||||
output.color = input[0].color0;
|
||||
triStream.Append(output);
|
||||
|
||||
output.position.xy = input[0].position.xy + input[0].position.zw * float2(0.0, 0.0);
|
||||
output.texcoord = input[0].texcoord.xy + input[0].texcoord.zw * float2(0.0, 0.0);
|
||||
output.color = input[0].color1;
|
||||
triStream.Append(output);
|
||||
|
||||
output.position.xy = input[0].position.xy + input[0].position.zw * float2(1.0, 1.0);
|
||||
output.texcoord = input[0].texcoord.xy + input[0].texcoord.zw * float2(1.0, 1.0);
|
||||
output.color = input[0].color2;
|
||||
triStream.Append(output);
|
||||
|
||||
output.position.xy = input[0].position.xy + input[0].position.zw * float2(0.0, 1.0);
|
||||
output.texcoord = input[0].texcoord.xy + input[0].texcoord.zw * float2(0.0, 1.0);
|
||||
output.color = input[0].color3;
|
||||
triStream.Append(output);
|
||||
[unroll]
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
float sx = corner_signs[i].x;
|
||||
float sy = corner_signs[i].y;
|
||||
/* Rotate the half-extent corner offset, then scale back
|
||||
* by the per-axis clip-space half-size. See block
|
||||
* comment above for why this preserves square icons. */
|
||||
float dx = (sx * cosT - sy * sinT) * input[0].position.z * 0.5;
|
||||
float dy = (sx * sinT + sy * cosT) * input[0].position.w * 0.5;
|
||||
output.position.xy = centre + float2(dx, dy);
|
||||
output.texcoord = input[0].texcoord.xy
|
||||
+ input[0].texcoord.zw * corner_uv[i];
|
||||
output.color = corner_colour[i];
|
||||
triStream.Append(output);
|
||||
}
|
||||
}
|
||||
|
||||
uniform sampler s0;
|
||||
|
||||
@@ -1278,6 +1278,13 @@ static void gfx_widgets_draw_task_msg(
|
||||
texture = MENU_WIDGETS_ICON_HOURGLASS;
|
||||
color = msg_queue_bar;
|
||||
radians = msg->hourglass_rotation;
|
||||
/* The display drivers that consume this rotation via the
|
||||
* cosine/sine pair (gl, gl1, glcore, vulkan) need the
|
||||
* trig values recomputed to match the live rotation -
|
||||
* leaving them at the zero-rotation defaults above pins
|
||||
* the hourglass icon to its starting orientation. */
|
||||
cosine = cosf(radians);
|
||||
sine = sinf(radians);
|
||||
}
|
||||
else if (msg->flags & DISPWIDG_FLAG_POSITIVE)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user