diff --git a/src/core/gpu.h b/src/core/gpu.h index 25d7ced5c..9c7fa158a 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -181,10 +181,22 @@ protected: BitField shading_enable; // 0 - flat, 1 = gouroud BitField primitive; - // Helper functions. - bool IsTextureEnabled() const { return (primitive != Primitive::Line && texture_enable); } - bool IsTextureBlendingEnabled() const { return (IsTextureEnabled() && !raw_texture_enable); } - bool IsTransparencyEnabled() const { return transparency_enable; } + // Returns true if dithering should be enabled. Depends on the primitive type. + bool IsDitheringEnabled() const + { + switch (primitive) + { + case Primitive::Polygon: + return shading_enable || !raw_texture_enable; + + case Primitive::Line: + return true; + + case Primitive::Rectangle: + default: + return false; + } + } }; // TODO: Use BitField to do sign extending instead @@ -241,6 +253,12 @@ protected: } }; + // 4x4 dither matrix. + static constexpr s32 DITHER_MATRIX[4][4] = {{-4, +0, -3, +1}, // row 0 + {+2, -2, +3, -1}, // row 1 + {-3, +1, -4, +0}, // row 2 + {+4, -1, +2, -2}}; // row 3 + void SoftReset(); // Sets dots per scanline diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index db32f689a..f26cbf5c2 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -241,7 +241,7 @@ void main() return ss.str(); } -std::string GPU_HW::GenerateFragmentShader(HWBatchRenderMode transparency, TextureMode texture_mode) +std::string GPU_HW::GenerateFragmentShader(HWBatchRenderMode transparency, TextureMode texture_mode, bool dithering) { const TextureMode actual_texture_mode = texture_mode & ~TextureMode::RawTextureBit; const bool raw_texture = (texture_mode & TextureMode::RawTextureBit) == TextureMode::RawTextureBit; @@ -258,6 +258,16 @@ std::string GPU_HW::GenerateFragmentShader(HWBatchRenderMode transparency, Textu DefineMacro(ss, "PALETTE_4_BIT", actual_texture_mode == GPU::TextureMode::Palette4Bit); DefineMacro(ss, "PALETTE_8_BIT", actual_texture_mode == GPU::TextureMode::Palette8Bit); DefineMacro(ss, "RAW_TEXTURE", raw_texture); + DefineMacro(ss, "DITHERING", dithering); + + ss << "const int[16] s_dither_values = int[16]( "; + for (u32 i = 0; i < 16; i++) + { + if (i > 0) + ss << ", "; + ss << DITHER_MATRIX[i / 4][i % 4]; + } + ss << " );\n"; ss << R"( in vec3 v_col0; @@ -271,6 +281,22 @@ uniform vec2 u_transparent_alpha; out vec4 o_col0; +vec4 ApplyDithering(vec4 col) +{ + ivec3 icol = ivec3(col.rgb * vec3(255.0, 255.0, 255.0)); + + // apply dither + ivec2 fc = ivec2(gl_FragCoord.xy) & ivec2(3, 3); + int offset = s_dither_values[fc.y * 4 + fc.x]; + icol += ivec3(offset, offset, offset); + + // saturate + icol = clamp(icol, ivec3(0, 0, 0), ivec3(255, 255, 255)); + + // clip to 5-bit range + return vec4((icol.rgb >> 3) / vec3(31.0, 31.0, 31.0), col.a); +} + #if TEXTURED ivec2 ApplyNativeTextureWindow(ivec2 coords) { @@ -370,6 +396,10 @@ void main() o_col0 = vec4(v_col0, 0.0); #endif #endif + + #if DITHERING + o_col0 = ApplyDithering(o_col0); + #endif } )"; @@ -547,13 +577,15 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32 const TransparencyMode transparency_mode = rc.transparency_enable ? m_render_state.transparency_mode : TransparencyMode::Disabled; const HWPrimitive rc_primitive = GetPrimitiveForCommand(rc); + const bool dithering_enable = rc.IsDitheringEnabled() ? m_GPUSTAT.dither_enable : false; if (!IsFlushed()) { const u32 max_added_vertices = num_vertices + 2; const bool buffer_overflow = (m_batch.vertices.size() + max_added_vertices) >= MAX_BATCH_VERTEX_COUNT; if (buffer_overflow || rc_primitive == HWPrimitive::LineStrip || m_batch.texture_mode != texture_mode || m_batch.transparency_mode != transparency_mode || m_batch.primitive != rc_primitive || - m_render_state.IsTexturePageChanged() || m_render_state.IsTextureWindowChanged()) + dithering_enable != m_batch.dithering || m_render_state.IsTexturePageChanged() || + m_render_state.IsTextureWindowChanged()) { FlushRender(); } @@ -563,6 +595,7 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32 m_batch.primitive = rc_primitive; m_batch.texture_mode = texture_mode; m_batch.transparency_mode = transparency_mode; + m_batch.dithering = dithering_enable; if (m_render_state.IsTexturePageChanged()) { diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index 54618e9d8..4c9ad2b48 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -44,14 +44,15 @@ protected: struct HWRenderBatch { - HWPrimitive primitive; - TextureMode texture_mode; u32 texture_page_x; u32 texture_page_y; u32 texture_palette_x; u32 texture_palette_y; + HWPrimitive primitive; + TextureMode texture_mode; TransparencyMode transparency_mode; std::array texture_window_values; + bool dithering; std::vector vertices; @@ -100,7 +101,7 @@ protected: } std::string GenerateVertexShader(bool textured); - std::string GenerateFragmentShader(HWBatchRenderMode transparency, TextureMode texture_mode); + std::string GenerateFragmentShader(HWBatchRenderMode transparency, TextureMode texture_mode, bool dithering); std::string GenerateScreenQuadVertexShader(); std::string GenerateFillFragmentShader(); std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced); diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index 2a776d74e..bf5b2f16c 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -121,8 +121,6 @@ void GPU_HW_OpenGL::DrawRendererStatsWindow() ImGui::NextColumn(); ImGui::Text("%u", m_last_stats.num_vertices); ImGui::NextColumn(); - - } ImGui::End(); @@ -246,10 +244,14 @@ bool GPU_HW_OpenGL::CompilePrograms() { for (u32 texture_mode = 0; texture_mode < 9; texture_mode++) { - if (!CompileProgram(m_render_programs[render_mode][texture_mode], static_cast(render_mode), - static_cast(texture_mode))) + for (u8 dithering = 0; dithering < 2; dithering++) { - return false; + if (!CompileProgram(m_render_programs[render_mode][texture_mode][dithering], + static_cast(render_mode), static_cast(texture_mode), + ConvertToBoolUnchecked(dithering))) + { + return false; + } } } } @@ -280,11 +282,12 @@ bool GPU_HW_OpenGL::CompilePrograms() return true; } -bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, HWBatchRenderMode render_mode, TextureMode texture_mode) +bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, HWBatchRenderMode render_mode, TextureMode texture_mode, + bool dithering) { const bool textured = texture_mode != TextureMode::Disabled; const std::string vs = GenerateVertexShader(textured); - const std::string fs = GenerateFragmentShader(render_mode, texture_mode); + const std::string fs = GenerateFragmentShader(render_mode, texture_mode, dithering); if (!prog.Compile(vs.c_str(), fs.c_str())) return false; @@ -319,7 +322,8 @@ bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, HWBatchRenderMode render_m void GPU_HW_OpenGL::SetDrawState(HWBatchRenderMode render_mode) { - const GL::Program& prog = m_render_programs[static_cast(render_mode)][static_cast(m_batch.texture_mode)]; + const GL::Program& prog = + m_render_programs[static_cast(render_mode)][static_cast(m_batch.texture_mode)][m_batch.dithering]; prog.Bind(); prog.Uniform2i(0, m_drawing_offset.x, m_drawing_offset.y); diff --git a/src/core/gpu_hw_opengl.h b/src/core/gpu_hw_opengl.h index 525ed8297..c91c2713a 100644 --- a/src/core/gpu_hw_opengl.h +++ b/src/core/gpu_hw_opengl.h @@ -42,8 +42,6 @@ private: u32 num_vram_read_texture_updates; }; - void DrawRendererStatistics(); - std::tuple ConvertToFramebufferCoordinates(s32 x, s32 y); void SetMaxResolutionScale(); @@ -55,7 +53,7 @@ private: void CreateVertexBuffer(); bool CompilePrograms(); - bool CompileProgram(GL::Program& prog, HWBatchRenderMode render_mode, TextureMode texture_mode); + bool CompileProgram(GL::Program& prog, HWBatchRenderMode render_mode, TextureMode texture_mode, bool dithering); void SetDrawState(HWBatchRenderMode render_mode); // downsample texture - used for readbacks at >1xIR. @@ -72,8 +70,8 @@ private: bool m_drawing_area_changed = true; bool m_show_renderer_statistics = false; - std::array, 4> m_render_programs; // [render_mode][texture_mode] - std::array, 2> m_display_programs; // [depth_24][interlaced] + std::array, 9>, 4> m_render_programs; // [render_mode][texture_mode][dithering] + std::array, 2> m_display_programs; // [depth_24][interlaced] GLStats m_stats = {}; GLStats m_last_stats = {};