GPU/HW: Add separate 3D/2D (sprite) texture filtering

This commit is contained in:
Stenzek 2024-06-17 15:49:55 +10:00
parent be4abb016f
commit 34f20798a1
No known key found for this signature in database
12 changed files with 226 additions and 77 deletions

View file

@ -479,7 +479,8 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes
if (HasTrait(Trait::DisableTextureFiltering))
{
if (display_osd_messages && settings.gpu_texture_filter != GPUTextureFilter::Nearest)
if (display_osd_messages && (settings.gpu_texture_filter != GPUTextureFilter::Nearest ||
g_settings.gpu_sprite_texture_filter != GPUTextureFilter::Nearest))
{
Host::AddIconOSDMessage("gamedb_disable_upscaling", ICON_FA_MAGIC,
TRANSLATE_STR("OSDMessage", "Texture filtering disabled by compatibility settings."),
@ -487,6 +488,7 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes
}
settings.gpu_texture_filter = GPUTextureFilter::Nearest;
settings.gpu_sprite_texture_filter = GPUTextureFilter::Nearest;
}
if (HasTrait(Trait::DisableScaledDithering))

View file

@ -59,10 +59,17 @@ ALWAYS_INLINE_RELEASE static u32 GetBoxDownsampleScale(u32 resolution_scale)
return scale;
}
ALWAYS_INLINE static bool ShouldClampUVs()
ALWAYS_INLINE static bool ShouldClampUVs(GPUTextureFilter texture_filter)
{
// We only need UV limits if PGXP is enabled, or texture filtering is enabled.
return g_settings.gpu_pgxp_enable || g_settings.gpu_texture_filter != GPUTextureFilter::Nearest;
return g_settings.gpu_pgxp_enable || texture_filter != GPUTextureFilter::Nearest;
}
ALWAYS_INLINE static bool ShouldAllowSpriteMode(u8 resolution_scale, GPUTextureFilter texture_filter,
GPUTextureFilter sprite_texture_filter)
{
// Use sprite shaders/mode when texcoord rounding is forced, or if the filters are different.
return (sprite_texture_filter != texture_filter || (resolution_scale > 1 && g_settings.gpu_force_round_texcoords));
}
ALWAYS_INLINE static bool ShouldDisableColorPerspective()
@ -73,7 +80,16 @@ ALWAYS_INLINE static bool ShouldDisableColorPerspective()
/// Returns true if the specified texture filtering mode requires dual-source blending.
ALWAYS_INLINE static bool IsBlendedTextureFiltering(GPUTextureFilter filter)
{
return (filter == GPUTextureFilter::Bilinear || filter == GPUTextureFilter::JINC2 || filter == GPUTextureFilter::xBR);
// return (filter == GPUTextureFilter::Bilinear || filter == GPUTextureFilter::JINC2 || filter ==
// GPUTextureFilter::xBR);
static_assert(((static_cast<u8>(GPUTextureFilter::Nearest) & 1u) == 0u) &&
((static_cast<u8>(GPUTextureFilter::Bilinear) & 1u) == 1u) &&
((static_cast<u8>(GPUTextureFilter::BilinearBinAlpha) & 1u) == 0u) &&
((static_cast<u8>(GPUTextureFilter::JINC2) & 1u) == 1u) &&
((static_cast<u8>(GPUTextureFilter::JINC2BinAlpha) & 1u) == 0u) &&
((static_cast<u8>(GPUTextureFilter::xBR) & 1u) == 1u) &&
((static_cast<u8>(GPUTextureFilter::xBRBinAlpha) & 1u) == 0u));
return ((static_cast<u8>(filter) & 1u) == 1u);
}
/// Computes the area affected by a VRAM transfer, including wrap-around of X.
@ -193,17 +209,18 @@ bool GPU_HW::Initialize()
m_resolution_scale = Truncate8(CalculateResolutionScale());
m_multisamples = Truncate8(std::min<u32>(g_settings.gpu_multisamples, g_gpu_device->GetMaxMultisamples()));
m_supports_dual_source_blend = features.dual_source_blend;
m_supports_framebuffer_fetch = features.framebuffer_fetch;
m_true_color = g_settings.gpu_true_color;
m_texture_filtering = g_settings.gpu_texture_filter;
m_sprite_texture_filtering = g_settings.gpu_sprite_texture_filter;
m_line_detect_mode = (m_resolution_scale > 1) ? g_settings.gpu_line_detect_mode : GPULineDetectMode::Disabled;
m_clamp_uvs = ShouldClampUVs();
m_compute_uv_range = m_clamp_uvs;
m_downsample_mode = GetDownsampleMode(m_resolution_scale);
m_wireframe_mode = g_settings.gpu_wireframe_mode;
m_supports_dual_source_blend = features.dual_source_blend;
m_supports_framebuffer_fetch = features.framebuffer_fetch;
m_true_color = g_settings.gpu_true_color;
m_pgxp_depth_buffer = g_settings.UsingPGXPDepthBuffer();
m_clamp_uvs = ShouldClampUVs(m_texture_filtering) || ShouldClampUVs(m_sprite_texture_filtering);
m_compute_uv_range = m_clamp_uvs;
m_allow_sprite_mode = ShouldAllowSpriteMode(m_resolution_scale, m_texture_filtering, m_sprite_texture_filtering);
CheckSettings();
@ -317,7 +334,7 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
const u8 resolution_scale = Truncate8(CalculateResolutionScale());
const u8 multisamples = Truncate8(std::min<u32>(g_settings.gpu_multisamples, g_gpu_device->GetMaxMultisamples()));
const bool clamp_uvs = ShouldClampUVs();
const bool clamp_uvs = ShouldClampUVs(m_texture_filtering) || ShouldClampUVs(m_sprite_texture_filtering);
const bool framebuffer_changed =
(m_resolution_scale != resolution_scale || m_multisamples != multisamples ||
(static_cast<bool>(m_vram_depth_texture) != (g_settings.UsingPGXPDepthBuffer() || !m_supports_framebuffer_fetch)));
@ -328,14 +345,17 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
(resolution_scale > 1 && g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering) ||
(resolution_scale > 1 && g_settings.gpu_texture_filter == GPUTextureFilter::Nearest &&
g_settings.gpu_force_round_texcoords != old_settings.gpu_force_round_texcoords) ||
m_texture_filtering != g_settings.gpu_texture_filter || m_clamp_uvs != clamp_uvs ||
m_texture_filtering != g_settings.gpu_texture_filter ||
m_sprite_texture_filtering != g_settings.gpu_sprite_texture_filter || m_clamp_uvs != clamp_uvs ||
(resolution_scale > 1 && (g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode ||
(m_downsample_mode == GPUDownsampleMode::Box &&
g_settings.gpu_downsample_scale != old_settings.gpu_downsample_scale))) ||
(features.geometry_shaders && g_settings.gpu_wireframe_mode != old_settings.gpu_wireframe_mode) ||
m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer() ||
(features.noperspective_interpolation &&
ShouldDisableColorPerspective() != old_settings.gpu_pgxp_color_correction));
ShouldDisableColorPerspective() != old_settings.gpu_pgxp_color_correction) ||
m_allow_sprite_mode !=
ShouldAllowSpriteMode(m_resolution_scale, g_settings.gpu_texture_filter, g_settings.gpu_sprite_texture_filter));
if (m_resolution_scale != resolution_scale)
{
@ -376,13 +396,15 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
m_resolution_scale = resolution_scale;
m_multisamples = multisamples;
m_true_color = g_settings.gpu_true_color;
m_texture_filtering = g_settings.gpu_texture_filter;
m_sprite_texture_filtering = g_settings.gpu_sprite_texture_filter;
m_line_detect_mode = (m_resolution_scale > 1) ? g_settings.gpu_line_detect_mode : GPULineDetectMode::Disabled;
m_clamp_uvs = clamp_uvs;
m_compute_uv_range = m_clamp_uvs;
m_downsample_mode = GetDownsampleMode(resolution_scale);
m_wireframe_mode = g_settings.gpu_wireframe_mode;
m_true_color = g_settings.gpu_true_color;
m_clamp_uvs = clamp_uvs;
m_compute_uv_range = m_clamp_uvs;
m_allow_sprite_mode = ShouldAllowSpriteMode(resolution_scale, m_texture_filtering, m_sprite_texture_filtering);
CheckSettings();
@ -454,13 +476,17 @@ void GPU_HW::CheckSettings()
TRANSLATE_STR("GPU_HW", "SSAA is not supported, using MSAA instead."),
Host::OSD_ERROR_DURATION);
}
if (!features.dual_source_blend && !features.framebuffer_fetch && IsBlendedTextureFiltering(m_texture_filtering))
if (!features.dual_source_blend && !features.framebuffer_fetch &&
(IsBlendedTextureFiltering(m_texture_filtering) || IsBlendedTextureFiltering(m_sprite_texture_filtering)))
{
Host::AddIconOSDMessage(
"TextureFilterUnsupported", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("GPU_HW", "Texture filter '{}' is not supported with the current renderer."),
Settings::GetTextureFilterDisplayName(m_texture_filtering), Host::OSD_ERROR_DURATION));
fmt::format(TRANSLATE_FS("GPU_HW", "Texture filter '{}/{}' is not supported with the current renderer."),
Settings::GetTextureFilterDisplayName(m_texture_filtering),
Settings::GetTextureFilterName(m_sprite_texture_filtering), Host::OSD_ERROR_DURATION));
m_texture_filtering = GPUTextureFilter::Nearest;
m_sprite_texture_filtering = GPUTextureFilter::Nearest;
m_allow_sprite_mode = ShouldAllowSpriteMode(m_resolution_scale, m_texture_filtering, m_sprite_texture_filtering);
}
if (!features.noperspective_interpolation && !ShouldDisableColorPerspective())
@ -650,7 +676,8 @@ void GPU_HW::PrintSettingsToLog()
((m_true_color && g_settings.gpu_debanding) ? " (Debanding)" : ""));
INFO_LOG("Force round texture coordinates: {}",
(m_resolution_scale > 1 && g_settings.gpu_force_round_texcoords) ? "Enabled" : "Disabled");
INFO_LOG("Texture Filtering: {}", Settings::GetTextureFilterDisplayName(m_texture_filtering));
INFO_LOG("Texture Filtering: {}/{}", Settings::GetTextureFilterDisplayName(m_texture_filtering),
Settings::GetTextureFilterDisplayName(m_sprite_texture_filtering));
INFO_LOG("Dual-source blending: {}", m_supports_dual_source_blend ? "Supported" : "Not supported");
INFO_LOG("Clamping UVs: {}", m_clamp_uvs ? "YES" : "NO");
INFO_LOG("Depth buffer: {}", m_pgxp_depth_buffer ? "YES" : "NO");
@ -658,6 +685,7 @@ void GPU_HW::PrintSettingsToLog()
INFO_LOG("Wireframe rendering: {}", Settings::GetGPUWireframeModeDisplayName(m_wireframe_mode));
INFO_LOG("Line detection: {}", Settings::GetLineDetectModeDisplayName(m_line_detect_mode));
INFO_LOG("Using software renderer for readbacks: {}", m_sw_renderer ? "YES" : "NO");
INFO_LOG("Separate sprite shaders: {}", m_allow_sprite_mode ? "YES" : "NO");
}
bool GPU_HW::NeedsDepthBuffer() const
@ -776,19 +804,23 @@ bool GPU_HW::CompilePipelines()
{
const GPUDevice::Features features = g_gpu_device->GetFeatures();
const bool per_sample_shading = g_settings.gpu_per_sample_shading && features.per_sample_shading;
const bool force_round_texcoords = (m_resolution_scale > 1 && g_settings.gpu_force_round_texcoords);
const bool force_round_texcoords = (m_resolution_scale > 1 && m_texture_filtering == GPUTextureFilter::Nearest &&
g_settings.gpu_force_round_texcoords);
const bool needs_depth_buffer = NeedsDepthBuffer();
const bool write_mask_as_depth = (!m_pgxp_depth_buffer && needs_depth_buffer);
m_allow_shader_blend = (features.feedback_loops && (m_pgxp_depth_buffer || !needs_depth_buffer));
GPU_HW_ShaderGen shadergen(g_gpu_device->GetRenderAPI(), m_resolution_scale, m_multisamples, per_sample_shading,
m_true_color, (m_resolution_scale > 1 && g_settings.gpu_scaled_dithering), m_clamp_uvs,
m_true_color, (m_resolution_scale > 1 && g_settings.gpu_scaled_dithering),
write_mask_as_depth, ShouldDisableColorPerspective(), m_supports_dual_source_blend,
m_supports_framebuffer_fetch, g_settings.gpu_true_color && g_settings.gpu_debanding);
constexpr u32 active_texture_modes = 4;
const u32 active_texture_modes =
m_allow_sprite_mode ? NUM_TEXTURE_MODES :
(NUM_TEXTURE_MODES - (NUM_TEXTURE_MODES - static_cast<u32>(BatchTextureMode::SpriteStart)));
const u32 active_vertex_shaders = m_allow_sprite_mode ? 3 : 2;
const u32 total_pipelines =
2 + // vertex shaders
active_vertex_shaders + // vertex shaders
(active_texture_modes * 5 * 9 * 2 * 2 * 2) + // fragment shaders
((m_pgxp_depth_buffer ? 2 : 1) * 5 * 5 * active_texture_modes * 2 * 2 * 2) + // batch pipelines
((m_wireframe_mode != GPUWireframeMode::Disabled) ? 1 : 0) + // wireframe
@ -804,19 +836,22 @@ bool GPU_HW::CompilePipelines()
ShaderCompileProgressTracker progress("Compiling Pipelines", total_pipelines);
// vertex shaders - [textured]
// vertex shaders - [non-textured/textured/sprite]
// fragment shaders - [render_mode][transparency_mode][texture_mode][check_mask][dithering][interlacing]
static constexpr auto destroy_shader = [](std::unique_ptr<GPUShader>& s) { s.reset(); };
DimensionalArray<std::unique_ptr<GPUShader>, 2> batch_vertex_shaders{};
DimensionalArray<std::unique_ptr<GPUShader>, 3> batch_vertex_shaders{};
DimensionalArray<std::unique_ptr<GPUShader>, 2, 2, 2, NUM_TEXTURE_MODES, 5, 5> batch_fragment_shaders{};
ScopedGuard batch_shader_guard([&batch_vertex_shaders, &batch_fragment_shaders]() {
batch_vertex_shaders.enumerate(destroy_shader);
batch_fragment_shaders.enumerate(destroy_shader);
});
for (u8 textured = 0; textured < 2; textured++)
for (u8 textured = 0; textured < active_vertex_shaders; textured++)
{
const std::string vs = shadergen.GenerateBatchVertexShader(ConvertToBoolUnchecked(textured), m_pgxp_depth_buffer);
const bool sprite = (textured > 1);
const bool uv_limits = ShouldClampUVs(sprite ? m_sprite_texture_filtering : m_texture_filtering);
const std::string vs = shadergen.GenerateBatchVertexShader(textured != 0, uv_limits,
!sprite && force_round_texcoords, m_pgxp_depth_buffer);
if (!(batch_vertex_shaders[textured] =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(), vs)))
{
@ -858,11 +893,15 @@ bool GPU_HW::CompilePipelines()
{
for (u8 interlacing = 0; interlacing < 2; interlacing++)
{
const bool sprite = (static_cast<BatchTextureMode>(texture_mode) >= BatchTextureMode::SpriteStart);
const bool uv_limits = ShouldClampUVs(sprite ? m_sprite_texture_filtering : m_texture_filtering);
const BatchTextureMode shader_texmode = static_cast<BatchTextureMode>(
texture_mode - (sprite ? static_cast<u8>(BatchTextureMode::SpriteStart) : 0));
const std::string fs = shadergen.GenerateBatchFragmentShader(
static_cast<BatchRenderMode>(render_mode), static_cast<GPUTransparencyMode>(transparency_mode),
static_cast<BatchTextureMode>(texture_mode), m_texture_filtering, force_round_texcoords,
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing),
ConvertToBoolUnchecked(check_mask));
shader_texmode, sprite ? m_sprite_texture_filtering : m_texture_filtering, uv_limits,
!sprite && force_round_texcoords, ConvertToBoolUnchecked(dithering),
ConvertToBoolUnchecked(interlacing), ConvertToBoolUnchecked(check_mask));
if (!(batch_fragment_shaders[render_mode][transparency_mode][texture_mode][check_mask][dithering]
[interlacing] = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
@ -940,6 +979,8 @@ bool GPU_HW::CompilePipelines()
for (u8 check_mask = 0; check_mask < 2; check_mask++)
{
const bool textured = (static_cast<BatchTextureMode>(texture_mode) != BatchTextureMode::Disabled);
const bool sprite = (static_cast<BatchTextureMode>(texture_mode) >= BatchTextureMode::SpriteStart);
const bool uv_limits = ShouldClampUVs(sprite ? m_sprite_texture_filtering : m_texture_filtering);
const bool use_shader_blending =
(render_mode == static_cast<u8>(BatchRenderMode::ShaderBlend) &&
((textured &&
@ -948,13 +989,13 @@ bool GPU_HW::CompilePipelines()
plconfig.input_layout.vertex_attributes =
textured ?
(m_clamp_uvs ? std::span<const GPUPipeline::VertexAttribute>(
vertex_attributes, NUM_BATCH_TEXTURED_LIMITS_VERTEX_ATTRIBUTES) :
std::span<const GPUPipeline::VertexAttribute>(
vertex_attributes, NUM_BATCH_TEXTURED_VERTEX_ATTRIBUTES)) :
(uv_limits ? std::span<const GPUPipeline::VertexAttribute>(
vertex_attributes, NUM_BATCH_TEXTURED_LIMITS_VERTEX_ATTRIBUTES) :
std::span<const GPUPipeline::VertexAttribute>(vertex_attributes,
NUM_BATCH_TEXTURED_VERTEX_ATTRIBUTES)) :
std::span<const GPUPipeline::VertexAttribute>(vertex_attributes, NUM_BATCH_VERTEX_ATTRIBUTES);
plconfig.vertex_shader = batch_vertex_shaders[BoolToUInt8(textured)].get();
plconfig.vertex_shader = batch_vertex_shaders[BoolToUInt8(textured) + BoolToUInt8(sprite)].get();
plconfig.fragment_shader =
batch_fragment_shaders[render_mode]
[use_shader_blending ? transparency_mode :
@ -982,7 +1023,8 @@ bool GPU_HW::CompilePipelines()
((static_cast<GPUTransparencyMode>(transparency_mode) != GPUTransparencyMode::Disabled &&
(static_cast<BatchRenderMode>(render_mode) != BatchRenderMode::TransparencyDisabled &&
static_cast<BatchRenderMode>(render_mode) != BatchRenderMode::OnlyOpaque)) ||
(textured && IsBlendedTextureFiltering(m_texture_filtering))))
(textured &&
IsBlendedTextureFiltering(sprite ? m_sprite_texture_filtering : m_texture_filtering))))
{
plconfig.blend.enable = true;
plconfig.blend.src_alpha_blend = GPUPipeline::BlendFunc::One;
@ -1512,12 +1554,15 @@ ALWAYS_INLINE_RELEASE void GPU_HW::DrawBatchVertices(BatchRenderMode render_mode
u32 base_vertex)
{
// [depth_test][transparency_mode][render_mode][texture_mode][dithering][interlacing][check_mask]
const u8 texture_mode = static_cast<u8>(m_batch.texture_mode) +
((m_batch.texture_mode != BatchTextureMode::Disabled && m_batch.sprite_mode) ?
static_cast<u8>(BatchTextureMode::SpriteStart) :
0);
const u8 depth_test = BoolToUInt8(m_batch.use_depth_buffer);
const u8 check_mask = BoolToUInt8(m_batch.check_mask_before_draw);
g_gpu_device->SetPipeline(m_batch_pipelines[depth_test][static_cast<u8>(m_batch.transparency_mode)][static_cast<u8>(
render_mode)][static_cast<u8>(m_batch.texture_mode)][BoolToUInt8(m_batch.dithering)]
[BoolToUInt8(m_batch.interlacing)][check_mask]
.get());
render_mode)][texture_mode][BoolToUInt8(m_batch.dithering)][BoolToUInt8(m_batch.interlacing)][check_mask]
.get());
if (render_mode != BatchRenderMode::ShaderBlend || m_supports_framebuffer_fetch)
g_gpu_device->DrawIndexed(num_indices, base_index, base_vertex);
@ -1628,6 +1673,36 @@ ALWAYS_INLINE_RELEASE void GPU_HW::HandleFlippedQuadTextureCoordinates(BatchVert
vertices[2].v++;
vertices[3].v++;
}
// 2D polygons should have zero change in V on the X axis, and vice versa.
if (m_allow_sprite_mode)
SetBatchSpriteMode(zero_dudy && zero_dvdx);
}
bool GPU_HW::IsPossibleSpritePolygon(const BatchVertex* vertices) const
{
const float abx = vertices[1].x - vertices[0].x;
const float aby = vertices[1].y - vertices[0].y;
const float bcx = vertices[2].x - vertices[1].x;
const float bcy = vertices[2].y - vertices[1].y;
const float cax = vertices[0].x - vertices[2].x;
const float cay = vertices[0].y - vertices[2].y;
const float dvdx = -aby * static_cast<float>(vertices[2].v) - bcy * static_cast<float>(vertices[0].v) -
cay * static_cast<float>(vertices[1].v);
const float dudy = +abx * static_cast<float>(vertices[2].u) + bcx * static_cast<float>(vertices[0].u) +
cax * static_cast<float>(vertices[1].u);
const float area = bcx * cay - bcy * cax;
const s32 texArea = (vertices[1].u - vertices[0].u) * (vertices[2].v - vertices[0].v) -
(vertices[2].u - vertices[0].u) * (vertices[1].v - vertices[0].v);
// Doesn't matter.
if (area == 0.0f || texArea == 0)
return m_batch.sprite_mode;
const float rcp_area = 1.0f / area;
const bool zero_dudy = ((dudy * rcp_area) == 0.0f);
const bool zero_dvdx = ((dvdx * rcp_area) == 0.0f);
return (zero_dudy && zero_dvdx);
}
ALWAYS_INLINE_RELEASE void GPU_HW::ExpandLineTriangles(BatchVertex* vertices, u32 base_vertex)
@ -1837,6 +1912,22 @@ void GPU_HW::CheckForDepthClear(const BatchVertex* vertices, u32 num_vertices)
m_last_depth_z = average_z;
}
void GPU_HW::SetBatchSpriteMode(bool enabled)
{
if (m_batch.sprite_mode == enabled)
return;
if (m_batch_index_count > 0)
{
FlushRender();
EnsureVertexBufferSpaceForCurrentCommand();
}
GL_INS_FMT("Sprite mode is now {}", enabled ? "ON" : "OFF");
m_batch.sprite_mode = enabled;
}
void GPU_HW::DrawLine(float x0, float y0, u32 col0, float x1, float y1, u32 col1, float depth)
{
DebugAssert(m_batch_vertex_space >= 4 && m_batch_index_space >= 6);
@ -1997,6 +2088,8 @@ void GPU_HW::LoadVertices()
const bool is_3d = (vertices[0].w != vertices[1].w || vertices[0].w != vertices[2].w);
if (m_resolution_scale > 1 && !is_3d && rc.quad_polygon)
HandleFlippedQuadTextureCoordinates(vertices.data());
else if (m_allow_sprite_mode)
SetBatchSpriteMode((pgxp && !is_3d) || IsPossibleSpritePolygon(vertices.data()));
if (m_compute_uv_range && textured)
ComputePolygonUVLimits(texpage, vertices.data(), num_vertices);
@ -2160,6 +2253,7 @@ void GPU_HW::LoadVertices()
// we can split the rectangle up into potentially 8 quads
SetBatchDepthBuffer(false);
SetBatchSpriteMode(m_allow_sprite_mode);
DebugAssert(m_batch_vertex_space >= MAX_VERTICES_FOR_RECTANGLE &&
m_batch_index_space >= MAX_VERTICES_FOR_RECTANGLE);
@ -2499,7 +2593,8 @@ ALWAYS_INLINE_RELEASE bool GPU_HW::NeedsShaderBlending(GPUTransparencyMode trans
((check_mask && (m_pgxp_depth_buffer || !m_vram_depth_texture)) ||
transparency == GPUTransparencyMode::BackgroundMinusForeground ||
(!m_supports_dual_source_blend &&
(transparency != GPUTransparencyMode::Disabled || IsBlendedTextureFiltering(m_texture_filtering)))));
(transparency != GPUTransparencyMode::Disabled || IsBlendedTextureFiltering(m_texture_filtering) ||
IsBlendedTextureFiltering(m_sprite_texture_filtering)))));
}
void GPU_HW::EnsureVertexBufferSpace(u32 required_vertices, u32 required_indices)

View file

@ -40,7 +40,13 @@ public:
Direct16Bit,
Disabled,
SpritePalette4Bit,
SpritePalette8Bit,
SpriteDirect16Bit,
MaxCount,
SpriteStart = SpritePalette4Bit,
};
static_assert(static_cast<u8>(BatchTextureMode::Palette4Bit) == static_cast<u8>(GPUTextureMode::Palette4Bit) &&
static_cast<u8>(BatchTextureMode::Palette8Bit) == static_cast<u8>(GPUTextureMode::Palette8Bit) &&
@ -108,6 +114,7 @@ private:
bool set_mask_while_drawing = false;
bool check_mask_before_draw = false;
bool use_depth_buffer = false;
bool sprite_mode = false;
// Returns the render mode for this batch.
BatchRenderMode GetRenderMode() const;
@ -201,6 +208,7 @@ private:
/// Handles quads with flipped texture coordinate directions.
void HandleFlippedQuadTextureCoordinates(BatchVertex* vertices);
bool IsPossibleSpritePolygon(const BatchVertex* vertices) const;
void ExpandLineTriangles(BatchVertex* vertices, u32 base_vertex);
/// Computes polygon U/V boundaries.
@ -209,6 +217,7 @@ private:
/// Sets the depth test flag for PGXP depth buffering.
void SetBatchDepthBuffer(bool enabled);
void CheckForDepthClear(const BatchVertex* vertices, u32 num_vertices);
void SetBatchSpriteMode(bool enabled);
void UpdateDownsamplingLevels();
void DownsampleFramebuffer(GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
@ -242,6 +251,7 @@ private:
u8 m_multisamples = 1;
GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest;
GPUTextureFilter m_sprite_texture_filtering = GPUTextureFilter::Nearest;
GPULineDetectMode m_line_detect_mode = GPULineDetectMode::Disabled;
GPUDownsampleMode m_downsample_mode = GPUDownsampleMode::Disabled;
GPUWireframeMode m_wireframe_mode = GPUWireframeMode::Disabled;
@ -252,6 +262,7 @@ private:
bool m_pgxp_depth_buffer : 1 = false;
bool m_clamp_uvs : 1 = false;
bool m_compute_uv_range : 1 = false;
bool m_allow_sprite_mode : 1 = false;
bool m_allow_shader_blend : 1 = false;
u8 m_texpage_dirty = 0;

View file

@ -6,14 +6,13 @@
#include <cstdio>
GPU_HW_ShaderGen::GPU_HW_ShaderGen(RenderAPI render_api, u32 resolution_scale, u32 multisamples,
bool per_sample_shading, bool true_color, bool scaled_dithering, bool uv_limits,
bool per_sample_shading, bool true_color, bool scaled_dithering,
bool write_mask_as_depth, bool disable_color_perspective,
bool supports_dual_source_blend, bool supports_framebuffer_fetch, bool debanding)
: ShaderGen(render_api, GetShaderLanguageForAPI(render_api), supports_dual_source_blend, supports_framebuffer_fetch),
m_resolution_scale(resolution_scale), m_multisamples(multisamples), m_per_sample_shading(per_sample_shading),
m_true_color(true_color), m_scaled_dithering(scaled_dithering), m_uv_limits(uv_limits),
m_write_mask_as_depth(write_mask_as_depth), m_disable_color_perspective(disable_color_perspective),
m_debanding(debanding)
m_true_color(true_color), m_scaled_dithering(scaled_dithering), m_write_mask_as_depth(write_mask_as_depth),
m_disable_color_perspective(disable_color_perspective), m_debanding(debanding)
{
}
@ -58,12 +57,14 @@ void GPU_HW_ShaderGen::WriteBatchUniformBuffer(std::stringstream& ss)
false);
}
std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(bool textured, bool pgxp_depth)
std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(bool textured, bool uv_limits, bool force_round_texcoords,
bool pgxp_depth)
{
std::stringstream ss;
WriteHeader(ss);
DefineMacro(ss, "TEXTURED", textured);
DefineMacro(ss, "UV_LIMITS", m_uv_limits);
DefineMacro(ss, "UV_LIMITS", uv_limits);
DefineMacro(ss, "FORCE_ROUND_TEXCOORDS", force_round_texcoords);
DefineMacro(ss, "PGXP_DEPTH", pgxp_depth);
WriteCommonFunctions(ss);
@ -71,7 +72,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(bool textured, bool pgxp
if (textured)
{
if (m_uv_limits)
if (uv_limits)
{
DeclareVertexEntryPoint(
ss, {"float4 a_pos", "float4 a_col0", "uint a_texcoord", "uint a_texpage", "float4 a_uv_limits"}, 1, 1,
@ -137,6 +138,11 @@ std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(bool textured, bool pgxp
#if UV_LIMITS
v_uv_limits = a_uv_limits * float4(255.0, 255.0, 255.0, 255.0);
#if FORCE_ROUND_TEXCOORDS
// Add 0.5 to the upper bounds when upscaling, to work around interpolation differences.
// Limited to force-round-texcoord hack, to avoid breaking other games.
v_uv_limits.zw += 0.5;
#endif
#endif
#endif
}
@ -630,9 +636,12 @@ void FilteredSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
}
}
std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(
GPU_HW::BatchRenderMode render_mode, GPUTransparencyMode transparency, GPU_HW::BatchTextureMode texture_mode,
GPUTextureFilter texture_filtering, bool force_round_texcoords, bool dithering, bool interlacing, bool check_mask)
std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMode render_mode,
GPUTransparencyMode transparency,
GPU_HW::BatchTextureMode texture_mode,
GPUTextureFilter texture_filtering, bool uv_limits,
bool force_round_texcoords, bool dithering, bool interlacing,
bool check_mask)
{
// TODO: don't write depth for shader blend
DebugAssert(transparency == GPUTransparencyMode::Disabled || render_mode == GPU_HW::BatchRenderMode::ShaderBlend);
@ -666,7 +675,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(
DefineMacro(ss, "INTERLACING", interlacing);
DefineMacro(ss, "TRUE_COLOR", m_true_color);
DefineMacro(ss, "TEXTURE_FILTERING", texture_filtering != GPUTextureFilter::Nearest);
DefineMacro(ss, "UV_LIMITS", m_uv_limits);
DefineMacro(ss, "UV_LIMITS", uv_limits);
DefineMacro(ss, "USE_DUAL_SOURCE", use_dual_source);
DefineMacro(ss, "WRITE_MASK_AS_DEPTH", m_write_mask_as_depth);
DefineMacro(ss, "FORCE_ROUND_TEXCOORDS", force_round_texcoords);
@ -796,7 +805,7 @@ float3 ApplyDebanding(float2 frag_coord)
if (texture_filtering != GPUTextureFilter::Nearest)
WriteBatchTextureFilter(ss, texture_filtering);
if (m_uv_limits)
if (uv_limits)
{
DeclareFragmentEntryPoint(ss, 1, 1,
{{"nointerpolation", "uint4 v_texpage"}, {"nointerpolation", "float4 v_uv_limits"}},

View file

@ -9,15 +9,14 @@ class GPU_HW_ShaderGen : public ShaderGen
{
public:
GPU_HW_ShaderGen(RenderAPI render_api, u32 resolution_scale, u32 multisamples, bool per_sample_shading,
bool true_color, bool scaled_dithering, bool uv_limits, bool write_mask_as_depth,
bool disable_color_perspective, bool supports_dual_source_blend, bool supports_framebuffer_fetch,
bool debanding);
bool true_color, bool scaled_dithering, bool write_mask_as_depth, bool disable_color_perspective,
bool supports_dual_source_blend, bool supports_framebuffer_fetch, bool debanding);
~GPU_HW_ShaderGen();
std::string GenerateBatchVertexShader(bool textured, bool pgxp_depth);
std::string GenerateBatchVertexShader(bool textured, bool uv_limits, bool force_round_texcoords, bool pgxp_depth);
std::string GenerateBatchFragmentShader(GPU_HW::BatchRenderMode render_mode, GPUTransparencyMode transparency,
GPU_HW::BatchTextureMode texture_mode, GPUTextureFilter texture_filtering,
bool force_round_texcoords, bool dithering, bool interlacing,
bool uv_limits, bool force_round_texcoords, bool dithering, bool interlacing,
bool check_mask);
std::string GenerateWireframeGeometryShader();
std::string GenerateWireframeFragmentShader();
@ -48,7 +47,6 @@ private:
bool m_per_sample_shading;
bool m_true_color;
bool m_scaled_dithering;
bool m_uv_limits;
bool m_write_mask_as_depth;
bool m_disable_color_perspective;
bool m_debanding;

View file

@ -548,7 +548,17 @@ void ImGuiManager::DrawEnhancementsOverlay()
if (g_settings.gpu_force_ntsc_timings && System::GetRegion() == ConsoleRegion::PAL)
text.append(" PAL60");
if (g_settings.gpu_texture_filter != GPUTextureFilter::Nearest)
text.append_format(" {}", Settings::GetTextureFilterName(g_settings.gpu_texture_filter));
{
if (g_settings.gpu_sprite_texture_filter != g_settings.gpu_texture_filter)
{
text.append_format(" {}/{}", Settings::GetTextureFilterName(g_settings.gpu_texture_filter),
Settings::GetTextureFilterName(g_settings.gpu_sprite_texture_filter));
}
else
{
text.append_format(" {}", Settings::GetTextureFilterName(g_settings.gpu_texture_filter));
}
}
if (g_settings.gpu_widescreen_hack && g_settings.display_aspect_ratio != DisplayAspectRatio::Auto &&
g_settings.display_aspect_ratio != DisplayAspectRatio::R4_3)
{
@ -1144,9 +1154,9 @@ void SaveStateSelectorUI::SaveCurrentSlot()
if (!System::SaveState(path.c_str(), &error, g_settings.create_save_state_backups))
{
Host::AddIconOSDMessage("SaveState", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save state to slot {0}:\n{1}"),
GetCurrentSlot(), error.GetDescription()),
Host::OSD_ERROR_DURATION);
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save state to slot {0}:\n{1}"),
GetCurrentSlot(), error.GetDescription()),
Host::OSD_ERROR_DURATION);
}
}

View file

@ -202,6 +202,9 @@ void Settings::Load(SettingsInterface& si)
ParseTextureFilterName(
si.GetStringValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER)).c_str())
.value_or(DEFAULT_GPU_TEXTURE_FILTER);
gpu_sprite_texture_filter =
ParseTextureFilterName(si.GetStringValue("GPU", "SpriteTextureFilter", GetTextureFilterName(gpu_texture_filter)).c_str())
.value_or(gpu_texture_filter);
gpu_line_detect_mode =
ParseLineDetectModeName(
si.GetStringValue("GPU", "LineDetectMode", GetLineDetectModeName(DEFAULT_GPU_LINE_DETECT_MODE)).c_str())
@ -498,6 +501,9 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering);
si.SetBoolValue("GPU", "ForceRoundTextureCoordinates", gpu_force_round_texcoords);
si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter));
si.SetStringValue(
"GPU", "SpriteTextureFilter",
(gpu_sprite_texture_filter != gpu_texture_filter) ? GetTextureFilterName(gpu_sprite_texture_filter) : "");
si.SetStringValue("GPU", "LineDetectMode", GetLineDetectModeName(gpu_line_detect_mode));
si.SetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(gpu_downsample_mode));
si.SetUIntValue("GPU", "DownsampleScale", gpu_downsample_scale);
@ -706,6 +712,7 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages)
g_settings.gpu_scaled_dithering = false;
g_settings.gpu_force_round_texcoords = false;
g_settings.gpu_texture_filter = GPUTextureFilter::Nearest;
g_settings.gpu_sprite_texture_filter = GPUTextureFilter::Nearest;
g_settings.gpu_line_detect_mode = GPULineDetectMode::Disabled;
g_settings.gpu_disable_interlacing = false;
g_settings.gpu_force_ntsc_timings = false;

View file

@ -119,6 +119,7 @@ struct Settings
bool gpu_scaled_dithering : 1 = true;
bool gpu_force_round_texcoords : 1 = false;
GPUTextureFilter gpu_texture_filter = DEFAULT_GPU_TEXTURE_FILTER;
GPUTextureFilter gpu_sprite_texture_filter = DEFAULT_GPU_TEXTURE_FILTER;
GPULineDetectMode gpu_line_detect_mode = DEFAULT_GPU_LINE_DETECT_MODE;
GPUDownsampleMode gpu_downsample_mode = DEFAULT_GPU_DOWNSAMPLE_MODE;
u8 gpu_downsample_scale = 1;

View file

@ -4017,6 +4017,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering ||
g_settings.gpu_force_round_texcoords != old_settings.gpu_force_round_texcoords ||
g_settings.gpu_texture_filter != old_settings.gpu_texture_filter ||
g_settings.gpu_sprite_texture_filter != old_settings.gpu_sprite_texture_filter ||
g_settings.gpu_line_detect_mode != old_settings.gpu_line_detect_mode ||
g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||

View file

@ -55,6 +55,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.textureFiltering, "GPU", "TextureFilter",
&Settings::ParseTextureFilterName, &Settings::GetTextureFilterName,
Settings::DEFAULT_GPU_TEXTURE_FILTER);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.spriteTextureFiltering, "GPU", "SpriteTextureFilter",
&Settings::ParseTextureFilterName, &Settings::GetTextureFilterName,
Settings::DEFAULT_GPU_TEXTURE_FILTER);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.gpuDownsampleMode, "GPU", "DownsampleMode",
&Settings::ParseDownsampleModeName, &Settings::GetDownsampleModeName,
Settings::DEFAULT_GPU_DOWNSAMPLE_MODE);
@ -526,6 +529,8 @@ void GraphicsSettingsWidget::setupAdditionalUi()
{
m_ui.textureFiltering->addItem(
QString::fromUtf8(Settings::GetTextureFilterDisplayName(static_cast<GPUTextureFilter>(i))));
m_ui.spriteTextureFiltering->addItem(
QString::fromUtf8(Settings::GetTextureFilterDisplayName(static_cast<GPUTextureFilter>(i))));
}
for (u32 i = 0; i < static_cast<u32>(GPUDownsampleMode::Count); i++)

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>584</width>
<height>430</height>
<height>450</height>
</rect>
</property>
<property name="windowTitle">
@ -216,17 +216,14 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="textureFiltering"/>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Aspect Ratio:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0,0,0">
<item>
<widget class="QComboBox" name="displayAspectRatio"/>
@ -260,37 +257,37 @@
</item>
</layout>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Deinterlacing:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QComboBox" name="displayDeinterlacing"/>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Crop:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QComboBox" name="displayCropMode"/>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Scaling:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QComboBox" name="displayScaling"/>
</item>
<item row="7" column="0" colspan="2">
<item row="8" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QCheckBox" name="pgxpDepthBuffer">
@ -350,6 +347,19 @@
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="textureFiltering"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Sprite Texture Filtering:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="spriteTextureFiltering"/>
</item>
</layout>
</widget>
</item>

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>780</width>
<height>490</height>
<height>650</height>
</rect>
</property>
<property name="sizePolicy">