mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-06-07 07:35:02 -06:00
overlays: Use SDF equations to represent curved shapes
This commit is contained in:
parent
b607993b7b
commit
a0c91bf96a
@ -585,6 +585,18 @@ namespace rsx
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void overlay_element::configure_sdf(compiled_resource::command_config& config, sdf_function func)
|
||||||
|
{
|
||||||
|
config.sdf_config.func = func;
|
||||||
|
config.sdf_config.cx = margin_left + x + (w / 2.f);
|
||||||
|
config.sdf_config.cy = margin_top + y + (h / 2.f);
|
||||||
|
config.sdf_config.hx = w / 2.f;
|
||||||
|
config.sdf_config.hy = h / 2.f;
|
||||||
|
config.sdf_config.br = 0.f;
|
||||||
|
config.sdf_config.bw = border_size;
|
||||||
|
config.sdf_config.border_color = border_color;
|
||||||
|
}
|
||||||
|
|
||||||
compiled_resource& overlay_element::get_compiled()
|
compiled_resource& overlay_element::get_compiled()
|
||||||
{
|
{
|
||||||
if (is_compiled())
|
if (is_compiled())
|
||||||
@ -609,6 +621,14 @@ namespace rsx
|
|||||||
config.pulse_sinus_offset = pulse_sinus_offset;
|
config.pulse_sinus_offset = pulse_sinus_offset;
|
||||||
config.pulse_speed_modifier = pulse_speed_modifier;
|
config.pulse_speed_modifier = pulse_speed_modifier;
|
||||||
|
|
||||||
|
if (border_size != 0 &&
|
||||||
|
border_color.a > 0.f &&
|
||||||
|
w > border_size &&
|
||||||
|
h > border_size)
|
||||||
|
{
|
||||||
|
configure_sdf(config, sdf_function::box);
|
||||||
|
}
|
||||||
|
|
||||||
auto& verts = compiled_resources_temp.draw_commands.front().verts;
|
auto& verts = compiled_resources_temp.draw_commands.front().verts;
|
||||||
verts.resize(4);
|
verts.resize(4);
|
||||||
|
|
||||||
@ -1095,82 +1115,33 @@ namespace rsx
|
|||||||
return compiled_resources;
|
return compiled_resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
overlay_element::get_compiled();
|
||||||
if (true)
|
auto& config = compiled_resources.draw_commands.front().config;
|
||||||
#else
|
configure_sdf(config, sdf_function::rounded_box);
|
||||||
if (radius == 0 || radius > (w / 2))
|
config.sdf_config.br = radius;
|
||||||
#endif
|
|
||||||
|
m_is_compiled = true;
|
||||||
|
return compiled_resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
compiled_resource& ellipse::get_compiled()
|
||||||
|
{
|
||||||
|
if (is_compiled())
|
||||||
|
{
|
||||||
|
return compiled_resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
compiled_resources.clear();
|
||||||
|
|
||||||
|
if (!is_visible())
|
||||||
{
|
{
|
||||||
// Invalid radius
|
|
||||||
compiled_resources = overlay_element::get_compiled();
|
|
||||||
m_is_compiled = true;
|
m_is_compiled = true;
|
||||||
return compiled_resources;
|
return compiled_resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
compiled_resource compiled_resources_temp = {};
|
rounded_rect::get_compiled();
|
||||||
compiled_resources_temp.append({}); // Bg horizontal mid
|
auto& config = compiled_resources.draw_commands.front().config;
|
||||||
compiled_resources_temp.append({}); // Bg horizontal top
|
configure_sdf(config, sdf_function::ellipse);
|
||||||
compiled_resources_temp.append({}); // Bg horizontal bottom
|
|
||||||
compiled_resources_temp.append({}); // Bg upper-left
|
|
||||||
compiled_resources_temp.append({}); // Bg lower-left
|
|
||||||
compiled_resources_temp.append({}); // Bg upper-right
|
|
||||||
compiled_resources_temp.append({}); // Bg lower-right
|
|
||||||
|
|
||||||
for (auto& draw_cmd : compiled_resources_temp.draw_commands)
|
|
||||||
{
|
|
||||||
auto& config = draw_cmd.config;
|
|
||||||
config.color = back_color;
|
|
||||||
config.disable_vertex_snap = true;
|
|
||||||
config.pulse_glow = pulse_effect_enabled;
|
|
||||||
config.pulse_sinus_offset = pulse_sinus_offset;
|
|
||||||
config.pulse_speed_modifier = pulse_speed_modifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& bg0 = compiled_resources_temp.draw_commands[0];
|
|
||||||
auto& bg1 = compiled_resources_temp.draw_commands[1];
|
|
||||||
auto& bg2 = compiled_resources_temp.draw_commands[2];
|
|
||||||
|
|
||||||
bg0.verts.emplace_back(f32(x), f32(y + radius), 0.f, 0.f);
|
|
||||||
bg0.verts.emplace_back(f32(x + w), f32(y + radius), 0.f, 0.f);
|
|
||||||
bg0.verts.emplace_back(f32(x), f32(y + h) - radius, 0.f, 0.f);
|
|
||||||
bg0.verts.emplace_back(f32(x + w), f32(y + h) - radius, 0.f, 0.f);
|
|
||||||
|
|
||||||
bg1.verts.emplace_back(f32(x + radius), f32(y), 0.f, 0.f);
|
|
||||||
bg1.verts.emplace_back(f32(x + w) - radius, f32(y), 0.f, 0.f);
|
|
||||||
bg1.verts.emplace_back(f32(x + radius), f32(y + radius), 0.f, 0.f);
|
|
||||||
bg1.verts.emplace_back(f32(x + w) - radius, f32(y + radius), 0.f, 0.f);
|
|
||||||
|
|
||||||
bg2.verts.emplace_back(f32(x + radius), f32(y + h) - radius, 0.f, 0.f);
|
|
||||||
bg2.verts.emplace_back(f32(x + w) - radius, f32(y + h) - radius, 0.f, 0.f);
|
|
||||||
bg2.verts.emplace_back(f32(x + radius), f32(y + h), 0.f, 0.f);
|
|
||||||
bg2.verts.emplace_back(f32(x + w) - radius, f32(y + h), 0.f, 0.f);
|
|
||||||
|
|
||||||
// Generate the quadrants
|
|
||||||
const f32 corners[4][2] =
|
|
||||||
{
|
|
||||||
{ f32(x + radius), f32(y + radius) },
|
|
||||||
{ f32(x + radius), f32(y + h) - radius },
|
|
||||||
{ f32(x + w) - radius, f32(y + radius) },
|
|
||||||
{ f32(x + w) - radius, f32(y + h) - radius }
|
|
||||||
};
|
|
||||||
|
|
||||||
const f32 radius_f = static_cast<f32>(radius);
|
|
||||||
const f32 scale[4][2] =
|
|
||||||
{
|
|
||||||
{ -radius_f, -radius_f },
|
|
||||||
{ -radius_f, +radius_f },
|
|
||||||
{ +radius_f, -radius_f },
|
|
||||||
{ +radius_f, +radius_f }
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
auto& command = compiled_resources_temp.draw_commands[i + 3];
|
|
||||||
command.config.primitives = rsx::overlays::primitive_type::triangle_fan;
|
|
||||||
command.verts = generate_unit_quadrant(num_control_points, corners[i], scale[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
compiled_resources.add(std::move(compiled_resources_temp), margin_left, margin_top);
|
|
||||||
|
|
||||||
m_is_compiled = true;
|
m_is_compiled = true;
|
||||||
return compiled_resources;
|
return compiled_resources;
|
||||||
|
|||||||
@ -31,6 +31,14 @@ namespace rsx
|
|||||||
triangle_fan = 4
|
triangle_fan = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class sdf_function : u8
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
ellipse,
|
||||||
|
box,
|
||||||
|
rounded_box,
|
||||||
|
};
|
||||||
|
|
||||||
struct image_info_base
|
struct image_info_base
|
||||||
{
|
{
|
||||||
int w = 0, h = 0, channels = 0;
|
int w = 0, h = 0, channels = 0;
|
||||||
@ -95,6 +103,20 @@ namespace rsx
|
|||||||
|
|
||||||
struct compiled_resource
|
struct compiled_resource
|
||||||
{
|
{
|
||||||
|
struct sdf_config_t
|
||||||
|
{
|
||||||
|
sdf_function func = sdf_function::none;
|
||||||
|
|
||||||
|
f32 cx; // Center x
|
||||||
|
f32 cy; // Center y
|
||||||
|
f32 hx; // Half-size in X
|
||||||
|
f32 hy; // Half-size in Y
|
||||||
|
f32 br; // Border radius
|
||||||
|
f32 bw; // Border width
|
||||||
|
|
||||||
|
color4f border_color;
|
||||||
|
};
|
||||||
|
|
||||||
struct command_config
|
struct command_config
|
||||||
{
|
{
|
||||||
primitive_type primitives = primitive_type::quad_list;
|
primitive_type primitives = primitive_type::quad_list;
|
||||||
@ -105,6 +127,8 @@ namespace rsx
|
|||||||
f32 pulse_sinus_offset = 0.0f; // The current pulse offset
|
f32 pulse_sinus_offset = 0.0f; // The current pulse offset
|
||||||
f32 pulse_speed_modifier = 0.005f;
|
f32 pulse_speed_modifier = 0.005f;
|
||||||
|
|
||||||
|
sdf_config_t sdf_config;
|
||||||
|
|
||||||
areaf clip_rect = {};
|
areaf clip_rect = {};
|
||||||
bool clip_region = false;
|
bool clip_region = false;
|
||||||
|
|
||||||
@ -171,6 +195,9 @@ namespace rsx
|
|||||||
f32 pulse_sinus_offset = 0.0f; // The current pulse offset
|
f32 pulse_sinus_offset = 0.0f; // The current pulse offset
|
||||||
f32 pulse_speed_modifier = 0.005f;
|
f32 pulse_speed_modifier = 0.005f;
|
||||||
|
|
||||||
|
u8 border_size = 0;
|
||||||
|
color4f border_color = { 0.f, 0.f, 0.f, 1.f };
|
||||||
|
|
||||||
// Analog to command_config::get_sinus_value
|
// Analog to command_config::get_sinus_value
|
||||||
// Apply modifier for sinus pulse. Resets the pulse. For example:
|
// Apply modifier for sinus pulse. Resets the pulse. For example:
|
||||||
// 0 -> reset to 0.5 rising
|
// 0 -> reset to 0.5 rising
|
||||||
@ -237,6 +264,8 @@ namespace rsx
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool m_is_compiled = false; // Only use m_is_compiled as a getter in is_compiled() if possible
|
bool m_is_compiled = false; // Only use m_is_compiled as a getter in is_compiled() if possible
|
||||||
|
|
||||||
|
void configure_sdf(compiled_resource::command_config& config, sdf_function func);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct layout_container : public overlay_element
|
struct layout_container : public overlay_element
|
||||||
@ -317,12 +346,17 @@ namespace rsx
|
|||||||
struct rounded_rect : public overlay_element
|
struct rounded_rect : public overlay_element
|
||||||
{
|
{
|
||||||
u8 radius = 5;
|
u8 radius = 5;
|
||||||
u8 num_control_points = 8; // Smoothness control
|
|
||||||
|
|
||||||
using overlay_element::overlay_element;
|
using overlay_element::overlay_element;
|
||||||
compiled_resource& get_compiled() override;
|
compiled_resource& get_compiled() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ellipse : public rounded_rect
|
||||||
|
{
|
||||||
|
using rounded_rect::rounded_rect;
|
||||||
|
compiled_resource& get_compiled() override;
|
||||||
|
};
|
||||||
|
|
||||||
struct image_view : public overlay_element
|
struct image_view : public overlay_element
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@ -13,6 +13,11 @@ R"(
|
|||||||
#define SAMPLER_MODE_FONT3D 2
|
#define SAMPLER_MODE_FONT3D 2
|
||||||
#define SAMPLER_MODE_TEXTURE2D 3
|
#define SAMPLER_MODE_TEXTURE2D 3
|
||||||
|
|
||||||
|
#define SDF_DISABLED 0
|
||||||
|
#define SDF_ELLIPSE 1
|
||||||
|
#define SDF_BOX 2
|
||||||
|
#define SDF_ROUND_BOX 3
|
||||||
|
|
||||||
#ifdef VULKAN
|
#ifdef VULKAN
|
||||||
layout(set=0, binding=0) uniform sampler2D fs0;
|
layout(set=0, binding=0) uniform sampler2D fs0;
|
||||||
layout(set=0, binding=1) uniform sampler2DArray fs1;
|
layout(set=0, binding=1) uniform sampler2DArray fs1;
|
||||||
@ -34,11 +39,17 @@ layout(%push_block) uniform FragmentConfiguration
|
|||||||
uint fragment_config;
|
uint fragment_config;
|
||||||
float timestamp;
|
float timestamp;
|
||||||
float blur_intensity;
|
float blur_intensity;
|
||||||
|
vec4 sdf_params;
|
||||||
|
vec4 sdf_origin;
|
||||||
|
vec4 sdf_border_color;
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
uniform uint fragment_config;
|
uniform uint fragment_config;
|
||||||
uniform float timestamp;
|
uniform float timestamp;
|
||||||
uniform float blur_intensity;
|
uniform float blur_intensity;
|
||||||
|
uniform vec4 sdf_params;
|
||||||
|
uniform vec2 sdf_origin;
|
||||||
|
uniform vec4 sdf_border_color;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct config_t
|
struct config_t
|
||||||
@ -46,6 +57,7 @@ struct config_t
|
|||||||
bool clip_fragments;
|
bool clip_fragments;
|
||||||
bool use_pulse_glow;
|
bool use_pulse_glow;
|
||||||
uint sampler_mode;
|
uint sampler_mode;
|
||||||
|
uint sdf;
|
||||||
};
|
};
|
||||||
|
|
||||||
config_t unpack_fragment_options()
|
config_t unpack_fragment_options()
|
||||||
@ -54,9 +66,64 @@ config_t unpack_fragment_options()
|
|||||||
result.clip_fragments = bitfieldExtract(fragment_config, 0, 1) != 0;
|
result.clip_fragments = bitfieldExtract(fragment_config, 0, 1) != 0;
|
||||||
result.use_pulse_glow = bitfieldExtract(fragment_config, 1, 1) != 0;
|
result.use_pulse_glow = bitfieldExtract(fragment_config, 1, 1) != 0;
|
||||||
result.sampler_mode = bitfieldExtract(fragment_config, 2, 2);
|
result.sampler_mode = bitfieldExtract(fragment_config, 2, 2);
|
||||||
|
result.sdf = bitfieldExtract(fragment_config, 4, 2);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec4 SDF_blend(
|
||||||
|
const in float sd,
|
||||||
|
const in float border_width,
|
||||||
|
const in vec4 inner_color,
|
||||||
|
const in vec4 border_color,
|
||||||
|
const in vec4 outer_color)
|
||||||
|
{
|
||||||
|
// Crucially, we need to get the derivative without subracting the border width.
|
||||||
|
// Subtracting the border width makes the function non-continuous and makes the jaggies hard to get rid of.
|
||||||
|
float fw = fwidth(sd);
|
||||||
|
|
||||||
|
// Compute the two transition points. The inner edge is of course biased by the border amount as the clamping point
|
||||||
|
// Treat smoothstep as fancy clamp where e0 < x < e1
|
||||||
|
float a = smoothstep(-border_width + fw, -border_width - fw, sd); // inner edge transition
|
||||||
|
float b = smoothstep(fw, -fw, sd); // outer edge transition
|
||||||
|
|
||||||
|
// Mix the 3 colors with the transition values.
|
||||||
|
vec4 color = mix(outer_color, border_color, b);
|
||||||
|
color = mix(color, inner_color, a);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SDF_fn(const in uint sdf)
|
||||||
|
{
|
||||||
|
const vec2 p = floor(gl_FragCoord.xy) - sdf_origin.xy; // Screen-spac distance
|
||||||
|
const vec2 hs = sdf_params.xy; // Half size
|
||||||
|
const float r = sdf_params.z; // Radius (for round box, ellipses use half size instead)
|
||||||
|
vec2 v; // Scratch
|
||||||
|
float d; // Distance calculated
|
||||||
|
|
||||||
|
switch (sdf)
|
||||||
|
{
|
||||||
|
case SDF_ELLIPSE:
|
||||||
|
// Slightly inaccurate hack, but good enough for classification and allows oval shapes
|
||||||
|
d = length(p / hs) - 1.f;
|
||||||
|
// Now we need to correct for the border because the circle was scaled down to a unit
|
||||||
|
return d * length(hs);
|
||||||
|
case SDF_BOX:
|
||||||
|
// Insanity, reduced junction of 3 functions
|
||||||
|
// If for each axis the axis-aligned distance = D then you can select/clamp each axis separately by doing a max(D, 0) on all dimensions
|
||||||
|
// Length then does the squareroot transformation.
|
||||||
|
// The second term is to add back the inner distance which is useful for rendering borders
|
||||||
|
v = abs(p) - hs;
|
||||||
|
return length(max(v, 0.f)) + min(max(v.x, v.y), 0.0);
|
||||||
|
case SDF_ROUND_BOX:
|
||||||
|
// Modified BOX SDF.
|
||||||
|
// The half box size is shrunk by R in it's diagonal, but we add radius back into the output to bias the output again
|
||||||
|
v = abs(p) - (hs - r);
|
||||||
|
return length(max(v, 0.f)) + min(max(v.x, v.y), 0.0) - r;
|
||||||
|
default:
|
||||||
|
return -1.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vec4 blur_sample(sampler2D tex, vec2 coord, vec2 tex_offset)
|
vec4 blur_sample(sampler2D tex, vec2 coord, vec2 tex_offset)
|
||||||
{
|
{
|
||||||
vec2 coords[9];
|
vec2 coords[9];
|
||||||
@ -125,6 +192,13 @@ void main()
|
|||||||
diff_color.a *= (sin(timestamp) + 1.f) * 0.5f;
|
diff_color.a *= (sin(timestamp) + 1.f) * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.sdf != SDF_DISABLED)
|
||||||
|
{
|
||||||
|
const float border_w = sdf_params.w; // Border width
|
||||||
|
const float d = SDF_fn(config.sdf);
|
||||||
|
diff_color = SDF_blend(d, border_w, diff_color, sdf_border_color, vec4(0.));
|
||||||
|
}
|
||||||
|
|
||||||
switch (config.sampler_mode)
|
switch (config.sampler_mode)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -23,7 +23,8 @@ namespace rsx
|
|||||||
{
|
{
|
||||||
fragment_clip_bit = 0,
|
fragment_clip_bit = 0,
|
||||||
pulse_glow_bit = 1,
|
pulse_glow_bit = 1,
|
||||||
sampling_mode_bit = 2
|
sampling_mode_bit = 2,
|
||||||
|
sdf_func_offset_bit = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -51,6 +52,13 @@ namespace rsx
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fragment_options& set_sdf(sdf_function func)
|
||||||
|
{
|
||||||
|
value &= ~(0x3 << e_offsets::sdf_func_offset_bit);
|
||||||
|
value |= (static_cast<u32>(func) << e_offsets::sdf_func_offset_bit);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
u32 get() const
|
u32 get() const
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
@ -74,6 +82,13 @@ namespace rsx
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_bits(u32 offset, u32 count, u32 set)
|
||||||
|
{
|
||||||
|
const u32 mask = (0xffffffffu >> (32 - count)) << offset;
|
||||||
|
value &= ~mask;
|
||||||
|
value |= set;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
vertex_options& disable_vertex_snap(bool enable)
|
vertex_options& disable_vertex_snap(bool enable)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user