mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 07:35:41 +00:00
GPU: Implement interlaced VRAM fills
This commit is contained in:
parent
2aecb570c1
commit
c483a78889
|
@ -893,7 +893,7 @@ void GPU::ReadVRAM(u32 x, u32 y, u32 width, u32 height) {}
|
|||
void GPU::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
|
||||
{
|
||||
const u16 color16 = RGBA8888ToRGBA5551(color);
|
||||
if ((x + width) <= VRAM_WIDTH)
|
||||
if ((x + width) <= VRAM_WIDTH && !IsInterlacedRenderingEnabled())
|
||||
{
|
||||
for (u32 yoffs = 0; yoffs < height; yoffs++)
|
||||
{
|
||||
|
@ -901,6 +901,26 @@ void GPU::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
|
|||
std::fill_n(&m_vram_ptr[row * VRAM_WIDTH + x], width, color16);
|
||||
}
|
||||
}
|
||||
else if (IsInterlacedRenderingEnabled())
|
||||
{
|
||||
// Hardware tests show that fills seem to break on the first two lines when the offset matches the displayed field.
|
||||
if (IsRasterScanlinePending())
|
||||
Synchronize();
|
||||
const u32 active_field = GetInterlacedField();
|
||||
for (u32 yoffs = 0; yoffs < height; yoffs++)
|
||||
{
|
||||
const u32 row = (y + yoffs) % VRAM_HEIGHT;
|
||||
if ((row & u32(1)) == active_field)
|
||||
continue;
|
||||
|
||||
u16* row_ptr = &m_vram_ptr[row * VRAM_WIDTH];
|
||||
for (u32 xoffs = 0; xoffs < width; xoffs++)
|
||||
{
|
||||
const u32 col = (x + xoffs) % VRAM_WIDTH;
|
||||
row_ptr[col] = color16;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 yoffs = 0; yoffs < height; yoffs++)
|
||||
|
|
|
@ -330,7 +330,16 @@ protected:
|
|||
void Execute(TickCount ticks);
|
||||
|
||||
/// Returns true if scanout should be interlaced.
|
||||
bool IsDisplayInterlaced() const { return !m_force_progressive_scan && m_GPUSTAT.In480iMode(); }
|
||||
ALWAYS_INLINE bool IsInterlacedDisplayEnabled() const { return (!m_force_progressive_scan) & m_GPUSTAT.In480iMode(); }
|
||||
|
||||
/// Returns true if interlaced rendering is enabled and force progressive scan is disabled.
|
||||
ALWAYS_INLINE bool IsInterlacedRenderingEnabled() const
|
||||
{
|
||||
return (!m_force_progressive_scan) & m_GPUSTAT.SkipDrawingToActiveField();
|
||||
}
|
||||
|
||||
/// Returns 0 if the currently-rendered field is even, otherwise 1.
|
||||
ALWAYS_INLINE u32 GetInterlacedField() const { return BoolToUInt32(m_GPUSTAT.displaying_odd_line); }
|
||||
|
||||
/// Sets/decodes GP0(E1h) (set draw mode).
|
||||
void SetDrawMode(u16 bits);
|
||||
|
|
|
@ -306,7 +306,7 @@ bool GPU::HandleRenderCommand(const u32*& command_ptr, u32 command_size)
|
|||
primitive_names[static_cast<u8>(rc.primitive.GetValue())], ZeroExtend32(num_vertices),
|
||||
ZeroExtend32(words_per_vertex));
|
||||
|
||||
if (m_GPUSTAT.SkipDrawingToActiveField() && IsRasterScanlinePending())
|
||||
if (IsInterlacedRenderingEnabled() && IsRasterScanlinePending())
|
||||
Synchronize();
|
||||
|
||||
DispatchRenderCommand(rc, num_vertices, command_ptr);
|
||||
|
|
|
@ -541,10 +541,10 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32
|
|||
m_batch_ubo_dirty = true;
|
||||
}
|
||||
|
||||
m_batch.interlacing = m_GPUSTAT.SkipDrawingToActiveField();
|
||||
m_batch.interlacing = IsInterlacedRenderingEnabled();
|
||||
if (m_batch.interlacing)
|
||||
{
|
||||
const u32 displayed_field = BoolToUInt32(m_GPUSTAT.displaying_odd_line);
|
||||
const u32 displayed_field = GetInterlacedField();
|
||||
m_batch_ubo_dirty |= (m_batch_ubo_data.u_interlaced_displayed_field != displayed_field);
|
||||
m_batch_ubo_data.u_interlaced_displayed_field = displayed_field;
|
||||
}
|
||||
|
|
|
@ -377,8 +377,13 @@ bool GPU_HW_D3D11::CompileShaders()
|
|||
if (!m_copy_pixel_shader)
|
||||
return false;
|
||||
|
||||
m_fill_pixel_shader = m_shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateFillFragmentShader());
|
||||
if (!m_fill_pixel_shader)
|
||||
m_vram_fill_pixel_shader = m_shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateFillFragmentShader());
|
||||
if (!m_vram_fill_pixel_shader)
|
||||
return false;
|
||||
|
||||
m_vram_interlaced_fill_pixel_shader =
|
||||
m_shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateInterlacedFillFragmentShader());
|
||||
if (!m_vram_interlaced_fill_pixel_shader)
|
||||
return false;
|
||||
|
||||
m_vram_read_pixel_shader = m_shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateVRAMReadFragmentShader());
|
||||
|
@ -542,7 +547,7 @@ void GPU_HW_D3D11::UpdateDisplay()
|
|||
const u32 display_height = m_crtc_state.display_vram_height;
|
||||
const u32 scaled_display_width = display_width * m_resolution_scale;
|
||||
const u32 scaled_display_height = display_height * m_resolution_scale;
|
||||
const bool interlaced = IsDisplayInterlaced();
|
||||
const bool interlaced = IsInterlacedDisplayEnabled();
|
||||
|
||||
if (m_GPUSTAT.display_disable)
|
||||
{
|
||||
|
@ -559,7 +564,7 @@ void GPU_HW_D3D11::UpdateDisplay()
|
|||
m_context->OMSetRenderTargets(1, m_display_texture.GetD3DRTVArray(), nullptr);
|
||||
m_context->PSSetShaderResources(0, 1, m_vram_texture.GetD3DSRVArray());
|
||||
|
||||
const u32 reinterpret_field_offset = BoolToUInt32(m_GPUSTAT.displaying_odd_line);
|
||||
const u32 reinterpret_field_offset = GetInterlacedField();
|
||||
const u32 reinterpret_start_x = m_crtc_state.regs.X * m_resolution_scale;
|
||||
const u32 reinterpret_width = scaled_display_width + (m_crtc_state.display_vram_left - m_crtc_state.regs.X);
|
||||
const u32 uniforms[4] = {reinterpret_field_offset, reinterpret_start_x};
|
||||
|
@ -634,12 +639,21 @@ void GPU_HW_D3D11::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
|
|||
if (!m_true_color)
|
||||
color = RGBA5551ToRGBA8888(RGBA8888ToRGBA5551(color));
|
||||
|
||||
float uniforms[4];
|
||||
std::tie(uniforms[0], uniforms[1], uniforms[2], uniforms[3]) = RGBA8ToFloat(color);
|
||||
struct Uniforms
|
||||
{
|
||||
float u_fill_color[4];
|
||||
u32 u_interlaced_displayed_field;
|
||||
};
|
||||
Uniforms uniforms;
|
||||
std::tie(uniforms.u_fill_color[0], uniforms.u_fill_color[1], uniforms.u_fill_color[2], uniforms.u_fill_color[3]) =
|
||||
RGBA8ToFloat(color);
|
||||
uniforms.u_interlaced_displayed_field = GetInterlacedField();
|
||||
|
||||
SetViewportAndScissor(x * m_resolution_scale, y * m_resolution_scale, width * m_resolution_scale,
|
||||
height * m_resolution_scale);
|
||||
DrawUtilityShader(m_fill_pixel_shader.Get(), uniforms, sizeof(uniforms));
|
||||
DrawUtilityShader(IsInterlacedRenderingEnabled() ? m_vram_interlaced_fill_pixel_shader.Get() :
|
||||
m_vram_fill_pixel_shader.Get(),
|
||||
&uniforms, sizeof(uniforms));
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
}
|
||||
|
|
|
@ -109,7 +109,8 @@ private:
|
|||
|
||||
ComPtr<ID3D11VertexShader> m_screen_quad_vertex_shader;
|
||||
ComPtr<ID3D11PixelShader> m_copy_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_fill_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_vram_fill_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_vram_interlaced_fill_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_vram_read_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_vram_write_pixel_shader;
|
||||
std::array<std::array<ComPtr<ID3D11PixelShader>, 2>, 2> m_display_pixel_shaders; // [depth_24][interlaced]
|
||||
|
|
|
@ -367,23 +367,34 @@ bool GPU_HW_OpenGL::CompilePrograms()
|
|||
|
||||
prog->Bind();
|
||||
prog->Uniform1i("samp0", 0);
|
||||
|
||||
m_display_programs[depth_24bit][interlaced] = std::move(*prog);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<GL::Program> prog = m_shader_cache.GetProgram(
|
||||
shadergen.GenerateScreenQuadVertexShader(), shadergen.GenerateVRAMReadFragmentShader(), [this](GL::Program& prog) {
|
||||
if (!m_is_gles)
|
||||
prog.BindFragData(0, "o_col0");
|
||||
});
|
||||
std::optional<GL::Program> prog =
|
||||
m_shader_cache.GetProgram(shadergen.GenerateScreenQuadVertexShader(),
|
||||
shadergen.GenerateInterlacedFillFragmentShader(), [this](GL::Program& prog) {
|
||||
if (!m_is_gles)
|
||||
prog.BindFragData(0, "o_col0");
|
||||
});
|
||||
if (!prog)
|
||||
return false;
|
||||
|
||||
prog->BindUniformBlock("UBOBlock", 1);
|
||||
prog->Bind();
|
||||
m_vram_interlaced_fill_program = std::move(*prog);
|
||||
|
||||
prog = m_shader_cache.GetProgram(shadergen.GenerateScreenQuadVertexShader(),
|
||||
shadergen.GenerateVRAMReadFragmentShader(), [this](GL::Program& prog) {
|
||||
if (!m_is_gles)
|
||||
prog.BindFragData(0, "o_col0");
|
||||
});
|
||||
if (!prog)
|
||||
return false;
|
||||
|
||||
prog->BindUniformBlock("UBOBlock", 1);
|
||||
prog->Bind();
|
||||
prog->Uniform1i("samp0", 0);
|
||||
|
||||
m_vram_read_program = std::move(*prog);
|
||||
|
||||
if (m_supports_texture_buffer)
|
||||
|
@ -399,7 +410,6 @@ bool GPU_HW_OpenGL::CompilePrograms()
|
|||
prog->BindUniformBlock("UBOBlock", 1);
|
||||
prog->Bind();
|
||||
prog->Uniform1i("samp0", 0);
|
||||
|
||||
m_vram_write_program = std::move(*prog);
|
||||
}
|
||||
|
||||
|
@ -490,7 +500,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
|||
const u32 display_height = m_crtc_state.display_vram_height;
|
||||
const u32 scaled_display_width = display_width * m_resolution_scale;
|
||||
const u32 scaled_display_height = display_height * m_resolution_scale;
|
||||
const bool interlaced = IsDisplayInterlaced();
|
||||
const bool interlaced = IsInterlacedDisplayEnabled();
|
||||
|
||||
if (m_GPUSTAT.display_disable)
|
||||
{
|
||||
|
@ -516,7 +526,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
|||
const u32 scaled_flipped_vram_offset_y =
|
||||
m_vram_texture.GetHeight() - scaled_vram_offset_y - scaled_display_height;
|
||||
|
||||
const u32 reinterpret_field_offset = BoolToUInt32(m_GPUSTAT.displaying_odd_line);
|
||||
const u32 reinterpret_field_offset = GetInterlacedField();
|
||||
const u32 reinterpret_start_x = m_crtc_state.regs.X * m_resolution_scale;
|
||||
const u32 reinterpret_width = scaled_display_width + (m_crtc_state.display_vram_left - m_crtc_state.regs.X);
|
||||
const u32 uniforms[4] = {reinterpret_field_offset, reinterpret_start_x};
|
||||
|
@ -600,11 +610,32 @@ void GPU_HW_OpenGL::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
|
|||
if (!m_true_color)
|
||||
color = RGBA5551ToRGBA8888(RGBA8888ToRGBA5551(color));
|
||||
|
||||
const auto [r, g, b, a] = RGBA8ToFloat(color);
|
||||
glClearColor(r, g, b, a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// fast path when not using interlaced rendering
|
||||
if (!IsInterlacedRenderingEnabled())
|
||||
{
|
||||
const auto [r, g, b, a] = RGBA8ToFloat(color);
|
||||
glClearColor(r, g, b, a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
SetScissorFromDrawingArea();
|
||||
}
|
||||
else
|
||||
{
|
||||
struct Uniforms
|
||||
{
|
||||
float u_fill_color[4];
|
||||
u32 u_interlaced_displayed_field;
|
||||
};
|
||||
Uniforms uniforms;
|
||||
std::tie(uniforms.u_fill_color[0], uniforms.u_fill_color[1], uniforms.u_fill_color[2], uniforms.u_fill_color[3]) =
|
||||
RGBA8ToFloat(color);
|
||||
uniforms.u_interlaced_displayed_field = GetInterlacedField();
|
||||
|
||||
SetScissorFromDrawingArea();
|
||||
m_vram_interlaced_fill_program.Bind();
|
||||
UploadUniformBlock(&uniforms, sizeof(uniforms));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data)
|
||||
|
|
|
@ -79,6 +79,7 @@ private:
|
|||
std::array<std::array<std::array<std::array<GL::Program, 2>, 2>, 9>, 4>
|
||||
m_render_programs; // [render_mode][texture_mode][dithering][interlacing]
|
||||
std::array<std::array<GL::Program, 2>, 2> m_display_programs; // [depth_24][interlaced]
|
||||
GL::Program m_vram_interlaced_fill_program;
|
||||
GL::Program m_vram_read_program;
|
||||
GL::Program m_vram_write_program;
|
||||
|
||||
|
|
|
@ -253,7 +253,17 @@ bool GPU_HW_OpenGL_ES::CompilePrograms()
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<GL::Program> prog =
|
||||
std::optional<GL::Program> prog = m_shader_cache.GetProgram(shadergen.GenerateScreenQuadVertexShader(),
|
||||
shadergen.GenerateInterlacedFillFragmentShader());
|
||||
if (!prog)
|
||||
return false;
|
||||
|
||||
prog->Bind();
|
||||
prog->RegisterUniform("u_fill_color");
|
||||
prog->RegisterUniform("u_u_interlaced_displayed_field");
|
||||
m_vram_interlaced_fill_program = std::move(*prog);
|
||||
|
||||
prog =
|
||||
m_shader_cache.GetProgram(shadergen.GenerateScreenQuadVertexShader(), shadergen.GenerateVRAMReadFragmentShader());
|
||||
if (!prog)
|
||||
return false;
|
||||
|
@ -357,7 +367,7 @@ void GPU_HW_OpenGL_ES::UpdateDisplay()
|
|||
const u32 display_height = m_crtc_state.display_vram_height;
|
||||
const u32 scaled_display_width = display_width * m_resolution_scale;
|
||||
const u32 scaled_display_height = display_height * m_resolution_scale;
|
||||
const bool interlaced = IsDisplayInterlaced();
|
||||
const bool interlaced = IsInterlacedDisplayEnabled();
|
||||
|
||||
if (m_GPUSTAT.display_disable)
|
||||
{
|
||||
|
@ -385,7 +395,7 @@ void GPU_HW_OpenGL_ES::UpdateDisplay()
|
|||
const u32 scaled_flipped_vram_offset_y =
|
||||
m_vram_texture.GetHeight() - scaled_vram_offset_y - scaled_display_height;
|
||||
|
||||
const u32 reinterpret_field_offset = BoolToUInt32(m_GPUSTAT.displaying_odd_line);
|
||||
const u32 reinterpret_field_offset = GetInterlacedField();
|
||||
const u32 reinterpret_start_x = m_crtc_state.regs.X * m_resolution_scale;
|
||||
const u32 reinterpret_width = scaled_display_width + (m_crtc_state.display_vram_left - m_crtc_state.regs.X);
|
||||
|
||||
|
@ -469,10 +479,21 @@ void GPU_HW_OpenGL_ES::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
|
|||
color = RGBA5551ToRGBA8888(RGBA8888ToRGBA5551(color));
|
||||
|
||||
const auto [r, g, b, a] = RGBA8ToFloat(color);
|
||||
glClearColor(r, g, b, a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
SetScissorFromDrawingArea();
|
||||
if (!IsInterlacedRenderingEnabled())
|
||||
{
|
||||
glClearColor(r, g, b, a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
SetScissorFromDrawingArea();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vram_interlaced_fill_program.Bind();
|
||||
m_vram_interlaced_fill_program.Uniform4f(0, r, g, b, a);
|
||||
m_vram_interlaced_fill_program.Uniform1i(1, GetInterlacedField());
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
RestoreGraphicsAPIState();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL_ES::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data)
|
||||
|
|
|
@ -67,5 +67,6 @@ private:
|
|||
std::array<std::array<std::array<std::array<GL::Program, 2>, 2>, 9>, 4>
|
||||
m_render_programs; // [render_mode][texture_mode][dithering][interlacing]
|
||||
std::array<std::array<GL::Program, 2>, 2> m_display_programs; // [depth_24][interlaced]
|
||||
GL::Program m_vram_interlaced_fill_program;
|
||||
GL::Program m_vram_read_program;
|
||||
};
|
||||
|
|
|
@ -523,7 +523,7 @@ float4 SampleFromVRAM(int4 texpage, int2 icoord)
|
|||
float oalpha;
|
||||
|
||||
#if INTERLACING
|
||||
if (((int(v_pos.y) / RESOLUTION_SCALE) & 1) == u_interlaced_displayed_field)
|
||||
if (((fixYCoord(int(v_pos.y)) / RESOLUTION_SCALE) & 1) == u_interlaced_displayed_field)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
|
@ -732,6 +732,26 @@ std::string GPU_HW_ShaderGen::GenerateFillFragmentShader()
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateInterlacedFillFragmentShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
WriteCommonFunctions(ss);
|
||||
DeclareUniformBuffer(ss, {"float4 u_fill_color", "int u_interlaced_displayed_field"});
|
||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, true, false);
|
||||
|
||||
ss << R"(
|
||||
{
|
||||
if (((fixYCoord(int(v_pos.y)) / RESOLUTION_SCALE) & 1) == u_interlaced_displayed_field)
|
||||
discard;
|
||||
|
||||
o_col0 = u_fill_color;
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateCopyFragmentShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -767,7 +787,7 @@ std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit, bo
|
|||
int2 icoords = int2(v_pos.xy);
|
||||
|
||||
#if INTERLACED
|
||||
if (((icoords.y / RESOLUTION_SCALE) & 1) != u_field_offset)
|
||||
if (((fixYCoord(icoords.y) / RESOLUTION_SCALE) & 1) != u_field_offset)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ public:
|
|||
std::string GenerateBatchLineExpandGeometryShader();
|
||||
std::string GenerateScreenQuadVertexShader();
|
||||
std::string GenerateFillFragmentShader();
|
||||
std::string GenerateInterlacedFillFragmentShader();
|
||||
std::string GenerateCopyFragmentShader();
|
||||
std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced);
|
||||
std::string GenerateVRAMReadFragmentShader();
|
||||
|
|
|
@ -574,11 +574,8 @@ void GPU_SW::ShadePixel(u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 tex
|
|||
if ((bg_color.bits & mask_and) != 0)
|
||||
return;
|
||||
|
||||
if (m_GPUSTAT.SkipDrawingToActiveField() &&
|
||||
BoolToUInt32(m_GPUSTAT.displaying_odd_line) == (static_cast<u32>(y) & 1u))
|
||||
{
|
||||
if (IsInterlacedRenderingEnabled() && GetInterlacedField() == (static_cast<u32>(y) & 1u))
|
||||
return;
|
||||
}
|
||||
|
||||
SetPixel(static_cast<u32>(x), static_cast<u32>(y), color.bits | m_GPUSTAT.GetMaskOR());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue