mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-30 01:25:51 +00:00
GPU/HW: Add JINC2 and xBRZ texture filtering options
Shaders ported from beetle-psx.
This commit is contained in:
parent
83f64dbc87
commit
6f250a4ff7
|
@ -13,6 +13,7 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c
|
|||
|
||||
## Latest News
|
||||
|
||||
- 2020/09/12: Additional texture filtering options added.
|
||||
- 2020/09/09: Basic cheat support added. Not all instructions/commands are supported yet.
|
||||
- 2020/09/01: Many additional user settings available, including memory cards and enhancements. Now you can set these per-game.
|
||||
- 2020/08/25: Automated builds for macOS now available.
|
||||
|
|
|
@ -26,7 +26,7 @@ ALWAYS_INLINE static constexpr std::tuple<T, T> MinMax(T v1, T v2)
|
|||
ALWAYS_INLINE static bool ShouldUseUVLimits()
|
||||
{
|
||||
// We only need UV limits if PGXP is enabled, or texture filtering is enabled.
|
||||
return g_settings.gpu_pgxp_enable || g_settings.gpu_texture_filtering;
|
||||
return g_settings.gpu_pgxp_enable || g_settings.gpu_texture_filter != GPUTextureFilter::Nearest;
|
||||
}
|
||||
|
||||
GPU_HW::GPU_HW() : GPU()
|
||||
|
@ -50,7 +50,7 @@ bool GPU_HW::Initialize(HostDisplay* host_display)
|
|||
m_render_api = host_display->GetRenderAPI();
|
||||
m_true_color = g_settings.gpu_true_color;
|
||||
m_scaled_dithering = g_settings.gpu_scaled_dithering;
|
||||
m_texture_filtering = g_settings.gpu_texture_filtering;
|
||||
m_texture_filtering = g_settings.gpu_texture_filter;
|
||||
m_using_uv_limits = ShouldUseUVLimits();
|
||||
PrintSettingsToLog();
|
||||
return true;
|
||||
|
@ -96,7 +96,7 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
|
|||
*framebuffer_changed = (m_resolution_scale != resolution_scale);
|
||||
*shaders_changed = (m_resolution_scale != resolution_scale || m_true_color != g_settings.gpu_true_color ||
|
||||
m_scaled_dithering != g_settings.gpu_scaled_dithering ||
|
||||
m_texture_filtering != g_settings.gpu_texture_filtering || m_using_uv_limits != use_uv_limits);
|
||||
m_texture_filtering != g_settings.gpu_texture_filter || m_using_uv_limits != use_uv_limits);
|
||||
|
||||
if (m_resolution_scale != resolution_scale)
|
||||
{
|
||||
|
@ -109,7 +109,7 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
|
|||
m_resolution_scale = resolution_scale;
|
||||
m_true_color = g_settings.gpu_true_color;
|
||||
m_scaled_dithering = g_settings.gpu_scaled_dithering;
|
||||
m_texture_filtering = g_settings.gpu_texture_filtering;
|
||||
m_texture_filtering = g_settings.gpu_texture_filter;
|
||||
m_using_uv_limits = use_uv_limits;
|
||||
PrintSettingsToLog();
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ void GPU_HW::PrintSettingsToLog()
|
|||
VRAM_HEIGHT * m_resolution_scale, m_max_resolution_scale);
|
||||
Log_InfoPrintf("Dithering: %s%s", m_true_color ? "Disabled" : "Enabled",
|
||||
(!m_true_color && m_scaled_dithering) ? " (Scaled)" : "");
|
||||
Log_InfoPrintf("Texture Filtering: %s", m_texture_filtering ? "Enabled" : "Disabled");
|
||||
Log_InfoPrintf("Texture Filtering: %s", Settings::GetTextureFilterDisplayName(m_texture_filtering));
|
||||
Log_InfoPrintf("Dual-source blending: %s", m_supports_dual_source_blend ? "Supported" : "Not supported");
|
||||
Log_InfoPrintf("Using UV limits: %s", m_using_uv_limits ? "YES" : "NO");
|
||||
}
|
||||
|
@ -1036,8 +1036,8 @@ void GPU_HW::DrawRendererStats(bool is_idle_frame)
|
|||
|
||||
ImGui::TextUnformatted("Texture Filtering:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(m_texture_filtering ? active_color : inactive_color,
|
||||
m_texture_filtering ? "Enabled" : "Disabled");
|
||||
ImGui::TextColored((m_texture_filtering != GPUTextureFilter::Nearest) ? active_color : inactive_color,
|
||||
Settings::GetTextureFilterDisplayName(m_texture_filtering));
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextUnformatted("PGXP:");
|
||||
|
|
|
@ -270,7 +270,7 @@ protected:
|
|||
HostDisplay::RenderAPI m_render_api = HostDisplay::RenderAPI::None;
|
||||
bool m_true_color = true;
|
||||
bool m_scaled_dithering = false;
|
||||
bool m_texture_filtering = false;
|
||||
GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest;
|
||||
bool m_supports_dual_source_blend = false;
|
||||
bool m_using_uv_limits = false;
|
||||
|
||||
|
|
|
@ -332,7 +332,7 @@ bool GPU_HW_D3D11::CreateStateObjects()
|
|||
for (u8 transparency_mode = 0; transparency_mode < 5; transparency_mode++)
|
||||
{
|
||||
bl_desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
|
||||
if (transparency_mode != static_cast<u8>(TransparencyMode::Disabled) || m_texture_filtering)
|
||||
if (transparency_mode != static_cast<u8>(TransparencyMode::Disabled) || m_texture_filtering != GPUTextureFilter::Nearest)
|
||||
{
|
||||
bl_desc.RenderTarget[0].BlendEnable = TRUE;
|
||||
bl_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
Log_SetChannel(GPU_HW_ShaderGen);
|
||||
|
||||
GPU_HW_ShaderGen::GPU_HW_ShaderGen(HostDisplay::RenderAPI render_api, u32 resolution_scale, bool true_color,
|
||||
bool scaled_dithering, bool texture_filtering, bool uv_limits,
|
||||
bool scaled_dithering, GPUTextureFilter texture_filtering, bool uv_limits,
|
||||
bool supports_dual_source_blend)
|
||||
: m_render_api(render_api), m_resolution_scale(resolution_scale), m_true_color(true_color),
|
||||
m_scaled_dithering(scaled_dithering), m_texture_filering(texture_filtering), m_uv_limits(uv_limits),
|
||||
m_scaled_dithering(scaled_dithering), m_texture_filter(texture_filtering), m_uv_limits(uv_limits),
|
||||
m_glsl(render_api != HostDisplay::RenderAPI::D3D11), m_supports_dual_source_blend(supports_dual_source_blend),
|
||||
m_use_glsl_interface_blocks(false)
|
||||
{
|
||||
|
@ -148,6 +148,8 @@ void GPU_HW_ShaderGen::WriteHeader(std::stringstream& ss)
|
|||
ss << "#define CONSTANT const\n";
|
||||
ss << "#define VECTOR_EQ(a, b) ((a) == (b))\n";
|
||||
ss << "#define VECTOR_NEQ(a, b) ((a) != (b))\n";
|
||||
ss << "#define VECTOR_COMP_EQ(a, b) equal((a), (b))\n";
|
||||
ss << "#define VECTOR_COMP_NEQ(a, b) notEqual((a), (b))\n";
|
||||
ss << "#define SAMPLE_TEXTURE(name, coords) texture(name, coords)\n";
|
||||
ss << "#define LOAD_TEXTURE(name, coords, mip) texelFetch(name, coords, mip)\n";
|
||||
ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) texelFetchOffset(name, coords, mip, offset)\n";
|
||||
|
@ -160,6 +162,8 @@ void GPU_HW_ShaderGen::WriteHeader(std::stringstream& ss)
|
|||
ss << "#define CONSTANT static const\n";
|
||||
ss << "#define VECTOR_EQ(a, b) (all((a) == (b)))\n";
|
||||
ss << "#define VECTOR_NEQ(a, b) (any((a) != (b)))\n";
|
||||
ss << "#define VECTOR_COMP_EQ(a, b) ((a) == (b))\n";
|
||||
ss << "#define VECTOR_COMP_NEQ(a, b) ((a) != (b))\n";
|
||||
ss << "#define SAMPLE_TEXTURE(name, coords) name.Sample(name##_ss, coords)\n";
|
||||
ss << "#define LOAD_TEXTURE(name, coords, mip) name.Load(int3(coords, mip))\n";
|
||||
ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) name.Load(int3(coords, mip), offset)\n";
|
||||
|
@ -578,6 +582,476 @@ std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(bool textured)
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
void GPU_HW_ShaderGen::WriteBatchTextureFilter(std::stringstream& ss, GPUTextureFilter texture_filter)
|
||||
{
|
||||
// JINC2 and xBRZ shaders originally from beetle-psx, modified to support filtering mask channel.
|
||||
if (texture_filter == GPUTextureFilter::Bilinear)
|
||||
{
|
||||
ss << R"(
|
||||
void FilteredSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
|
||||
out float4 texcol, out float ialpha)
|
||||
{
|
||||
// Compute the coordinates of the four texels we will be interpolating between.
|
||||
// Clamp this to the triangle texture coordinates.
|
||||
float2 texel_top_left = frac(coords) - float2(0.5, 0.5);
|
||||
float2 texel_offset = sign(texel_top_left);
|
||||
float4 fcoords = max(coords.xyxy + float4(0.0, 0.0, texel_offset.x, texel_offset.y),
|
||||
float4(0.0, 0.0, 0.0, 0.0));
|
||||
|
||||
// Load four texels.
|
||||
float4 s00 = SampleFromVRAM(texpage, clamp(fcoords.xy, uv_limits.xy, uv_limits.zw));
|
||||
float4 s10 = SampleFromVRAM(texpage, clamp(fcoords.zy, uv_limits.xy, uv_limits.zw));
|
||||
float4 s01 = SampleFromVRAM(texpage, clamp(fcoords.xw, uv_limits.xy, uv_limits.zw));
|
||||
float4 s11 = SampleFromVRAM(texpage, clamp(fcoords.zw, uv_limits.xy, uv_limits.zw));
|
||||
|
||||
// Compute alpha from how many texels aren't pixel color 0000h.
|
||||
float a00 = float(VECTOR_NEQ(s00, TRANSPARENT_PIXEL_COLOR));
|
||||
float a10 = float(VECTOR_NEQ(s10, TRANSPARENT_PIXEL_COLOR));
|
||||
float a01 = float(VECTOR_NEQ(s01, TRANSPARENT_PIXEL_COLOR));
|
||||
float a11 = float(VECTOR_NEQ(s11, TRANSPARENT_PIXEL_COLOR));
|
||||
|
||||
// Bilinearly interpolate.
|
||||
float2 weights = abs(texel_top_left);
|
||||
texcol = lerp(lerp(s00, s10, weights.x), lerp(s01, s11, weights.x), weights.y);
|
||||
ialpha = lerp(lerp(a00, a10, weights.x), lerp(a01, a11, weights.x), weights.y);
|
||||
|
||||
// Compensate for partially transparent sampling.
|
||||
if (ialpha > 0.0)
|
||||
texcol.rgb /= float3(ialpha, ialpha, ialpha);
|
||||
}
|
||||
)";
|
||||
}
|
||||
else if (texture_filter == GPUTextureFilter::JINC2)
|
||||
{
|
||||
ss << R"(
|
||||
CONSTANT float JINC2_WINDOW_SINC = 0.44;
|
||||
CONSTANT float JINC2_SINC = 0.82;
|
||||
CONSTANT float JINC2_AR_STRENGTH = 0.8;
|
||||
|
||||
CONSTANT float halfpi = 1.5707963267948966192313216916398;
|
||||
CONSTANT float pi = 3.1415926535897932384626433832795;
|
||||
CONSTANT float wa = 1.382300768;
|
||||
CONSTANT float wb = 2.576105976;
|
||||
|
||||
// Calculates the distance between two points
|
||||
float d(float2 pt1, float2 pt2)
|
||||
{
|
||||
float2 v = pt2 - pt1;
|
||||
return sqrt(dot(v,v));
|
||||
}
|
||||
|
||||
float min4(float a, float b, float c, float d)
|
||||
{
|
||||
return min(a, min(b, min(c, d)));
|
||||
}
|
||||
|
||||
float4 min4(float4 a, float4 b, float4 c, float4 d)
|
||||
{
|
||||
return min(a, min(b, min(c, d)));
|
||||
}
|
||||
|
||||
float max4(float a, float b, float c, float d)
|
||||
{
|
||||
return max(a, max(b, max(c, d)));
|
||||
}
|
||||
|
||||
float4 max4(float4 a, float4 b, float4 c, float4 d)
|
||||
{
|
||||
return max(a, max(b, max(c, d)));
|
||||
}
|
||||
|
||||
float4 resampler(float4 x)
|
||||
{
|
||||
float4 res;
|
||||
|
||||
// res = (x==float4(0.0, 0.0, 0.0, 0.0)) ? float4(wa*wb) : sin(x*wa)*sin(x*wb)/(x*x);
|
||||
// Need to use mix(.., equal(..)) since we want zero check to be component wise
|
||||
res = lerp(sin(x*wa)*sin(x*wb)/(x*x), float4(wa*wb, wa*wb, wa*wb, wa*wb), VECTOR_COMP_EQ(x,float4(0.0, 0.0, 0.0, 0.0)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void FilteredSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
|
||||
out float4 texcol, out float ialpha)
|
||||
{
|
||||
float4 weights[4];
|
||||
|
||||
float2 dx = float2(1.0, 0.0);
|
||||
float2 dy = float2(0.0, 1.0);
|
||||
|
||||
float2 pc = coords.xy;
|
||||
|
||||
float2 tc = (floor(pc-float2(0.5,0.5))+float2(0.5,0.5));
|
||||
|
||||
weights[0] = resampler(float4(d(pc, tc -dx -dy), d(pc, tc -dy), d(pc, tc +dx -dy), d(pc, tc+2.0*dx -dy)));
|
||||
weights[1] = resampler(float4(d(pc, tc -dx ), d(pc, tc ), d(pc, tc +dx ), d(pc, tc+2.0*dx )));
|
||||
weights[2] = resampler(float4(d(pc, tc -dx +dy), d(pc, tc +dy), d(pc, tc +dx +dy), d(pc, tc+2.0*dx +dy)));
|
||||
weights[3] = resampler(float4(d(pc, tc -dx+2.0*dy), d(pc, tc +2.0*dy), d(pc, tc +dx+2.0*dy), d(pc, tc+2.0*dx+2.0*dy)));
|
||||
|
||||
dx = dx;
|
||||
dy = dy;
|
||||
tc = tc;
|
||||
|
||||
#define sample_texel(coords) SampleFromVRAM(texpage, clamp((coords), uv_limits.xy, uv_limits.zw))
|
||||
|
||||
float4 c00 = sample_texel(tc -dx -dy);
|
||||
float a00 = float(VECTOR_NEQ(c00, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c10 = sample_texel(tc -dy);
|
||||
float a10 = float(VECTOR_NEQ(c10, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c20 = sample_texel(tc +dx -dy);
|
||||
float a20 = float(VECTOR_NEQ(c20, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c30 = sample_texel(tc+2.0*dx -dy);
|
||||
float a30 = float(VECTOR_NEQ(c30, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c01 = sample_texel(tc -dx );
|
||||
float a01 = float(VECTOR_NEQ(c01, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c11 = sample_texel(tc );
|
||||
float a11 = float(VECTOR_NEQ(c11, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c21 = sample_texel(tc +dx );
|
||||
float a21 = float(VECTOR_NEQ(c21, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c31 = sample_texel(tc+2.0*dx );
|
||||
float a31 = float(VECTOR_NEQ(c31, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c02 = sample_texel(tc -dx +dy);
|
||||
float a02 = float(VECTOR_NEQ(c02, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c12 = sample_texel(tc +dy);
|
||||
float a12 = float(VECTOR_NEQ(c12, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c22 = sample_texel(tc +dx +dy);
|
||||
float a22 = float(VECTOR_NEQ(c22, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c32 = sample_texel(tc+2.0*dx +dy);
|
||||
float a32 = float(VECTOR_NEQ(c32, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c03 = sample_texel(tc -dx+2.0*dy);
|
||||
float a03 = float(VECTOR_NEQ(c03, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c13 = sample_texel(tc +2.0*dy);
|
||||
float a13 = float(VECTOR_NEQ(c13, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c23 = sample_texel(tc +dx+2.0*dy);
|
||||
float a23 = float(VECTOR_NEQ(c23, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 c33 = sample_texel(tc+2.0*dx+2.0*dy);
|
||||
float a33 = float(VECTOR_NEQ(c33, TRANSPARENT_PIXEL_COLOR));
|
||||
|
||||
#undef sample_texel
|
||||
|
||||
// Get min/max samples
|
||||
float4 min_sample = min4(c11, c21, c12, c22);
|
||||
float min_sample_alpha = min4(a11, a21, a12, a22);
|
||||
float4 max_sample = max4(c11, c21, c12, c22);
|
||||
float max_sample_alpha = max4(a11, a21, a12, a22);
|
||||
|
||||
float4 color;
|
||||
color = float4(dot(weights[0], float4(c00.x, c10.x, c20.x, c30.x)), dot(weights[0], float4(c00.y, c10.y, c20.y, c30.y)), dot(weights[0], float4(c00.z, c10.z, c20.z, c30.z)), dot(weights[0], float4(c00.w, c10.w, c20.w, c30.w)));
|
||||
color+= float4(dot(weights[1], float4(c01.x, c11.x, c21.x, c31.x)), dot(weights[1], float4(c01.y, c11.y, c21.y, c31.y)), dot(weights[1], float4(c01.z, c11.z, c21.z, c31.z)), dot(weights[1], float4(c01.w, c11.w, c21.w, c31.w)));
|
||||
color+= float4(dot(weights[2], float4(c02.x, c12.x, c22.x, c32.x)), dot(weights[2], float4(c02.y, c12.y, c22.y, c32.y)), dot(weights[2], float4(c02.z, c12.z, c22.z, c32.z)), dot(weights[2], float4(c02.w, c12.w, c22.w, c32.w)));
|
||||
color+= float4(dot(weights[3], float4(c03.x, c13.x, c23.x, c33.x)), dot(weights[3], float4(c03.y, c13.y, c23.y, c33.y)), dot(weights[3], float4(c03.z, c13.z, c23.z, c33.z)), dot(weights[3], float4(c03.w, c13.w, c23.w, c33.w)));
|
||||
color = color/(dot(weights[0], float4(1,1,1,1)) + dot(weights[1], float4(1,1,1,1)) + dot(weights[2], float4(1,1,1,1)) + dot(weights[3], float4(1,1,1,1)));
|
||||
|
||||
float alpha;
|
||||
alpha = dot(weights[0], float4(a00, a10, a20, a30));
|
||||
alpha+= dot(weights[1], float4(a01, a11, a21, a31));
|
||||
alpha+= dot(weights[2], float4(a02, a12, a22, a32));
|
||||
alpha+= dot(weights[3], float4(a03, a13, a23, a33));
|
||||
//alpha = alpha/(weights[0].w + weights[1].w + weights[2].w + weights[3].w);
|
||||
alpha = alpha/(dot(weights[0], float4(1,1,1,1)) + dot(weights[1], float4(1,1,1,1)) + dot(weights[2], float4(1,1,1,1)) + dot(weights[3], float4(1,1,1,1)));
|
||||
|
||||
// Anti-ringing
|
||||
float4 aux = color;
|
||||
float aux_alpha = alpha;
|
||||
color = clamp(color, min_sample, max_sample);
|
||||
alpha = clamp(alpha, min_sample_alpha, max_sample_alpha);
|
||||
color = lerp(aux, color, JINC2_AR_STRENGTH);
|
||||
alpha = lerp(aux_alpha, alpha, JINC2_AR_STRENGTH);
|
||||
|
||||
// final sum and weight normalization
|
||||
ialpha = alpha;
|
||||
texcol = color;
|
||||
|
||||
// Compensate for partially transparent sampling.
|
||||
if (ialpha > 0.0)
|
||||
texcol.rgb /= float3(ialpha, ialpha, ialpha);
|
||||
}
|
||||
)";
|
||||
}
|
||||
else if (texture_filter == GPUTextureFilter::xBRZ)
|
||||
{
|
||||
ss << R"(
|
||||
CONSTANT int BLEND_NONE = 0;
|
||||
CONSTANT int BLEND_NORMAL = 1;
|
||||
CONSTANT int BLEND_DOMINANT = 2;
|
||||
CONSTANT float LUMINANCE_WEIGHT = 1.0;
|
||||
CONSTANT float EQUAL_COLOR_TOLERANCE = 0.1176470588235294;
|
||||
CONSTANT float STEEP_DIRECTION_THRESHOLD = 2.2;
|
||||
CONSTANT float DOMINANT_DIRECTION_THRESHOLD = 3.6;
|
||||
CONSTANT float4 w = float4(0.2627, 0.6780, 0.0593, 0.5);
|
||||
|
||||
float DistYCbCr(float4 pixA, float4 pixB)
|
||||
{
|
||||
const float scaleB = 0.5 / (1.0 - w.b);
|
||||
const float scaleR = 0.5 / (1.0 - w.r);
|
||||
float4 diff = pixA - pixB;
|
||||
float Y = dot(diff, w);
|
||||
float Cb = scaleB * (diff.b - Y);
|
||||
float Cr = scaleR * (diff.r - Y);
|
||||
|
||||
return sqrt(((LUMINANCE_WEIGHT * Y) * (LUMINANCE_WEIGHT * Y)) + (Cb * Cb) + (Cr * Cr));
|
||||
}
|
||||
|
||||
bool IsPixEqual(const float4 pixA, const float4 pixB)
|
||||
{
|
||||
return (DistYCbCr(pixA, pixB) < EQUAL_COLOR_TOLERANCE);
|
||||
}
|
||||
|
||||
float get_left_ratio(float2 center, float2 origin, float2 direction, float2 scale)
|
||||
{
|
||||
float2 P0 = center - origin;
|
||||
float2 proj = direction * (dot(P0, direction) / dot(direction, direction));
|
||||
float2 distv = P0 - proj;
|
||||
float2 orth = float2(-direction.y, direction.x);
|
||||
float side = sign(dot(P0, orth));
|
||||
float v = side * length(distv * scale);
|
||||
|
||||
// return step(0, v);
|
||||
return smoothstep(-sqrt(2.0)/2.0, sqrt(2.0)/2.0, v);
|
||||
}
|
||||
|
||||
#define P(coord, xoffs, yoffs) SampleFromVRAM(texpage, clamp(coords + float2((xoffs), (yoffs)), uv_limits.xy, uv_limits.zw))
|
||||
|
||||
void FilteredSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
|
||||
out float4 texcol, out float ialpha)
|
||||
{
|
||||
//---------------------------------------
|
||||
// Input Pixel Mapping: -|x|x|x|-
|
||||
// x|A|B|C|x
|
||||
// x|D|E|F|x
|
||||
// x|G|H|I|x
|
||||
// -|x|x|x|-
|
||||
|
||||
float2 scale = float2(8.0, 8.0);
|
||||
float2 pos = frac(coords.xy) - float2(0.5, 0.5);
|
||||
float2 coord = coords.xy - pos;
|
||||
|
||||
float4 A = P(coord, -1,-1);
|
||||
float Aw = A.w;
|
||||
A.w = float(VECTOR_NEQ(A, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 B = P(coord, 0,-1);
|
||||
float Bw = B.w;
|
||||
B.w = float(VECTOR_NEQ(B, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 C = P(coord, 1,-1);
|
||||
float Cw = C.w;
|
||||
C.w = float(VECTOR_NEQ(C, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 D = P(coord, -1, 0);
|
||||
float Dw = D.w;
|
||||
D.w = float(VECTOR_NEQ(D, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 E = P(coord, 0, 0);
|
||||
float Ew = E.w;
|
||||
E.w = float(VECTOR_NEQ(E, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 F = P(coord, 1, 0);
|
||||
float Fw = F.w;
|
||||
F.w = float(VECTOR_NEQ(F, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 G = P(coord, -1, 1);
|
||||
float Gw = G.w;
|
||||
G.w = float(VECTOR_NEQ(G, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 H = P(coord, 0, 1);
|
||||
float Hw = H.w;
|
||||
H.w = float(VECTOR_NEQ(H, TRANSPARENT_PIXEL_COLOR));
|
||||
float4 I = P(coord, 1, 1);
|
||||
float Iw = I.w;
|
||||
I.w = float(VECTOR_NEQ(H, TRANSPARENT_PIXEL_COLOR));
|
||||
|
||||
// blendResult Mapping: x|y|
|
||||
// w|z|
|
||||
int4 blendResult = int4(BLEND_NONE,BLEND_NONE,BLEND_NONE,BLEND_NONE);
|
||||
|
||||
// Preprocess corners
|
||||
// Pixel Tap Mapping: -|-|-|-|-
|
||||
// -|-|B|C|-
|
||||
// -|D|E|F|x
|
||||
// -|G|H|I|x
|
||||
// -|-|x|x|-
|
||||
if (!((VECTOR_EQ(E,F) && VECTOR_EQ(H,I)) || (VECTOR_EQ(E,H) && VECTOR_EQ(F,I))))
|
||||
{
|
||||
float dist_H_F = DistYCbCr(G, E) + DistYCbCr(E, C) + DistYCbCr(P(coord, 0,2), I) + DistYCbCr(I, P(coord, 2,0)) + (4.0 * DistYCbCr(H, F));
|
||||
float dist_E_I = DistYCbCr(D, H) + DistYCbCr(H, P(coord, 1,2)) + DistYCbCr(B, F) + DistYCbCr(F, P(coord, 2,1)) + (4.0 * DistYCbCr(E, I));
|
||||
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_H_F) < dist_E_I;
|
||||
blendResult.z = ((dist_H_F < dist_E_I) && VECTOR_NEQ(E,F) && VECTOR_NEQ(E,H)) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
|
||||
}
|
||||
|
||||
|
||||
// Pixel Tap Mapping: -|-|-|-|-
|
||||
// -|A|B|-|-
|
||||
// x|D|E|F|-
|
||||
// x|G|H|I|-
|
||||
// -|x|x|-|-
|
||||
if (!((VECTOR_EQ(D,E) && VECTOR_EQ(G,H)) || (VECTOR_EQ(D,G) && VECTOR_EQ(E,H))))
|
||||
{
|
||||
float dist_G_E = DistYCbCr(P(coord, -2,1) , D) + DistYCbCr(D, B) + DistYCbCr(P(coord, -1,2), H) + DistYCbCr(H, F) + (4.0 * DistYCbCr(G, E));
|
||||
float dist_D_H = DistYCbCr(P(coord, -2,0) , G) + DistYCbCr(G, P(coord, 0,2)) + DistYCbCr(A, E) + DistYCbCr(E, I) + (4.0 * DistYCbCr(D, H));
|
||||
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_D_H) < dist_G_E;
|
||||
blendResult.w = ((dist_G_E > dist_D_H) && VECTOR_NEQ(E,D) && VECTOR_NEQ(E,H)) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
|
||||
}
|
||||
|
||||
// Pixel Tap Mapping: -|-|x|x|-
|
||||
// -|A|B|C|x
|
||||
// -|D|E|F|x
|
||||
// -|-|H|I|-
|
||||
// -|-|-|-|-
|
||||
if (!((VECTOR_EQ(B,C) && VECTOR_EQ(E,F)) || (VECTOR_EQ(B,E) && VECTOR_EQ(C,F))))
|
||||
{
|
||||
float dist_E_C = DistYCbCr(D, B) + DistYCbCr(B, P(coord, 1,-2)) + DistYCbCr(H, F) + DistYCbCr(F, P(coord, 2,-1)) + (4.0 * DistYCbCr(E, C));
|
||||
float dist_B_F = DistYCbCr(A, E) + DistYCbCr(E, I) + DistYCbCr(P(coord, 0,-2), C) + DistYCbCr(C, P(coord, 2,0)) + (4.0 * DistYCbCr(B, F));
|
||||
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_B_F) < dist_E_C;
|
||||
blendResult.y = ((dist_E_C > dist_B_F) && VECTOR_NEQ(E,B) && VECTOR_NEQ(E,F)) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
|
||||
}
|
||||
|
||||
// Pixel Tap Mapping: -|x|x|-|-
|
||||
// x|A|B|C|-
|
||||
// x|D|E|F|-
|
||||
// -|G|H|-|-
|
||||
// -|-|-|-|-
|
||||
if (!((VECTOR_EQ(A,B) && VECTOR_EQ(D,E)) || (VECTOR_EQ(A,D) && VECTOR_EQ(B,E))))
|
||||
{
|
||||
float dist_D_B = DistYCbCr(P(coord, -2,0), A) + DistYCbCr(A, P(coord, 0,-2)) + DistYCbCr(G, E) + DistYCbCr(E, C) + (4.0 * DistYCbCr(D, B));
|
||||
float dist_A_E = DistYCbCr(P(coord, -2,-1), D) + DistYCbCr(D, H) + DistYCbCr(P(coord, -1,-2), B) + DistYCbCr(B, F) + (4.0 * DistYCbCr(A, E));
|
||||
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_D_B) < dist_A_E;
|
||||
blendResult.x = ((dist_D_B < dist_A_E) && VECTOR_NEQ(E,D) && VECTOR_NEQ(E,B)) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
|
||||
}
|
||||
|
||||
float4 res = E;
|
||||
float resW = Ew;
|
||||
|
||||
// Pixel Tap Mapping: -|-|-|-|-
|
||||
// -|-|B|C|-
|
||||
// -|D|E|F|x
|
||||
// -|G|H|I|x
|
||||
// -|-|x|x|-
|
||||
if(blendResult.z != BLEND_NONE)
|
||||
{
|
||||
float dist_F_G = DistYCbCr(F, G);
|
||||
float dist_H_C = DistYCbCr(H, C);
|
||||
bool doLineBlend = (blendResult.z == BLEND_DOMINANT ||
|
||||
!((blendResult.y != BLEND_NONE && !IsPixEqual(E, G)) || (blendResult.w != BLEND_NONE && !IsPixEqual(E, C)) ||
|
||||
(IsPixEqual(G, H) && IsPixEqual(H, I) && IsPixEqual(I, F) && IsPixEqual(F, C) && !IsPixEqual(E, I))));
|
||||
|
||||
float2 origin = float2(0.0, 1.0 / sqrt(2.0));
|
||||
float2 direction = float2(1.0, -1.0);
|
||||
if(doLineBlend)
|
||||
{
|
||||
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_F_G <= dist_H_C) && VECTOR_NEQ(E,G) && VECTOR_NEQ(D,G);
|
||||
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_H_C <= dist_F_G) && VECTOR_NEQ(E,C) && VECTOR_NEQ(B,C);
|
||||
origin = haveShallowLine? float2(0.0, 0.25) : float2(0.0, 0.5);
|
||||
direction.x += haveShallowLine? 1.0: 0.0;
|
||||
direction.y -= haveSteepLine? 1.0: 0.0;
|
||||
}
|
||||
|
||||
float4 blendPix = lerp(H,F, step(DistYCbCr(E, F), DistYCbCr(E, H)));
|
||||
float blendW = lerp(Hw,Fw, step(DistYCbCr(E, F), DistYCbCr(E, H)));
|
||||
res = lerp(res, blendPix, get_left_ratio(pos, origin, direction, scale));
|
||||
resW = lerp(resW, blendW, get_left_ratio(pos, origin, direction, scale));
|
||||
}
|
||||
|
||||
// Pixel Tap Mapping: -|-|-|-|-
|
||||
// -|A|B|-|-
|
||||
// x|D|E|F|-
|
||||
// x|G|H|I|-
|
||||
// -|x|x|-|-
|
||||
if(blendResult.w != BLEND_NONE)
|
||||
{
|
||||
float dist_H_A = DistYCbCr(H, A);
|
||||
float dist_D_I = DistYCbCr(D, I);
|
||||
bool doLineBlend = (blendResult.w == BLEND_DOMINANT ||
|
||||
!((blendResult.z != BLEND_NONE && !IsPixEqual(E, A)) || (blendResult.x != BLEND_NONE && !IsPixEqual(E, I)) ||
|
||||
(IsPixEqual(A, D) && IsPixEqual(D, G) && IsPixEqual(G, H) && IsPixEqual(H, I) && !IsPixEqual(E, G))));
|
||||
|
||||
float2 origin = float2(-1.0 / sqrt(2.0), 0.0);
|
||||
float2 direction = float2(1.0, 1.0);
|
||||
if(doLineBlend)
|
||||
{
|
||||
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_H_A <= dist_D_I) && VECTOR_NEQ(E,A) && VECTOR_NEQ(B,A);
|
||||
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_D_I <= dist_H_A) && VECTOR_NEQ(E,I) && VECTOR_NEQ(F,I);
|
||||
origin = haveShallowLine? float2(-0.25, 0.0) : float2(-0.5, 0.0);
|
||||
direction.y += haveShallowLine? 1.0: 0.0;
|
||||
direction.x += haveSteepLine? 1.0: 0.0;
|
||||
}
|
||||
origin = origin;
|
||||
direction = direction;
|
||||
|
||||
float4 blendPix = lerp(H,D, step(DistYCbCr(E, D), DistYCbCr(E, H)));
|
||||
float blendW = lerp(Hw,Dw, step(DistYCbCr(E, D), DistYCbCr(E, H)));
|
||||
res = lerp(res, blendPix, get_left_ratio(pos, origin, direction, scale));
|
||||
resW = lerp(resW, blendW, get_left_ratio(pos, origin, direction, scale));
|
||||
}
|
||||
|
||||
// Pixel Tap Mapping: -|-|x|x|-
|
||||
// -|A|B|C|x
|
||||
// -|D|E|F|x
|
||||
// -|-|H|I|-
|
||||
// -|-|-|-|-
|
||||
if(blendResult.y != BLEND_NONE)
|
||||
{
|
||||
float dist_B_I = DistYCbCr(B, I);
|
||||
float dist_F_A = DistYCbCr(F, A);
|
||||
bool doLineBlend = (blendResult.y == BLEND_DOMINANT ||
|
||||
!((blendResult.x != BLEND_NONE && !IsPixEqual(E, I)) || (blendResult.z != BLEND_NONE && !IsPixEqual(E, A)) ||
|
||||
(IsPixEqual(I, F) && IsPixEqual(F, C) && IsPixEqual(C, B) && IsPixEqual(B, A) && !IsPixEqual(E, C))));
|
||||
|
||||
float2 origin = float2(1.0 / sqrt(2.0), 0.0);
|
||||
float2 direction = float2(-1.0, -1.0);
|
||||
|
||||
if(doLineBlend)
|
||||
{
|
||||
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_B_I <= dist_F_A) && VECTOR_NEQ(E,I) && VECTOR_NEQ(H,I);
|
||||
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_F_A <= dist_B_I) && VECTOR_NEQ(E,A) && VECTOR_NEQ(D,A);
|
||||
origin = haveShallowLine? float2(0.25, 0.0) : float2(0.5, 0.0);
|
||||
direction.y -= haveShallowLine? 1.0: 0.0;
|
||||
direction.x -= haveSteepLine? 1.0: 0.0;
|
||||
}
|
||||
|
||||
float4 blendPix = lerp(F,B, step(DistYCbCr(E, B), DistYCbCr(E, F)));
|
||||
float blendW = lerp(Fw,Bw, step(DistYCbCr(E, B), DistYCbCr(E, F)));
|
||||
res = lerp(res, blendPix, get_left_ratio(pos, origin, direction, scale));
|
||||
resW = lerp(resW, blendW, get_left_ratio(pos, origin, direction, scale));
|
||||
}
|
||||
|
||||
// Pixel Tap Mapping: -|x|x|-|-
|
||||
// x|A|B|C|-
|
||||
// x|D|E|F|-
|
||||
// -|G|H|-|-
|
||||
// -|-|-|-|-
|
||||
if(blendResult.x != BLEND_NONE)
|
||||
{
|
||||
float dist_D_C = DistYCbCr(D, C);
|
||||
float dist_B_G = DistYCbCr(B, G);
|
||||
bool doLineBlend = (blendResult.x == BLEND_DOMINANT ||
|
||||
!((blendResult.w != BLEND_NONE && !IsPixEqual(E, C)) || (blendResult.y != BLEND_NONE && !IsPixEqual(E, G)) ||
|
||||
(IsPixEqual(C, B) && IsPixEqual(B, A) && IsPixEqual(A, D) && IsPixEqual(D, G) && !IsPixEqual(E, A))));
|
||||
|
||||
float2 origin = float2(0.0, -1.0 / sqrt(2.0));
|
||||
float2 direction = float2(-1.0, 1.0);
|
||||
if(doLineBlend)
|
||||
{
|
||||
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_D_C <= dist_B_G) && VECTOR_NEQ(E,C) && VECTOR_NEQ(F,C);
|
||||
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_B_G <= dist_D_C) && VECTOR_NEQ(E,G) && VECTOR_NEQ(H,G);
|
||||
origin = haveShallowLine? float2(0.0, -0.25) : float2(0.0, -0.5);
|
||||
direction.x -= haveShallowLine? 1.0: 0.0;
|
||||
direction.y += haveSteepLine? 1.0: 0.0;
|
||||
}
|
||||
|
||||
float4 blendPix = lerp(D,B, step(DistYCbCr(E, B), DistYCbCr(E, D)));
|
||||
float blendW = lerp(Dw,Bw, step(DistYCbCr(E, B), DistYCbCr(E, D)));
|
||||
res = lerp(res, blendPix, get_left_ratio(pos, origin, direction, scale));
|
||||
resW = lerp(resW, blendW, get_left_ratio(pos, origin, direction, scale));
|
||||
}
|
||||
|
||||
ialpha = res.w;
|
||||
texcol = float4(res.xyz, resW);
|
||||
|
||||
// Compensate for partially transparent sampling.
|
||||
if (ialpha > 0.0)
|
||||
texcol.rgb /= float3(ialpha, ialpha, ialpha);
|
||||
}
|
||||
|
||||
#undef P
|
||||
|
||||
)";
|
||||
}
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMode transparency,
|
||||
GPU::TextureMode texture_mode, bool dithering,
|
||||
bool interlacing)
|
||||
|
@ -588,7 +1062,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMod
|
|||
const bool use_dual_source =
|
||||
m_supports_dual_source_blend && ((transparency != GPU_HW::BatchRenderMode::TransparencyDisabled &&
|
||||
transparency != GPU_HW::BatchRenderMode::OnlyOpaque) ||
|
||||
m_texture_filering);
|
||||
m_texture_filter != GPUTextureFilter::Nearest);
|
||||
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
|
@ -606,7 +1080,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMod
|
|||
DefineMacro(ss, "DITHERING_SCALED", m_scaled_dithering);
|
||||
DefineMacro(ss, "INTERLACING", interlacing);
|
||||
DefineMacro(ss, "TRUE_COLOR", m_true_color);
|
||||
DefineMacro(ss, "TEXTURE_FILTERING", m_texture_filering);
|
||||
DefineMacro(ss, "TEXTURE_FILTERING", m_texture_filter != GPUTextureFilter::Nearest);
|
||||
DefineMacro(ss, "UV_LIMITS", m_uv_limits);
|
||||
DefineMacro(ss, "USE_DUAL_SOURCE", use_dual_source);
|
||||
|
||||
|
@ -708,43 +1182,14 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
|
|||
#endif
|
||||
}
|
||||
|
||||
void BilinearSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
|
||||
out float4 texcol, out float ialpha)
|
||||
{
|
||||
// Compute the coordinates of the four texels we will be interpolating between.
|
||||
// Clamp this to the triangle texture coordinates.
|
||||
float2 texel_top_left = frac(coords) - float2(0.5, 0.5);
|
||||
float2 texel_offset = sign(texel_top_left);
|
||||
float4 fcoords = max(coords.xyxy + float4(0.0, 0.0, texel_offset.x, texel_offset.y),
|
||||
float4(0.0, 0.0, 0.0, 0.0));
|
||||
|
||||
// Load four texels.
|
||||
float4 s00 = SampleFromVRAM(texpage, clamp(fcoords.xy, uv_limits.xy, uv_limits.zw));
|
||||
float4 s10 = SampleFromVRAM(texpage, clamp(fcoords.zy, uv_limits.xy, uv_limits.zw));
|
||||
float4 s01 = SampleFromVRAM(texpage, clamp(fcoords.xw, uv_limits.xy, uv_limits.zw));
|
||||
float4 s11 = SampleFromVRAM(texpage, clamp(fcoords.zw, uv_limits.xy, uv_limits.zw));
|
||||
|
||||
// Compute alpha from how many texels aren't pixel color 0000h.
|
||||
float a00 = float(VECTOR_NEQ(s00, TRANSPARENT_PIXEL_COLOR));
|
||||
float a10 = float(VECTOR_NEQ(s10, TRANSPARENT_PIXEL_COLOR));
|
||||
float a01 = float(VECTOR_NEQ(s01, TRANSPARENT_PIXEL_COLOR));
|
||||
float a11 = float(VECTOR_NEQ(s11, TRANSPARENT_PIXEL_COLOR));
|
||||
|
||||
// Bilinearly interpolate.
|
||||
float2 weights = abs(texel_top_left);
|
||||
texcol = lerp(lerp(s00, s10, weights.x), lerp(s01, s11, weights.x), weights.y);
|
||||
ialpha = lerp(lerp(a00, a10, weights.x), lerp(a01, a11, weights.x), weights.y);
|
||||
|
||||
// Compensate for partially transparent sampling.
|
||||
if (ialpha > 0.0)
|
||||
texcol.rgb /= float3(ialpha, ialpha, ialpha);
|
||||
}
|
||||
|
||||
#endif
|
||||
)";
|
||||
|
||||
if (textured)
|
||||
{
|
||||
if (m_texture_filter != GPUTextureFilter::Nearest)
|
||||
WriteBatchTextureFilter(ss, m_texture_filter);
|
||||
|
||||
if (m_uv_limits)
|
||||
{
|
||||
DeclareFragmentEntryPoint(ss, 1, 1,
|
||||
|
@ -794,7 +1239,7 @@ void BilinearSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
|
|||
|
||||
float4 texcol;
|
||||
#if TEXTURE_FILTERING
|
||||
BilinearSampleFromVRAM(v_texpage, coords, uv_limits, texcol, ialpha);
|
||||
FilteredSampleFromVRAM(v_texpage, coords, uv_limits, texcol, ialpha);
|
||||
if (ialpha < 0.5)
|
||||
discard;
|
||||
#else
|
||||
|
@ -809,7 +1254,7 @@ void BilinearSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
|
|||
ialpha = 1.0;
|
||||
#endif
|
||||
|
||||
semitransparent = (texcol.a != 0.0);
|
||||
semitransparent = (texcol.a >= 0.5);
|
||||
|
||||
// If not using true color, truncate the framebuffer colors to 5-bit.
|
||||
#if !TRUE_COLOR
|
||||
|
|
|
@ -8,7 +8,7 @@ class GPU_HW_ShaderGen
|
|||
{
|
||||
public:
|
||||
GPU_HW_ShaderGen(HostDisplay::RenderAPI render_api, u32 resolution_scale, bool true_color, bool scaled_dithering,
|
||||
bool texture_filtering, bool uv_limits, bool supports_dual_source_blend);
|
||||
GPUTextureFilter texture_filtering, bool uv_limits, bool supports_dual_source_blend);
|
||||
~GPU_HW_ShaderGen();
|
||||
|
||||
static bool UseGLSLBindingLayout();
|
||||
|
@ -45,12 +45,13 @@ private:
|
|||
|
||||
void WriteCommonFunctions(std::stringstream& ss);
|
||||
void WriteBatchUniformBuffer(std::stringstream& ss);
|
||||
void WriteBatchTextureFilter(std::stringstream& ss, GPUTextureFilter texture_filter);
|
||||
|
||||
HostDisplay::RenderAPI m_render_api;
|
||||
u32 m_resolution_scale;
|
||||
bool m_true_color;
|
||||
bool m_scaled_dithering;
|
||||
bool m_texture_filering;
|
||||
GPUTextureFilter m_texture_filter;
|
||||
bool m_uv_limits;
|
||||
bool m_glsl;
|
||||
bool m_supports_dual_source_blend;
|
||||
|
|
|
@ -671,7 +671,7 @@ bool GPU_HW_Vulkan::CompilePipelines()
|
|||
if ((static_cast<TransparencyMode>(transparency_mode) != TransparencyMode::Disabled &&
|
||||
(static_cast<BatchRenderMode>(render_mode) != BatchRenderMode::TransparencyDisabled &&
|
||||
static_cast<BatchRenderMode>(render_mode) != BatchRenderMode::OnlyOpaque)) ||
|
||||
m_texture_filtering)
|
||||
m_texture_filtering != GPUTextureFilter::Nearest)
|
||||
{
|
||||
gpbuilder.SetBlendAttachment(
|
||||
0, true, VK_BLEND_FACTOR_ONE,
|
||||
|
|
|
@ -372,7 +372,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
si.SetBoolValue("GPU", "UseDebugDevice", false);
|
||||
si.SetBoolValue("GPU", "TrueColor", false);
|
||||
si.SetBoolValue("GPU", "ScaledDithering", true);
|
||||
si.SetBoolValue("GPU", "TextureFiltering", false);
|
||||
si.SetStringValue("GPU", "TextureFilter", Settings::GetTextureFilterName(Settings::DEFAULT_GPU_TEXTURE_FILTER));
|
||||
si.SetBoolValue("GPU", "DisableInterlacing", false);
|
||||
si.SetBoolValue("GPU", "ForceNTSCTimings", false);
|
||||
si.SetBoolValue("GPU", "WidescreenHack", false);
|
||||
|
@ -543,7 +543,7 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
g_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead ||
|
||||
g_settings.gpu_true_color != old_settings.gpu_true_color ||
|
||||
g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering ||
|
||||
g_settings.gpu_texture_filtering != old_settings.gpu_texture_filtering ||
|
||||
g_settings.gpu_texture_filter != old_settings.gpu_texture_filter ||
|
||||
g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
|
||||
g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
|
||||
g_settings.display_crop_mode != old_settings.display_crop_mode ||
|
||||
|
|
|
@ -102,7 +102,10 @@ void Settings::Load(SettingsInterface& si)
|
|||
gpu_use_debug_device = si.GetBoolValue("GPU", "UseDebugDevice", false);
|
||||
gpu_true_color = si.GetBoolValue("GPU", "TrueColor", true);
|
||||
gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", false);
|
||||
gpu_texture_filtering = si.GetBoolValue("GPU", "TextureFiltering", false);
|
||||
gpu_texture_filter =
|
||||
ParseTextureFilterName(
|
||||
si.GetStringValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER)).c_str())
|
||||
.value_or(DEFAULT_GPU_TEXTURE_FILTER);
|
||||
gpu_disable_interlacing = si.GetBoolValue("GPU", "DisableInterlacing", false);
|
||||
gpu_force_ntsc_timings = si.GetBoolValue("GPU", "ForceNTSCTimings", false);
|
||||
gpu_widescreen_hack = si.GetBoolValue("GPU", "WidescreenHack", false);
|
||||
|
@ -217,7 +220,7 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("GPU", "UseDebugDevice", gpu_use_debug_device);
|
||||
si.SetBoolValue("GPU", "TrueColor", gpu_true_color);
|
||||
si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering);
|
||||
si.SetBoolValue("GPU", "TextureFiltering", gpu_texture_filtering);
|
||||
si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter));
|
||||
si.SetBoolValue("GPU", "DisableInterlacing", gpu_disable_interlacing);
|
||||
si.SetBoolValue("GPU", "ForceNTSCTimings", gpu_force_ntsc_timings);
|
||||
si.SetBoolValue("GPU", "WidescreenHack", gpu_widescreen_hack);
|
||||
|
@ -449,6 +452,35 @@ const char* Settings::GetRendererDisplayName(GPURenderer renderer)
|
|||
return s_gpu_renderer_display_names[static_cast<int>(renderer)];
|
||||
}
|
||||
|
||||
static constexpr auto s_texture_filter_names = make_array("Nearest", "Bilinear", "JINC2", "xBRZ");
|
||||
static constexpr auto s_texture_filter_display_names =
|
||||
make_array(TRANSLATABLE("GPUTextureFilter", "Nearest-Neighbor"), TRANSLATABLE("GPUTextureFilter", "Bilinear"),
|
||||
TRANSLATABLE("GPUTextureFilter", "JINC2"), TRANSLATABLE("GPUTextureFilter", "xBRZ"));
|
||||
|
||||
std::optional<GPUTextureFilter> Settings::ParseTextureFilterName(const char* str)
|
||||
{
|
||||
int index = 0;
|
||||
for (const char* name : s_texture_filter_names)
|
||||
{
|
||||
if (StringUtil::Strcasecmp(name, str) == 0)
|
||||
return static_cast<GPUTextureFilter>(index);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* Settings::GetTextureFilterName(GPUTextureFilter filter)
|
||||
{
|
||||
return s_texture_filter_names[static_cast<int>(filter)];
|
||||
}
|
||||
|
||||
const char* Settings::GetTextureFilterDisplayName(GPUTextureFilter filter)
|
||||
{
|
||||
return s_texture_filter_display_names[static_cast<int>(filter)];
|
||||
}
|
||||
|
||||
static std::array<const char*, 3> s_display_crop_mode_names = {{"None", "Overscan", "Borders"}};
|
||||
static std::array<const char*, 3> s_display_crop_mode_display_names = {
|
||||
{TRANSLATABLE("DisplayCropMode", "None"), TRANSLATABLE("DisplayCropMode", "Only Overscan Area"),
|
||||
|
|
|
@ -88,7 +88,7 @@ struct Settings
|
|||
bool gpu_use_debug_device = false;
|
||||
bool gpu_true_color = true;
|
||||
bool gpu_scaled_dithering = false;
|
||||
bool gpu_texture_filtering = false;
|
||||
GPUTextureFilter gpu_texture_filter = GPUTextureFilter::Nearest;
|
||||
bool gpu_disable_interlacing = false;
|
||||
bool gpu_force_ntsc_timings = false;
|
||||
bool gpu_widescreen_hack = false;
|
||||
|
@ -201,6 +201,10 @@ struct Settings
|
|||
static const char* GetRendererName(GPURenderer renderer);
|
||||
static const char* GetRendererDisplayName(GPURenderer renderer);
|
||||
|
||||
static std::optional<GPUTextureFilter> ParseTextureFilterName(const char* str);
|
||||
static const char* GetTextureFilterName(GPUTextureFilter filter);
|
||||
static const char* GetTextureFilterDisplayName(GPUTextureFilter filter);
|
||||
|
||||
static std::optional<DisplayCropMode> ParseDisplayCropMode(const char* str);
|
||||
static const char* GetDisplayCropModeName(DisplayCropMode crop_mode);
|
||||
static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode);
|
||||
|
@ -227,6 +231,7 @@ struct Settings
|
|||
#else
|
||||
static constexpr GPURenderer DEFAULT_GPU_RENDERER = GPURenderer::HardwareOpenGL;
|
||||
#endif
|
||||
static constexpr GPUTextureFilter DEFAULT_GPU_TEXTURE_FILTER = GPUTextureFilter::Nearest;
|
||||
static constexpr ConsoleRegion DEFAULT_CONSOLE_REGION = ConsoleRegion::Auto;
|
||||
static constexpr CPUExecutionMode DEFAULT_CPU_EXECUTION_MODE = CPUExecutionMode::Recompiler;
|
||||
static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::Cubeb;
|
||||
|
|
|
@ -66,6 +66,15 @@ enum class GPURenderer : u8
|
|||
Count
|
||||
};
|
||||
|
||||
enum class GPUTextureFilter : u8
|
||||
{
|
||||
Nearest,
|
||||
Bilinear,
|
||||
JINC2,
|
||||
xBRZ,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class DisplayCropMode : u8
|
||||
{
|
||||
None,
|
||||
|
|
|
@ -471,12 +471,12 @@ static std::array<retro_core_option_definition, 32> s_option_definitions = {{
|
|||
"others will break.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
{"duckstation_GPU.TextureFiltering",
|
||||
"Bilinear Texture Filtering",
|
||||
{"duckstation_GPU.TextureFilter",
|
||||
"Texture Filtering",
|
||||
"Smooths out the blockyness of magnified textures on 3D object by using bilinear filtering. Will have a "
|
||||
"greater effect on higher resolution scales. Only applies to the hardware renderers.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
{{"Nearest", "Nearest-Neighbor"}, {"Bilinear", "Bilinear"}, {"JINC2", "JINC2"}, {"xBRZ", "xBRZ"}},
|
||||
"Nearest"},
|
||||
{"duckstation_GPU.WidescreenHack",
|
||||
"Widescreen Hack",
|
||||
"Increases the field of view from 4:3 to 16:9 in 3D games. For 2D games, or games which use pre-rendered "
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
#include "settingsdialog.h"
|
||||
#include "settingwidgetbinder.h"
|
||||
|
||||
EnhancementSettingsWidget::EnhancementSettingsWidget(QtHostInterface* host_interface, QWidget* parent, SettingsDialog* dialog)
|
||||
EnhancementSettingsWidget::EnhancementSettingsWidget(QtHostInterface* host_interface, QWidget* parent,
|
||||
SettingsDialog* dialog)
|
||||
: QWidget(parent), m_host_interface(host_interface)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
@ -16,8 +17,9 @@ EnhancementSettingsWidget::EnhancementSettingsWidget(QtHostInterface* host_inter
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.scaledDithering, "GPU", "ScaledDithering");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.disableInterlacing, "GPU", "DisableInterlacing");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.forceNTSCTimings, "GPU", "ForceNTSCTimings");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.linearTextureFiltering, "GPU",
|
||||
"TextureFiltering");
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(
|
||||
m_host_interface, m_ui.textureFiltering, "GPU", "TextureFilter", &Settings::ParseTextureFilterName,
|
||||
&Settings::GetTextureFilterDisplayName, Settings::DEFAULT_GPU_TEXTURE_FILTER);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.widescreenHack, "GPU", "WidescreenHack");
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pgxpEnable, "GPU", "PGXPEnable", false);
|
||||
|
@ -62,7 +64,7 @@ EnhancementSettingsWidget::EnhancementSettingsWidget(QtHostInterface* host_inter
|
|||
"approximately 17% faster. <br>For variable "
|
||||
"frame rate games, it may not affect the speed."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.linearTextureFiltering, tr("Bilinear Texture Filtering"), tr("Unchecked"),
|
||||
m_ui.textureFiltering, tr("Texture Filtering"), tr("Unchecked"),
|
||||
tr("Smooths out the blockyness of magnified textures on 3D object by using bilinear filtering. <br>Will have a "
|
||||
"greater effect on higher resolution scales. Only applies to the hardware renderers."));
|
||||
dialog->registerWidgetHelp(
|
||||
|
@ -96,6 +98,12 @@ void EnhancementSettingsWidget::updateScaledDitheringEnabled()
|
|||
void EnhancementSettingsWidget::setupAdditionalUi()
|
||||
{
|
||||
QtUtils::FillComboBoxWithResolutionScales(m_ui.resolutionScale);
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(GPUTextureFilter::Count); i++)
|
||||
{
|
||||
m_ui.textureFiltering->addItem(
|
||||
qApp->translate("GPUTextureFilter", Settings::GetTextureFilterDisplayName(static_cast<GPUTextureFilter>(i))));
|
||||
}
|
||||
}
|
||||
|
||||
void EnhancementSettingsWidget::updatePGXPSettingsEnabled()
|
||||
|
|
|
@ -42,24 +42,34 @@
|
|||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="resolutionScale"/>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Texture Filtering:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="textureFiltering"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="trueColor">
|
||||
<property name="text">
|
||||
<string>True Color Rendering (24-bit, disables dithering)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="scaledDithering">
|
||||
<property name="text">
|
||||
<string>Scaled Dithering (scale dither pattern to resolution)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="linearTextureFiltering">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="widescreenHack">
|
||||
<property name="text">
|
||||
<string>Bilinear Texture Filtering</string>
|
||||
<string>Widescreen Hack (render 3D in 16:9)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -86,13 +96,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="widescreenHack">
|
||||
<property name="text">
|
||||
<string>Widescreen Hack</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -283,9 +283,19 @@ void GamePropertiesDialog::populateGameSettings()
|
|||
m_ui.userResolutionScale->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
if (gs.gpu_texture_filter.has_value())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.userTextureFiltering);
|
||||
m_ui.userTextureFiltering->setCurrentIndex(static_cast<int>(gs.gpu_texture_filter.value()) + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
QSignalBlocker sb(m_ui.userResolutionScale);
|
||||
m_ui.userTextureFiltering->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
populateBooleanUserSetting(m_ui.userTrueColor, gs.gpu_true_color);
|
||||
populateBooleanUserSetting(m_ui.userScaledDithering, gs.gpu_scaled_dithering);
|
||||
populateBooleanUserSetting(m_ui.userBilinearTextureFiltering, gs.gpu_bilinear_texture_filtering);
|
||||
populateBooleanUserSetting(m_ui.userForceNTSCTimings, gs.gpu_force_ntsc_timings);
|
||||
populateBooleanUserSetting(m_ui.userWidescreenHack, gs.gpu_widescreen_hack);
|
||||
populateBooleanUserSetting(m_ui.userPGXP, gs.gpu_pgxp);
|
||||
|
@ -388,10 +398,17 @@ void GamePropertiesDialog::connectUi()
|
|||
saveGameSettings();
|
||||
});
|
||||
|
||||
connect(m_ui.userTextureFiltering, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
if (index <= 0)
|
||||
m_game_settings.gpu_texture_filter.reset();
|
||||
else
|
||||
m_game_settings.gpu_texture_filter = static_cast<GPUTextureFilter>(index - 1);
|
||||
saveGameSettings();
|
||||
});
|
||||
|
||||
connectBooleanUserSetting(m_ui.userTrueColor, &m_game_settings.gpu_true_color);
|
||||
connectBooleanUserSetting(m_ui.userScaledDithering, &m_game_settings.gpu_scaled_dithering);
|
||||
connectBooleanUserSetting(m_ui.userForceNTSCTimings, &m_game_settings.gpu_force_ntsc_timings);
|
||||
connectBooleanUserSetting(m_ui.userBilinearTextureFiltering, &m_game_settings.gpu_bilinear_texture_filtering);
|
||||
connectBooleanUserSetting(m_ui.userWidescreenHack, &m_game_settings.gpu_widescreen_hack);
|
||||
connectBooleanUserSetting(m_ui.userPGXP, &m_game_settings.gpu_pgxp);
|
||||
|
||||
|
|
|
@ -252,6 +252,16 @@
|
|||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="userResolutionScale"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="text">
|
||||
<string>Texture Filtering:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="userTextureFiltering" />
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
|
@ -274,7 +284,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="userWidescreenHack">
|
||||
<property name="text">
|
||||
<string>Widescreen Hack</string>
|
||||
|
@ -284,7 +294,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="userForceNTSCTimings">
|
||||
<property name="text">
|
||||
<string>Force NTSC Timings (60hz-on-PAL)</string>
|
||||
|
@ -294,17 +304,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="userBilinearTextureFiltering">
|
||||
<property name="text">
|
||||
<string>Bilinear Texture Filtering</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="userPGXP">
|
||||
<property name="text">
|
||||
<string>PGXP Geometry Correction</string>
|
||||
|
|
|
@ -935,7 +935,23 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
|
||||
settings_changed |= ImGui::MenuItem("True (24-Bit) Color", nullptr, &m_settings_copy.gpu_true_color);
|
||||
settings_changed |= ImGui::MenuItem("Scaled Dithering", nullptr, &m_settings_copy.gpu_scaled_dithering);
|
||||
settings_changed |= ImGui::MenuItem("Texture Filtering", nullptr, &m_settings_copy.gpu_texture_filtering);
|
||||
|
||||
if (ImGui::BeginMenu("Texture Filtering"))
|
||||
{
|
||||
const GPUTextureFilter current = m_settings_copy.gpu_texture_filter;
|
||||
for (u32 i = 0; i < static_cast<u32>(GPUTextureFilter::Count); i++)
|
||||
{
|
||||
if (ImGui::MenuItem(Settings::GetTextureFilterDisplayName(static_cast<GPUTextureFilter>(i)), nullptr,
|
||||
i == static_cast<u32>(current)))
|
||||
{
|
||||
m_settings_copy.gpu_texture_filter = static_cast<GPUTextureFilter>(i);
|
||||
settings_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
settings_changed |= ImGui::MenuItem("Disable Interlacing", nullptr, &m_settings_copy.gpu_disable_interlacing);
|
||||
settings_changed |= ImGui::MenuItem("Widescreen Hack", nullptr, &m_settings_copy.gpu_widescreen_hack);
|
||||
settings_changed |= ImGui::MenuItem("Display Linear Filtering", nullptr, &m_settings_copy.display_linear_filtering);
|
||||
|
@ -1401,8 +1417,22 @@ void SDLHostInterface::DrawSettingsWindow()
|
|||
settings_changed = true;
|
||||
}
|
||||
|
||||
ImGui::Text("Texture Filtering:");
|
||||
ImGui::SameLine(indent);
|
||||
int gpu_texture_filter = static_cast<int>(m_settings_copy.gpu_texture_filter);
|
||||
if (ImGui::Combo(
|
||||
"##gpu_texture_filter", &gpu_texture_filter,
|
||||
[](void*, int index, const char** out_text) {
|
||||
*out_text = Settings::GetTextureFilterDisplayName(static_cast<GPUTextureFilter>(index));
|
||||
return true;
|
||||
},
|
||||
nullptr, static_cast<int>(GPUTextureFilter::Count)))
|
||||
{
|
||||
m_settings_copy.gpu_texture_filter = static_cast<GPUTextureFilter>(gpu_texture_filter);
|
||||
settings_changed = true;
|
||||
}
|
||||
|
||||
settings_changed |= ImGui::Checkbox("True 24-bit Color (disables dithering)", &m_settings_copy.gpu_true_color);
|
||||
settings_changed |= ImGui::Checkbox("Texture Filtering", &m_settings_copy.gpu_texture_filtering);
|
||||
settings_changed |= ImGui::Checkbox("Disable Interlacing", &m_settings_copy.gpu_disable_interlacing);
|
||||
settings_changed |= ImGui::Checkbox("Force NTSC Timings", &m_settings_copy.gpu_force_ntsc_timings);
|
||||
settings_changed |= ImGui::Checkbox("Widescreen Hack", &m_settings_copy.gpu_widescreen_hack);
|
||||
|
|
|
@ -116,7 +116,7 @@ bool Entry::LoadFromStream(ByteStream* stream)
|
|||
!ReadOptionalFromStream(stream, &gpu_resolution_scale) || !ReadOptionalFromStream(stream, &gpu_true_color) ||
|
||||
!ReadOptionalFromStream(stream, &gpu_scaled_dithering) ||
|
||||
!ReadOptionalFromStream(stream, &gpu_force_ntsc_timings) ||
|
||||
!ReadOptionalFromStream(stream, &gpu_bilinear_texture_filtering) ||
|
||||
!ReadOptionalFromStream(stream, &gpu_texture_filter) ||
|
||||
!ReadOptionalFromStream(stream, &gpu_widescreen_hack) || !ReadOptionalFromStream(stream, &gpu_pgxp) ||
|
||||
!ReadOptionalFromStream(stream, &controller_1_type) || !ReadOptionalFromStream(stream, &controller_2_type) ||
|
||||
!ReadOptionalFromStream(stream, &memory_card_1_type) || !ReadOptionalFromStream(stream, &memory_card_2_type) ||
|
||||
|
@ -154,7 +154,7 @@ bool Entry::SaveToStream(ByteStream* stream) const
|
|||
WriteOptionalToStream(stream, display_aspect_ratio) && WriteOptionalToStream(stream, gpu_resolution_scale) &&
|
||||
WriteOptionalToStream(stream, gpu_true_color) && WriteOptionalToStream(stream, gpu_scaled_dithering) &&
|
||||
WriteOptionalToStream(stream, gpu_force_ntsc_timings) &&
|
||||
WriteOptionalToStream(stream, gpu_bilinear_texture_filtering) &&
|
||||
WriteOptionalToStream(stream, gpu_texture_filter) &&
|
||||
WriteOptionalToStream(stream, gpu_widescreen_hack) && WriteOptionalToStream(stream, gpu_pgxp) &&
|
||||
WriteOptionalToStream(stream, controller_1_type) && WriteOptionalToStream(stream, controller_2_type) &&
|
||||
WriteOptionalToStream(stream, memory_card_1_type) && WriteOptionalToStream(stream, memory_card_2_type) &&
|
||||
|
@ -201,7 +201,7 @@ static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA
|
|||
entry->gpu_scaled_dithering = StringUtil::FromChars<bool>(cvalue);
|
||||
cvalue = ini.GetValue(section, "GPUBilinearTextureFiltering", nullptr);
|
||||
if (cvalue)
|
||||
entry->gpu_bilinear_texture_filtering = StringUtil::FromChars<bool>(cvalue);
|
||||
entry->gpu_texture_filter = Settings::ParseTextureFilterName(cvalue);
|
||||
cvalue = ini.GetValue(section, "GPUForceNTSCTimings", nullptr);
|
||||
if (cvalue)
|
||||
entry->gpu_force_ntsc_timings = StringUtil::FromChars<bool>(cvalue);
|
||||
|
@ -265,11 +265,8 @@ static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA
|
|||
ini.SetValue(section, "GPUTrueColor", entry.gpu_true_color.value() ? "true" : "false");
|
||||
if (entry.gpu_scaled_dithering.has_value())
|
||||
ini.SetValue(section, "GPUScaledDithering", entry.gpu_scaled_dithering.value() ? "true" : "false");
|
||||
if (entry.gpu_bilinear_texture_filtering.has_value())
|
||||
{
|
||||
ini.SetValue(section, "GPUBilinearTextureFiltering",
|
||||
entry.gpu_bilinear_texture_filtering.value() ? "true" : "false");
|
||||
}
|
||||
if (entry.gpu_texture_filter.has_value())
|
||||
ini.SetValue(section, "GPUTextureFilter", Settings::GetTextureFilterName(entry.gpu_texture_filter.value()));
|
||||
if (entry.gpu_force_ntsc_timings.has_value())
|
||||
ini.SetValue(section, "GPUForceNTSCTimings", entry.gpu_force_ntsc_timings.value() ? "true" : "false");
|
||||
if (entry.gpu_widescreen_hack.has_value())
|
||||
|
@ -417,8 +414,8 @@ void Entry::ApplySettings(bool display_osd_messages) const
|
|||
g_settings.gpu_scaled_dithering = gpu_scaled_dithering.value();
|
||||
if (gpu_force_ntsc_timings.has_value())
|
||||
g_settings.gpu_force_ntsc_timings = gpu_force_ntsc_timings.value();
|
||||
if (gpu_bilinear_texture_filtering)
|
||||
g_settings.gpu_texture_filtering = gpu_bilinear_texture_filtering.value();
|
||||
if (gpu_texture_filter.has_value())
|
||||
g_settings.gpu_texture_filter = gpu_texture_filter.value();
|
||||
if (gpu_widescreen_hack.has_value())
|
||||
g_settings.gpu_widescreen_hack = gpu_widescreen_hack.value();
|
||||
if (gpu_pgxp.has_value())
|
||||
|
|
|
@ -47,7 +47,7 @@ struct Entry
|
|||
std::optional<bool> gpu_true_color;
|
||||
std::optional<bool> gpu_scaled_dithering;
|
||||
std::optional<bool> gpu_force_ntsc_timings;
|
||||
std::optional<bool> gpu_bilinear_texture_filtering;
|
||||
std::optional<GPUTextureFilter> gpu_texture_filter;
|
||||
std::optional<bool> gpu_widescreen_hack;
|
||||
std::optional<bool> gpu_pgxp;
|
||||
std::optional<ControllerType> controller_1_type;
|
||||
|
|
Loading…
Reference in a new issue