mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 06:25:37 +00:00
GPU: Implement dithering on OpenGL backend
This commit is contained in:
parent
1d1da1d82c
commit
ac82383abe
|
@ -181,10 +181,22 @@ protected:
|
||||||
BitField<u32, bool, 28, 1> shading_enable; // 0 - flat, 1 = gouroud
|
BitField<u32, bool, 28, 1> shading_enable; // 0 - flat, 1 = gouroud
|
||||||
BitField<u32, Primitive, 29, 21> primitive;
|
BitField<u32, Primitive, 29, 21> primitive;
|
||||||
|
|
||||||
// Helper functions.
|
// Returns true if dithering should be enabled. Depends on the primitive type.
|
||||||
bool IsTextureEnabled() const { return (primitive != Primitive::Line && texture_enable); }
|
bool IsDitheringEnabled() const
|
||||||
bool IsTextureBlendingEnabled() const { return (IsTextureEnabled() && !raw_texture_enable); }
|
{
|
||||||
bool IsTransparencyEnabled() const { return transparency_enable; }
|
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
|
// 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();
|
void SoftReset();
|
||||||
|
|
||||||
// Sets dots per scanline
|
// Sets dots per scanline
|
||||||
|
|
|
@ -241,7 +241,7 @@ void main()
|
||||||
return ss.str();
|
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 TextureMode actual_texture_mode = texture_mode & ~TextureMode::RawTextureBit;
|
||||||
const bool raw_texture = (texture_mode & TextureMode::RawTextureBit) == 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_4_BIT", actual_texture_mode == GPU::TextureMode::Palette4Bit);
|
||||||
DefineMacro(ss, "PALETTE_8_BIT", actual_texture_mode == GPU::TextureMode::Palette8Bit);
|
DefineMacro(ss, "PALETTE_8_BIT", actual_texture_mode == GPU::TextureMode::Palette8Bit);
|
||||||
DefineMacro(ss, "RAW_TEXTURE", raw_texture);
|
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"(
|
ss << R"(
|
||||||
in vec3 v_col0;
|
in vec3 v_col0;
|
||||||
|
@ -271,6 +281,22 @@ uniform vec2 u_transparent_alpha;
|
||||||
|
|
||||||
out vec4 o_col0;
|
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
|
#if TEXTURED
|
||||||
ivec2 ApplyNativeTextureWindow(ivec2 coords)
|
ivec2 ApplyNativeTextureWindow(ivec2 coords)
|
||||||
{
|
{
|
||||||
|
@ -370,6 +396,10 @@ void main()
|
||||||
o_col0 = vec4(v_col0, 0.0);
|
o_col0 = vec4(v_col0, 0.0);
|
||||||
#endif
|
#endif
|
||||||
#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 =
|
const TransparencyMode transparency_mode =
|
||||||
rc.transparency_enable ? m_render_state.transparency_mode : TransparencyMode::Disabled;
|
rc.transparency_enable ? m_render_state.transparency_mode : TransparencyMode::Disabled;
|
||||||
const HWPrimitive rc_primitive = GetPrimitiveForCommand(rc);
|
const HWPrimitive rc_primitive = GetPrimitiveForCommand(rc);
|
||||||
|
const bool dithering_enable = rc.IsDitheringEnabled() ? m_GPUSTAT.dither_enable : false;
|
||||||
if (!IsFlushed())
|
if (!IsFlushed())
|
||||||
{
|
{
|
||||||
const u32 max_added_vertices = num_vertices + 2;
|
const u32 max_added_vertices = num_vertices + 2;
|
||||||
const bool buffer_overflow = (m_batch.vertices.size() + max_added_vertices) >= MAX_BATCH_VERTEX_COUNT;
|
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 ||
|
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_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();
|
FlushRender();
|
||||||
}
|
}
|
||||||
|
@ -563,6 +595,7 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32
|
||||||
m_batch.primitive = rc_primitive;
|
m_batch.primitive = rc_primitive;
|
||||||
m_batch.texture_mode = texture_mode;
|
m_batch.texture_mode = texture_mode;
|
||||||
m_batch.transparency_mode = transparency_mode;
|
m_batch.transparency_mode = transparency_mode;
|
||||||
|
m_batch.dithering = dithering_enable;
|
||||||
|
|
||||||
if (m_render_state.IsTexturePageChanged())
|
if (m_render_state.IsTexturePageChanged())
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,14 +44,15 @@ protected:
|
||||||
|
|
||||||
struct HWRenderBatch
|
struct HWRenderBatch
|
||||||
{
|
{
|
||||||
HWPrimitive primitive;
|
|
||||||
TextureMode texture_mode;
|
|
||||||
u32 texture_page_x;
|
u32 texture_page_x;
|
||||||
u32 texture_page_y;
|
u32 texture_page_y;
|
||||||
u32 texture_palette_x;
|
u32 texture_palette_x;
|
||||||
u32 texture_palette_y;
|
u32 texture_palette_y;
|
||||||
|
HWPrimitive primitive;
|
||||||
|
TextureMode texture_mode;
|
||||||
TransparencyMode transparency_mode;
|
TransparencyMode transparency_mode;
|
||||||
std::array<u8, 4> texture_window_values;
|
std::array<u8, 4> texture_window_values;
|
||||||
|
bool dithering;
|
||||||
|
|
||||||
std::vector<HWVertex> vertices;
|
std::vector<HWVertex> vertices;
|
||||||
|
|
||||||
|
@ -100,7 +101,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GenerateVertexShader(bool textured);
|
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 GenerateScreenQuadVertexShader();
|
||||||
std::string GenerateFillFragmentShader();
|
std::string GenerateFillFragmentShader();
|
||||||
std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced);
|
std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced);
|
||||||
|
|
|
@ -121,8 +121,6 @@ void GPU_HW_OpenGL::DrawRendererStatsWindow()
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%u", m_last_stats.num_vertices);
|
ImGui::Text("%u", m_last_stats.num_vertices);
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
@ -246,13 +244,17 @@ bool GPU_HW_OpenGL::CompilePrograms()
|
||||||
{
|
{
|
||||||
for (u32 texture_mode = 0; texture_mode < 9; texture_mode++)
|
for (u32 texture_mode = 0; texture_mode < 9; texture_mode++)
|
||||||
{
|
{
|
||||||
if (!CompileProgram(m_render_programs[render_mode][texture_mode], static_cast<HWBatchRenderMode>(render_mode),
|
for (u8 dithering = 0; dithering < 2; dithering++)
|
||||||
static_cast<TextureMode>(texture_mode)))
|
{
|
||||||
|
if (!CompileProgram(m_render_programs[render_mode][texture_mode][dithering],
|
||||||
|
static_cast<HWBatchRenderMode>(render_mode), static_cast<TextureMode>(texture_mode),
|
||||||
|
ConvertToBoolUnchecked(dithering)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Use string_view
|
// TODO: Use string_view
|
||||||
for (u8 depth_24bit = 0; depth_24bit < 2; depth_24bit++)
|
for (u8 depth_24bit = 0; depth_24bit < 2; depth_24bit++)
|
||||||
|
@ -280,11 +282,12 @@ bool GPU_HW_OpenGL::CompilePrograms()
|
||||||
return true;
|
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 bool textured = texture_mode != TextureMode::Disabled;
|
||||||
const std::string vs = GenerateVertexShader(textured);
|
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()))
|
if (!prog.Compile(vs.c_str(), fs.c_str()))
|
||||||
return false;
|
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)
|
void GPU_HW_OpenGL::SetDrawState(HWBatchRenderMode render_mode)
|
||||||
{
|
{
|
||||||
const GL::Program& prog = m_render_programs[static_cast<u32>(render_mode)][static_cast<u32>(m_batch.texture_mode)];
|
const GL::Program& prog =
|
||||||
|
m_render_programs[static_cast<u32>(render_mode)][static_cast<u32>(m_batch.texture_mode)][m_batch.dithering];
|
||||||
prog.Bind();
|
prog.Bind();
|
||||||
|
|
||||||
prog.Uniform2i(0, m_drawing_offset.x, m_drawing_offset.y);
|
prog.Uniform2i(0, m_drawing_offset.x, m_drawing_offset.y);
|
||||||
|
|
|
@ -42,8 +42,6 @@ private:
|
||||||
u32 num_vram_read_texture_updates;
|
u32 num_vram_read_texture_updates;
|
||||||
};
|
};
|
||||||
|
|
||||||
void DrawRendererStatistics();
|
|
||||||
|
|
||||||
std::tuple<s32, s32> ConvertToFramebufferCoordinates(s32 x, s32 y);
|
std::tuple<s32, s32> ConvertToFramebufferCoordinates(s32 x, s32 y);
|
||||||
|
|
||||||
void SetMaxResolutionScale();
|
void SetMaxResolutionScale();
|
||||||
|
@ -55,7 +53,7 @@ private:
|
||||||
void CreateVertexBuffer();
|
void CreateVertexBuffer();
|
||||||
|
|
||||||
bool CompilePrograms();
|
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);
|
void SetDrawState(HWBatchRenderMode render_mode);
|
||||||
|
|
||||||
// downsample texture - used for readbacks at >1xIR.
|
// downsample texture - used for readbacks at >1xIR.
|
||||||
|
@ -72,7 +70,7 @@ private:
|
||||||
bool m_drawing_area_changed = true;
|
bool m_drawing_area_changed = true;
|
||||||
bool m_show_renderer_statistics = false;
|
bool m_show_renderer_statistics = false;
|
||||||
|
|
||||||
std::array<std::array<GL::Program, 9>, 4> m_render_programs; // [render_mode][texture_mode]
|
std::array<std::array<std::array<GL::Program, 2>, 9>, 4> m_render_programs; // [render_mode][texture_mode][dithering]
|
||||||
std::array<std::array<GL::Program, 2>, 2> m_display_programs; // [depth_24][interlaced]
|
std::array<std::array<GL::Program, 2>, 2> m_display_programs; // [depth_24][interlaced]
|
||||||
|
|
||||||
GLStats m_stats = {};
|
GLStats m_stats = {};
|
||||||
|
|
Loading…
Reference in a new issue