GPU: Implement interlaced rendering in hardware backends

This commit is contained in:
Connor McLaughlin 2020-04-04 00:11:33 +10:00
parent bb3c0a2ccc
commit 2aecb570c1
13 changed files with 123 additions and 89 deletions

View File

@ -627,12 +627,12 @@ void GPU::Execute(TickCount ticks)
// alternating even line bit in 240-line mode
if (m_GPUSTAT.In480iMode())
{
m_GPUSTAT.drawing_even_line =
ConvertToBoolUnchecked((m_crtc_state.regs.Y + BoolToUInt32(!m_GPUSTAT.interlaced_field)) & u32(1));
m_GPUSTAT.displaying_odd_line =
ConvertToBoolUnchecked((m_crtc_state.regs.Y + BoolToUInt32(m_GPUSTAT.interlaced_field)) & u32(1));
}
else
{
m_GPUSTAT.drawing_even_line =
m_GPUSTAT.displaying_odd_line =
ConvertToBoolUnchecked((m_crtc_state.regs.Y + m_crtc_state.current_scanline) & u32(1));
}
@ -995,6 +995,9 @@ void GPU::SetDrawMode(u16 value)
m_draw_mode.mode_reg.bits = new_mode_reg.bits;
if (m_GPUSTAT.draw_to_displayed_field != new_mode_reg.draw_to_displayed_field)
FlushRender();
// Bits 0..10 are returned in the GPU status register.
m_GPUSTAT.bits =
(m_GPUSTAT.bits & ~(DrawMode::Reg::GPUSTAT_MASK)) | (ZeroExtend32(new_mode_reg.bits) & DrawMode::Reg::GPUSTAT_MASK);
@ -1124,7 +1127,7 @@ void GPU::DrawDebugStateWindow()
ImGui::Text("Vertical Interlace: %s (%s field)", m_GPUSTAT.vertical_interlace ? "Yes" : "No",
m_GPUSTAT.interlaced_field ? "odd" : "even");
ImGui::Text("Display Disable: %s", m_GPUSTAT.display_disable ? "Yes" : "No");
ImGui::Text("Drawing Even Line: %s", m_GPUSTAT.drawing_even_line ? "Yes" : "No");
ImGui::Text("Displaying Odd Line/Field: %s", m_GPUSTAT.displaying_odd_line ? "Yes" : "No");
ImGui::Text("Color Depth: %u-bit", m_GPUSTAT.display_area_color_depth_24 ? 24 : 15);
ImGui::Text("Start Offset: (%u, %u)", cs.regs.X.GetValue(), cs.regs.Y.GetValue());
ImGui::Text("Display Total: %u (%u) horizontal, %u vertical", cs.horizontal_total,
@ -1145,7 +1148,7 @@ void GPU::DrawDebugStateWindow()
if (ImGui::CollapsingHeader("GPU", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Text("Dither: %s", m_GPUSTAT.dither_enable ? "Enabled" : "Disabled");
ImGui::Text("Draw To Display Area: %s", m_GPUSTAT.draw_to_display_area ? "Enabled" : "Disabled");
ImGui::Text("Draw To Displayed Field: %s", m_GPUSTAT.draw_to_displayed_field ? "Enabled" : "Disabled");
ImGui::Text("Draw Set Mask Bit: %s", m_GPUSTAT.set_mask_while_drawing ? "Yes" : "No");
ImGui::Text("Draw To Masked Pixels: %s", m_GPUSTAT.check_mask_before_draw ? "Yes" : "No");
ImGui::Text("Reverse Flag: %s", m_GPUSTAT.reverse_flag ? "Yes" : "No");

View File

@ -377,7 +377,7 @@ protected:
BitField<u32, TransparencyMode, 5, 2> semi_transparency_mode;
BitField<u32, TextureMode, 7, 2> texture_color_mode;
BitField<u32, bool, 9, 1> dither_enable;
BitField<u32, bool, 10, 1> draw_to_display_area;
BitField<u32, bool, 10, 1> draw_to_displayed_field;
BitField<u32, bool, 11, 1> set_mask_while_drawing;
BitField<u32, bool, 12, 1> check_mask_before_draw;
BitField<u32, bool, 13, 1> interlaced_field;
@ -396,7 +396,7 @@ protected:
BitField<u32, bool, 27, 1> ready_to_send_vram;
BitField<u32, bool, 28, 1> ready_to_recieve_dma;
BitField<u32, DMADirection, 29, 2> dma_direction;
BitField<u32, bool, 31, 1> drawing_even_line;
BitField<u32, bool, 31, 1> displaying_odd_line;
bool IsMaskingEnabled() const
{
@ -452,7 +452,7 @@ protected:
BitField<u16, TransparencyMode, 5, 2> transparency_mode;
BitField<u16, TextureMode, 7, 2> texture_mode;
BitField<u16, bool, 9, 1> dither_enable;
BitField<u16, bool, 10, 1> draw_to_display_area;
BitField<u16, bool, 10, 1> draw_to_displayed_field;
BitField<u16, bool, 11, 1> texture_disable;
BitField<u16, bool, 12, 1> texture_x_flip;
BitField<u16, bool, 13, 1> texture_y_flip;

View File

@ -541,6 +541,14 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32
m_batch_ubo_dirty = true;
}
m_batch.interlacing = m_GPUSTAT.SkipDrawingToActiveField();
if (m_batch.interlacing)
{
const u32 displayed_field = BoolToUInt32(m_GPUSTAT.displaying_odd_line);
m_batch_ubo_dirty |= (m_batch_ubo_data.u_interlaced_displayed_field != displayed_field);
m_batch_ubo_data.u_interlaced_displayed_field = displayed_field;
}
// update state
m_batch.primitive = rc_primitive;
m_batch.texture_mode = texture_mode;

View File

@ -76,6 +76,7 @@ protected:
TextureMode texture_mode;
TransparencyMode transparency_mode;
bool dithering;
bool interlacing;
bool set_mask_while_drawing;
bool check_mask_before_draw;
@ -102,7 +103,7 @@ protected:
float u_src_alpha_factor;
float u_dst_alpha_factor;
u32 u_set_mask_while_drawing;
u32 padding[1];
u32 u_interlaced_displayed_field;
};
struct RendererStats

View File

@ -349,16 +349,20 @@ bool GPU_HW_D3D11::CompileShaders()
{
for (u8 dithering = 0; dithering < 2; dithering++)
{
const std::string ps = shadergen.GenerateBatchFragmentShader(static_cast<BatchRenderMode>(render_mode),
static_cast<TextureMode>(texture_mode),
ConvertToBoolUnchecked(dithering));
for (u8 interlacing = 0; interlacing < 2; interlacing++)
{
const std::string ps = shadergen.GenerateBatchFragmentShader(
static_cast<BatchRenderMode>(render_mode), static_cast<TextureMode>(texture_mode),
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing));
m_batch_pixel_shaders[render_mode][texture_mode][dithering] = m_shader_cache.GetPixelShader(m_device.Get(), ps);
if (!m_batch_pixel_shaders[render_mode][texture_mode][dithering])
m_batch_pixel_shaders[render_mode][texture_mode][dithering][interlacing] =
m_shader_cache.GetPixelShader(m_device.Get(), ps);
if (!m_batch_pixel_shaders[render_mode][texture_mode][dithering][interlacing])
return false;
}
}
}
}
m_batch_line_expand_geometry_shader.Reset();
if (m_resolution_scale > 1)
@ -388,12 +392,12 @@ bool GPU_HW_D3D11::CompileShaders()
for (u8 depth_24bit = 0; depth_24bit < 2; depth_24bit++)
{
for (u8 interlaced = 0; interlaced < 2; interlaced++)
for (u8 interlacing = 0; interlacing < 2; interlacing++)
{
const std::string ps = shadergen.GenerateDisplayFragmentShader(ConvertToBoolUnchecked(depth_24bit),
ConvertToBoolUnchecked(interlaced));
m_display_pixel_shaders[depth_24bit][interlaced] = m_shader_cache.GetPixelShader(m_device.Get(), ps);
if (!m_display_pixel_shaders[depth_24bit][interlaced])
ConvertToBoolUnchecked(interlacing));
m_display_pixel_shaders[depth_24bit][interlacing] = m_shader_cache.GetPixelShader(m_device.Get(), ps);
if (!m_display_pixel_shaders[depth_24bit][interlacing])
return false;
}
}
@ -486,7 +490,7 @@ void GPU_HW_D3D11::SetDrawState(BatchRenderMode render_mode)
nullptr, 0);
m_context->PSSetShader(m_batch_pixel_shaders[static_cast<u8>(render_mode)][static_cast<u8>(m_batch.texture_mode)]
[BoolToUInt8(m_batch.dithering)]
[BoolToUInt8(m_batch.dithering)][BoolToUInt8(m_batch.interlacing)]
.Get(),
nullptr, 0);
@ -555,8 +559,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 =
(m_crtc_state.regs.Y + BoolToUInt8(interlaced && m_GPUSTAT.interlaced_field)) & 1u;
const u32 reinterpret_field_offset = BoolToUInt32(m_GPUSTAT.displaying_odd_line);
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};

View File

@ -103,8 +103,8 @@ private:
std::array<ComPtr<ID3D11BlendState>, 5> m_batch_blend_states; // [transparency_mode]
ComPtr<ID3D11InputLayout> m_batch_input_layout;
std::array<ComPtr<ID3D11VertexShader>, 2> m_batch_vertex_shaders; // [textured]
std::array<std::array<std::array<ComPtr<ID3D11PixelShader>, 2>, 9>, 4>
m_batch_pixel_shaders; // [render_mode][texture_mode][dithering]
std::array<std::array<std::array<std::array<ComPtr<ID3D11PixelShader>, 2>, 2>, 9>, 4>
m_batch_pixel_shaders; // [render_mode][texture_mode][dithering][interlacing]
ComPtr<ID3D11GeometryShader> m_batch_line_expand_geometry_shader;
ComPtr<ID3D11VertexShader> m_screen_quad_vertex_shader;

View File

@ -301,12 +301,14 @@ bool GPU_HW_OpenGL::CompilePrograms()
for (u32 texture_mode = 0; texture_mode < 9; texture_mode++)
{
for (u8 dithering = 0; dithering < 2; dithering++)
{
for (u8 interlacing = 0; interlacing < 2; interlacing++)
{
const bool textured = (static_cast<TextureMode>(texture_mode) != TextureMode::Disabled);
const std::string vs = shadergen.GenerateBatchVertexShader(textured);
const std::string fs = shadergen.GenerateBatchFragmentShader(static_cast<BatchRenderMode>(render_mode),
static_cast<TextureMode>(texture_mode),
ConvertToBoolUnchecked(dithering));
const std::string fs = shadergen.GenerateBatchFragmentShader(
static_cast<BatchRenderMode>(render_mode), static_cast<TextureMode>(texture_mode),
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing));
std::optional<GL::Program> prog = m_shader_cache.GetProgram(vs, fs, [this, textured](GL::Program& prog) {
prog.BindAttribute(0, "a_pos");
@ -330,7 +332,8 @@ bool GPU_HW_OpenGL::CompilePrograms()
prog->Uniform1i("samp0", 0);
}
m_render_programs[render_mode][texture_mode][dithering] = std::move(*prog);
m_render_programs[render_mode][texture_mode][dithering][interlacing] = std::move(*prog);
}
}
}
}
@ -406,7 +409,7 @@ bool GPU_HW_OpenGL::CompilePrograms()
void GPU_HW_OpenGL::SetDrawState(BatchRenderMode render_mode)
{
const GL::Program& prog = m_render_programs[static_cast<u8>(render_mode)][static_cast<u8>(m_batch.texture_mode)]
[BoolToUInt8(m_batch.dithering)];
[BoolToUInt8(m_batch.dithering)][BoolToUInt8(m_batch.interlacing)];
prog.Bind();
if (m_batch.texture_mode != TextureMode::Disabled)
@ -513,8 +516,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 =
(m_crtc_state.regs.Y + BoolToUInt8(interlaced && m_GPUSTAT.interlaced_field)) & 1u;
const u32 reinterpret_field_offset = BoolToUInt32(m_GPUSTAT.displaying_odd_line);
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};

View File

@ -1,8 +1,8 @@
#pragma once
#include "common/gl/program.h"
#include "common/gl/shader_cache.h"
#include "common/gl/stream_buffer.h"
#include "common/gl/texture.h"
#include "common/gl/shader_cache.h"
#include "glad.h"
#include "gpu_hw.h"
#include <array>
@ -76,7 +76,8 @@ private:
std::unique_ptr<GL::StreamBuffer> m_texture_stream_buffer;
GLuint m_texture_buffer_r16ui_texture = 0;
std::array<std::array<std::array<GL::Program, 2>, 9>, 4> m_render_programs; // [render_mode][texture_mode][dithering]
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_read_program;
GL::Program m_vram_write_program;

View File

@ -196,12 +196,14 @@ bool GPU_HW_OpenGL_ES::CompilePrograms()
for (u32 texture_mode = 0; texture_mode < 9; texture_mode++)
{
for (u8 dithering = 0; dithering < 2; dithering++)
{
for (u8 interlacing = 0; interlacing < 2; interlacing++)
{
const bool textured = (static_cast<TextureMode>(texture_mode) != TextureMode::Disabled);
const std::string vs = shadergen.GenerateBatchVertexShader(textured);
const std::string fs = shadergen.GenerateBatchFragmentShader(static_cast<BatchRenderMode>(render_mode),
static_cast<TextureMode>(texture_mode),
ConvertToBoolUnchecked(dithering));
const std::string fs = shadergen.GenerateBatchFragmentShader(
static_cast<BatchRenderMode>(render_mode), static_cast<TextureMode>(texture_mode),
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing));
std::optional<GL::Program> prog = m_shader_cache.GetProgram(vs, fs, [this, textured](GL::Program& prog) {
prog.BindAttribute(0, "a_pos");
@ -221,11 +223,13 @@ bool GPU_HW_OpenGL_ES::CompilePrograms()
prog->RegisterUniform("u_src_alpha_factor");
prog->RegisterUniform("u_dst_alpha_factor");
prog->RegisterUniform("u_set_mask_while_drawing");
prog->RegisterUniform("u_interlaced_displayed_field");
if (textured)
prog->Uniform1i("samp0", 0);
m_render_programs[render_mode][texture_mode][dithering] = std::move(*prog);
m_render_programs[render_mode][texture_mode][dithering][interlacing] = std::move(*prog);
}
}
}
}
@ -277,7 +281,7 @@ void GPU_HW_OpenGL_ES::SetVertexPointers()
void GPU_HW_OpenGL_ES::SetDrawState(BatchRenderMode render_mode)
{
const GL::Program& prog = m_render_programs[static_cast<u8>(render_mode)][static_cast<u8>(m_batch.texture_mode)]
[BoolToUInt8(m_batch.dithering)];
[BoolToUInt8(m_batch.dithering)][BoolToUInt8(m_batch.interlacing)];
m_batch_ubo_dirty |= !prog.IsBound();
prog.Bind();
@ -311,6 +315,7 @@ void GPU_HW_OpenGL_ES::SetDrawState(BatchRenderMode render_mode)
prog.Uniform1f(2, m_batch_ubo_data.u_src_alpha_factor);
prog.Uniform1f(3, m_batch_ubo_data.u_dst_alpha_factor);
prog.Uniform1i(4, static_cast<s32>(m_batch_ubo_data.u_set_mask_while_drawing));
prog.Uniform1i(5, static_cast<s32>(m_batch_ubo_data.u_interlaced_displayed_field));
m_batch_ubo_dirty = false;
}
}
@ -380,8 +385,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 =
(m_crtc_state.regs.Y + BoolToUInt8(interlaced && m_GPUSTAT.interlaced_field)) & 1u;
const u32 reinterpret_field_offset = BoolToUInt32(m_GPUSTAT.displaying_odd_line);
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);

View File

@ -1,8 +1,8 @@
#pragma once
#include "common/gl/program.h"
#include "common/gl/shader_cache.h"
#include "common/gl/stream_buffer.h"
#include "common/gl/texture.h"
#include "common/gl/shader_cache.h"
#include "glad.h"
#include "gpu_hw.h"
#include <array>
@ -64,7 +64,8 @@ private:
std::vector<BatchVertex> m_vertex_buffer;
std::array<std::array<std::array<GL::Program, 2>, 9>, 4> m_render_programs; // [render_mode][texture_mode][dithering]
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_read_program;
};

View File

@ -345,7 +345,8 @@ void GPU_HW_ShaderGen::DeclareFragmentEntryPoint(std::stringstream& ss, u32 num_
void GPU_HW_ShaderGen::WriteBatchUniformBuffer(std::stringstream& ss)
{
DeclareUniformBuffer(ss, {"uint2 u_texture_window_mask", "uint2 u_texture_window_offset", "float u_src_alpha_factor",
"float u_dst_alpha_factor", "bool u_set_mask_while_drawing"});
"float u_dst_alpha_factor", "bool u_set_mask_while_drawing",
"int u_interlaced_displayed_field"});
}
std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(bool textured)
@ -394,7 +395,8 @@ std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(bool textured)
}
std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMode transparency,
GPU::TextureMode texture_mode, bool dithering)
GPU::TextureMode texture_mode, bool dithering,
bool interlacing)
{
const GPU::TextureMode actual_texture_mode = texture_mode & ~GPU::TextureMode::RawTextureBit;
const bool raw_texture = (texture_mode & GPU::TextureMode::RawTextureBit) == GPU::TextureMode::RawTextureBit;
@ -416,6 +418,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMod
DefineMacro(ss, "RAW_TEXTURE", raw_texture);
DefineMacro(ss, "DITHERING", dithering);
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, "USE_DUAL_SOURCE", use_dual_source);
@ -519,6 +522,11 @@ float4 SampleFromVRAM(int4 texpage, int2 icoord)
float ialpha;
float oalpha;
#if INTERLACING
if (((int(v_pos.y) / RESOLUTION_SCALE) & 1) == u_interlaced_displayed_field)
discard;
#endif
#if TEXTURED
#if TEXTURE_FILTERING
int2 icoord = int2(v_tex0);

View File

@ -13,7 +13,7 @@ public:
std::string GenerateBatchVertexShader(bool textured);
std::string GenerateBatchFragmentShader(GPU_HW::BatchRenderMode transparency, GPU::TextureMode texture_mode,
bool dithering);
bool dithering, bool interlacing);
std::string GenerateBatchLineExpandGeometryShader();
std::string GenerateScreenQuadVertexShader();
std::string GenerateFillFragmentShader();

View File

@ -574,8 +574,11 @@ 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.drawing_even_line) != (static_cast<u32>(y) & 1u))
if (m_GPUSTAT.SkipDrawingToActiveField() &&
BoolToUInt32(m_GPUSTAT.displaying_odd_line) == (static_cast<u32>(y) & 1u))
{
return;
}
SetPixel(static_cast<u32>(x), static_cast<u32>(y), color.bits | m_GPUSTAT.GetMaskOR());
}