From 148279e2f2effd757972dec851e9ecdae5a5fff4 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 29 Mar 2020 01:14:37 +1000 Subject: [PATCH] GPU: Rewrite CRTC display modeling and overscan handling --- src/core/gpu.cpp | 256 +++++++++++++++++++++------------- src/core/gpu.h | 41 +++--- src/core/gpu_hw.cpp | 2 +- src/core/gpu_hw_d3d11.cpp | 17 +-- src/core/gpu_hw_opengl.cpp | 17 +-- src/core/gpu_hw_opengl_es.cpp | 17 +-- src/core/gpu_sw.cpp | 17 +-- src/core/host_display.cpp | 78 ++++++++--- src/core/host_display.h | 18 ++- 9 files changed, 287 insertions(+), 176 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 1ae012e6d..be63616f9 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -36,6 +36,9 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr void GPU::UpdateSettings() { m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan; + + // Crop mode calls this, so recalculate the display area + UpdateCRTCDisplayParameters(); } void GPU::Reset() @@ -107,12 +110,14 @@ bool GPU::DoState(StateWrapper& sw) sw.Do(&m_crtc_state.regs.horizontal_display_range); sw.Do(&m_crtc_state.regs.vertical_display_range); sw.Do(&m_crtc_state.dot_clock_divider); - sw.Do(&m_crtc_state.visible_display_width); - sw.Do(&m_crtc_state.visible_display_height); - sw.Do(&m_crtc_state.active_display_left); - sw.Do(&m_crtc_state.active_display_top); - sw.Do(&m_crtc_state.active_display_width); - sw.Do(&m_crtc_state.active_display_height); + sw.Do(&m_crtc_state.display_width); + sw.Do(&m_crtc_state.display_height); + sw.Do(&m_crtc_state.display_origin_left); + sw.Do(&m_crtc_state.display_origin_top); + sw.Do(&m_crtc_state.display_vram_left); + sw.Do(&m_crtc_state.display_vram_top); + sw.Do(&m_crtc_state.display_vram_width); + sw.Do(&m_crtc_state.display_vram_height); sw.Do(&m_crtc_state.horizontal_total); sw.Do(&m_crtc_state.horizontal_display_start); sw.Do(&m_crtc_state.horizontal_display_end); @@ -319,7 +324,6 @@ void GPU::UpdateCRTCConfig() { static constexpr std::array dot_clock_dividers = {{10, 8, 5, 4, 7, 7, 7, 7}}; CRTCState& cs = m_crtc_state; - const DisplayCropMode crop_mode = m_system->GetSettings().display_crop_mode; if (m_GPUSTAT.pal_mode) { @@ -343,99 +347,154 @@ void GPU::UpdateCRTCConfig() const u8 horizontal_resolution_index = m_GPUSTAT.horizontal_resolution_1 | (m_GPUSTAT.horizontal_resolution_2 << 2); cs.dot_clock_divider = dot_clock_dividers[horizontal_resolution_index]; - cs.horizontal_display_start = static_cast(std::min(cs.regs.X1, cs.horizontal_total)); - cs.horizontal_display_end = static_cast(std::min(cs.regs.X2, cs.horizontal_total)); - cs.vertical_display_start = static_cast(std::min(cs.regs.Y1, cs.vertical_total)); - cs.vertical_display_end = static_cast(std::min(cs.regs.Y2, cs.vertical_total)); + cs.horizontal_display_start = std::min(cs.regs.X1, cs.horizontal_total); + cs.horizontal_display_end = std::min(cs.regs.X2, cs.horizontal_total); + cs.vertical_display_start = std::min(cs.regs.Y1, cs.vertical_total); + cs.vertical_display_end = std::min(cs.regs.Y2, cs.vertical_total); - // determine the active display size - cs.active_display_width = std::clamp((cs.regs.X2 - cs.regs.X1) / cs.dot_clock_divider, 1, VRAM_WIDTH); - cs.active_display_height = - std::clamp((cs.regs.Y2 - cs.regs.Y1), 1, VRAM_HEIGHT >> BoolToUInt8(m_GPUSTAT.In480iMode())); + UpdateCRTCDisplayParameters(); + UpdateSliceTicks(); +} - // Construct screen borders from configured active area and the standard visible range. - // TODO: Ensure it doesn't overflow - const u16 horizontal_start_display_tick = (crop_mode == DisplayCropMode::None ? 488 : 608); - const u16 horizontal_end_display_tick = (crop_mode == DisplayCropMode::None ? 2800 : 2560); - cs.visible_display_width = horizontal_end_display_tick / cs.dot_clock_divider; - cs.active_display_left = - (std::max(m_crtc_state.regs.X1, horizontal_start_display_tick) - horizontal_start_display_tick) / - cs.dot_clock_divider; +void GPU::UpdateCRTCDisplayParameters() +{ + CRTCState& cs = m_crtc_state; + const DisplayCropMode crop_mode = m_system->GetSettings().display_crop_mode; - const u16 vertical_start_display_line = (crop_mode == DisplayCropMode::None ? 8 : (m_GPUSTAT.pal_mode ? 20 : 16)); - const u16 vertical_end_display_line = - (crop_mode == DisplayCropMode::None ? static_cast(cs.vertical_total) : - static_cast(m_GPUSTAT.pal_mode ? 308 : 256)); - const u16 bottom_padding = vertical_end_display_line - std::min(m_crtc_state.regs.Y2, vertical_end_display_line); - cs.active_display_top = - std::max(m_crtc_state.regs.Y1, vertical_start_display_line) - vertical_start_display_line; - cs.visible_display_height = cs.active_display_top + cs.active_display_height + bottom_padding; + u16 horizontal_display_start_tick, horizontal_display_end_tick; + u16 vertical_display_start_line, vertical_display_end_line; + if (m_GPUSTAT.pal_mode) + { + // TODO: Verify PAL numbers. + switch (crop_mode) + { + case DisplayCropMode::None: + horizontal_display_start_tick = 487; + horizontal_display_end_tick = 3282; + vertical_display_start_line = 12; + vertical_display_end_line = 312; + break; + + case DisplayCropMode::Overscan: + horizontal_display_start_tick = 628; + horizontal_display_end_tick = 3188; + vertical_display_start_line = 19; + vertical_display_end_line = 307; + break; + + case DisplayCropMode::Borders: + default: + horizontal_display_start_tick = m_crtc_state.horizontal_display_start; + horizontal_display_end_tick = m_crtc_state.horizontal_display_end; + vertical_display_start_line = m_crtc_state.vertical_display_start; + vertical_display_end_line = m_crtc_state.vertical_display_end; + break; + } + } + else + { + switch (crop_mode) + { + case DisplayCropMode::None: + horizontal_display_start_tick = 488; + horizontal_display_end_tick = 3288; + vertical_display_start_line = 8; + vertical_display_end_line = 260; + break; + + case DisplayCropMode::Overscan: + horizontal_display_start_tick = 608; + horizontal_display_end_tick = 3168; + vertical_display_start_line = 16; + vertical_display_end_line = 256; + break; + + case DisplayCropMode::Borders: + default: + horizontal_display_start_tick = m_crtc_state.horizontal_display_start; + horizontal_display_end_tick = m_crtc_state.horizontal_display_end; + vertical_display_start_line = m_crtc_state.vertical_display_start; + vertical_display_end_line = m_crtc_state.vertical_display_end; + break; + } + } + + const u8 height_shift = BoolToUInt8(m_GPUSTAT.In480iMode()); + + // Determine screen size. + cs.display_width = std::max( + ((horizontal_display_end_tick - horizontal_display_start_tick) + (cs.dot_clock_divider - 1)) / cs.dot_clock_divider, + 1u); + cs.display_height = std::max((vertical_display_end_line - vertical_display_start_line) << height_shift, 1u); + + // Determine if we need to adjust the VRAM rectangle (because the display is starting outside the visible area) or add + // padding. + if (cs.horizontal_display_start >= horizontal_display_start_tick) + { + cs.display_origin_left = (cs.horizontal_display_start - horizontal_display_start_tick) / cs.dot_clock_divider; + cs.display_vram_left = m_crtc_state.regs.X; + } + else + { + cs.display_origin_left = 0; + cs.display_vram_left = std::min( + m_crtc_state.regs.X + ((horizontal_display_start_tick - cs.horizontal_display_start) / cs.dot_clock_divider), + VRAM_WIDTH - 1); + + // for 24-bit scanout we must stay aligned + if (m_GPUSTAT.display_area_color_depth_24 && ((cs.display_vram_left - cs.regs.X) & 1u)) + cs.display_vram_left--; + } + + if (cs.horizontal_display_end <= horizontal_display_end_tick) + { + cs.display_vram_width = std::min( + std::max( + (((cs.horizontal_display_end - std::max(cs.horizontal_display_start, horizontal_display_start_tick)) + + (cs.dot_clock_divider - 1)) / + cs.dot_clock_divider), + 1u), + VRAM_WIDTH - cs.display_vram_left); + } + else + { + cs.display_vram_width = std::min( + std::max( + (((horizontal_display_end_tick - std::max(cs.horizontal_display_start, horizontal_display_start_tick)) + + (cs.dot_clock_divider - 1)) / + cs.dot_clock_divider), + 1u), + VRAM_WIDTH - cs.display_vram_left); + } + + if (cs.vertical_display_start >= vertical_display_start_line) + { + cs.display_origin_top = (cs.vertical_display_start - vertical_display_start_line) << height_shift; + cs.display_vram_top = m_crtc_state.regs.Y; + } + else + { + cs.display_origin_top = 0; + cs.display_vram_top = + std::min(m_crtc_state.regs.Y + ((cs.vertical_display_start - vertical_display_start_line) << height_shift), + VRAM_HEIGHT - 1); + } + + if (cs.vertical_display_end <= vertical_display_end_line) + { + cs.display_vram_height = std::min( + (cs.vertical_display_end - std::max(cs.vertical_display_start, vertical_display_start_line)) << height_shift, + VRAM_HEIGHT - cs.display_vram_top); + } + else + { + cs.display_vram_height = std::min( + (vertical_display_end_line - std::max(cs.vertical_display_start, vertical_display_start_line)) << height_shift, + VRAM_HEIGHT - cs.display_vram_top); + } // Aspect ratio is always 4:3. cs.display_aspect_ratio = 4.0f / 3.0f; - - if (crop_mode == DisplayCropMode::Borders) - { - // Compute the aspect ratio necessary to display borders in the inactive region of the picture. - // Convert total dots/lines to time. - const float dot_clock = - (static_cast(MASTER_CLOCK) * (11.0f / 7.0f / static_cast(cs.dot_clock_divider))); - const float dot_clock_period = 1.0f / dot_clock; - const float dots_per_scanline = static_cast(cs.horizontal_total) / static_cast(cs.dot_clock_divider); - const float horizontal_period = dots_per_scanline * dot_clock_period; - const float vertical_period = horizontal_period * static_cast(cs.vertical_total); - - // Convert active dots/lines to time. - const float visible_dots_per_scanline = static_cast(cs.active_display_width); - const float horizontal_active_time = horizontal_period * visible_dots_per_scanline; - const float vertical_active_time = horizontal_active_time * static_cast(cs.regs.Y2 - cs.regs.Y1); - - // Use the reference active time/lines for the signal to work out the border area, and thus aspect ratio - // transformation for the active area in our framebuffer. For the purposes of these calculations, we're assuming - // progressive scan. - float display_ratio; - if (m_GPUSTAT.pal_mode) - { - // Wikipedia says PAL is active 51.95us of 64.00us, and 576/625 lines. - const float signal_horizontal_active_time = 51.95f; - const float signal_horizontal_total_time = 64.0f; - const float signal_vertical_active_lines = 576.0f; - const float signal_vertical_total_lines = 625.0f; - const float h_ratio = - (horizontal_active_time / horizontal_period) * (signal_horizontal_total_time / signal_horizontal_active_time); - const float v_ratio = - (vertical_active_time / vertical_period) * (signal_vertical_total_lines / signal_vertical_active_lines); - display_ratio = h_ratio / v_ratio; - } - else - { - const float signal_horizontal_active_time = 52.66f; - const float signal_horizontal_total_time = 63.56f; - const float signal_vertical_active_lines = 486.0f; - const float signal_vertical_total_lines = 525.0f; - const float h_ratio = - (horizontal_active_time / horizontal_period) * (signal_horizontal_total_time / signal_horizontal_active_time); - const float v_ratio = - (vertical_active_time / vertical_period) * (signal_vertical_total_lines / signal_vertical_active_lines); - display_ratio = h_ratio / v_ratio; - } - - // Ensure the numbers are sane, and not due to a misconfigured active display range. - cs.display_aspect_ratio = (std::isnormal(display_ratio) && display_ratio != 0.0f) ? display_ratio : (4.0f / 3.0f); - cs.visible_display_width = cs.active_display_width; - cs.visible_display_height = cs.active_display_height; - cs.active_display_left = 0; - cs.active_display_top = 0; - } - - Log_DevPrintf("Screen resolution: %ux%u", cs.visible_display_width, cs.visible_display_height); - Log_DevPrintf("Active display: %ux%u @ %u,%u (VRAM %u,%u)", cs.active_display_width, cs.active_display_height, - cs.active_display_left, cs.active_display_top, cs.regs.X.GetValue(), cs.regs.Y.GetValue()); - Log_DevPrintf("Padding: Left=%u, Top=%u, Right=%u, Bottom=%u", cs.active_display_left, cs.active_display_top, - cs.visible_display_width - cs.active_display_width - cs.active_display_left, - cs.visible_display_height - cs.active_display_height - cs.active_display_top); - - UpdateSliceTicks(); } TickCount GPU::GetPendingGPUTicks() const @@ -679,6 +738,7 @@ void GPU::WriteGP1(u32 value) m_crtc_state.regs.display_address_start = param & CRTCState::Regs::DISPLAY_ADDRESS_START_MASK; Log_DebugPrintf("Display address start <- 0x%08X", m_crtc_state.regs.display_address_start); m_system->IncrementInternalFrameNumber(); + UpdateCRTCDisplayParameters(); } break; @@ -1065,7 +1125,6 @@ void GPU::DrawDebugStateWindow() 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("Display Resolution: %ux%u", cs.active_display_width, cs.active_display_height); 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, @@ -1074,6 +1133,13 @@ void GPU::DrawDebugStateWindow() cs.regs.X1.GetValue() / cs.dot_clock_divider, cs.regs.X2.GetValue() / cs.dot_clock_divider, cs.regs.Y1.GetValue(), cs.regs.Y2.GetValue()); ImGui::Text("Current Scanline: %u (tick %u)", cs.current_scanline, cs.current_tick_in_scanline); + ImGui::Text("Display resolution: %ux%u", cs.display_width, cs.display_height); + ImGui::Text("Display origin: %u, %u", cs.display_origin_left, cs.display_origin_top); + ImGui::Text("Active display: %ux%u @ (%u, %u)", cs.display_vram_width, cs.display_vram_height, cs.display_vram_left, + cs.display_vram_top); + ImGui::Text("Padding: Left=%u, Top=%u, Right=%u, Bottom=%u", cs.display_origin_left, cs.display_origin_top, + cs.display_width - cs.display_vram_width - cs.display_origin_left, + cs.display_height - cs.display_vram_height - cs.display_origin_top); } if (ImGui::CollapsingHeader("GPU", ImGuiTreeNodeFlags_DefaultOpen)) diff --git a/src/core/gpu.h b/src/core/gpu.h index bc8319fac..7e3a01a10 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -313,6 +313,7 @@ protected: // Sets dots per scanline void UpdateCRTCConfig(); + void UpdateCRTCDisplayParameters(); // Update ticks for this execution slice void UpdateSliceTicks(); @@ -547,19 +548,26 @@ protected: u16 dot_clock_divider; - u16 visible_display_width; - u16 visible_display_height; - u16 active_display_left; - u16 active_display_top; - u16 active_display_width; - u16 active_display_height; + // Size of the simulated screen in pixels. Depending on crop mode, this may include overscan area. + u16 display_width; + u16 display_height; - TickCount horizontal_total; - TickCount horizontal_display_start; - TickCount horizontal_display_end; - u32 vertical_total; - u32 vertical_display_start; - u32 vertical_display_end; + // Top-left corner where the VRAM is displayed. Depending on the CRTC config, this may indicate padding. + u16 display_origin_left; + u16 display_origin_top; + + // Rectangle describing the displayed area of VRAM, in coordinates. + u16 display_vram_left; + u16 display_vram_top; + u16 display_vram_width; + u16 display_vram_height; + + u16 horizontal_total; + u16 horizontal_display_start; + u16 horizontal_display_end; + u16 vertical_total; + u16 vertical_display_start; + u16 vertical_display_end; TickCount fractional_ticks; TickCount current_tick_in_scanline; @@ -568,15 +576,6 @@ protected: float display_aspect_ratio; bool in_hblank; bool in_vblank; - - /// Returns a rectangle representing the active display region within the visible area of the screen, i.e. where the - /// VRAM texture should be "scanned out" to. Areas outside this region (the border) should be displayed as black. - Common::Rectangle GetActiveDisplayRectangle() const - { - return Common::Rectangle::FromExtents( - static_cast(ZeroExtend32(active_display_left)), static_cast(ZeroExtend32(active_display_top)), - static_cast(ZeroExtend32(active_display_width)), static_cast(ZeroExtend32(active_display_height))); - } } m_crtc_state = {}; State m_state = State::Idle; diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 7038550d9..00dcbe891 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -490,7 +490,7 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32 (m_draw_mode.GetTexturePageRectangle().Intersects(m_vram_dirty_rect) || (m_draw_mode.IsUsingPalette() && m_draw_mode.GetTexturePaletteRectangle().Intersects(m_vram_dirty_rect)))) { - Log_DevPrintf("Invalidating VRAM read cache due to drawing area overlap"); + //Log_DevPrintf("Invalidating VRAM read cache due to drawing area overlap"); if (!IsFlushed()) FlushRender(); diff --git a/src/core/gpu_hw_d3d11.cpp b/src/core/gpu_hw_d3d11.cpp index 4f3197ffa..10250b646 100644 --- a/src/core/gpu_hw_d3d11.cpp +++ b/src/core/gpu_hw_d3d11.cpp @@ -525,18 +525,17 @@ void GPU_HW_D3D11::UpdateDisplay() { m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); - m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT), + m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); } else { - const u32 vram_offset_x = m_crtc_state.regs.X; - const u32 vram_offset_y = m_crtc_state.regs.Y; + const u32 vram_offset_x = m_crtc_state.display_vram_left; + const u32 vram_offset_y = m_crtc_state.display_vram_top; const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; - const u32 display_width = std::min(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x); - const u32 display_height = std::min(m_crtc_state.active_display_height << BoolToUInt8(m_GPUSTAT.In480iMode()), - VRAM_HEIGHT - vram_offset_y); + const u32 display_width = m_crtc_state.display_vram_width; + 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(); @@ -594,8 +593,10 @@ void GPU_HW_D3D11::UpdateDisplay() RestoreGraphicsAPIState(); } - m_host_display->SetDisplayParameters(m_crtc_state.visible_display_width, m_crtc_state.visible_display_height, - m_crtc_state.GetActiveDisplayRectangle(), m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, + m_crtc_state.display_origin_left, m_crtc_state.display_origin_top, + m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, + m_crtc_state.display_aspect_ratio); } } diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index e8038cff9..534c16a08 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -479,18 +479,17 @@ void GPU_HW_OpenGL::UpdateDisplay() m_vram_texture.GetWidth(), static_cast(m_vram_texture.GetHeight()), 0, m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), -static_cast(m_vram_texture.GetHeight())); - m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT), + m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); } else { - const u32 vram_offset_x = m_crtc_state.regs.X; - const u32 vram_offset_y = m_crtc_state.regs.Y; + const u32 vram_offset_x = m_crtc_state.display_vram_left; + const u32 vram_offset_y = m_crtc_state.display_vram_top; const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; - const u32 display_width = std::min(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x); - const u32 display_height = std::min(m_crtc_state.active_display_height << BoolToUInt8(m_GPUSTAT.In480iMode()), - VRAM_HEIGHT - vram_offset_y); + const u32 display_width = m_crtc_state.display_vram_width; + 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(); @@ -572,8 +571,10 @@ void GPU_HW_OpenGL::UpdateDisplay() glEnable(GL_SCISSOR_TEST); } - m_host_display->SetDisplayParameters(m_crtc_state.visible_display_width, m_crtc_state.visible_display_height, - m_crtc_state.GetActiveDisplayRectangle(), m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, + m_crtc_state.display_origin_left, m_crtc_state.display_origin_top, + m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, + m_crtc_state.display_aspect_ratio); } } diff --git a/src/core/gpu_hw_opengl_es.cpp b/src/core/gpu_hw_opengl_es.cpp index e0399a370..900f2ab88 100644 --- a/src/core/gpu_hw_opengl_es.cpp +++ b/src/core/gpu_hw_opengl_es.cpp @@ -343,18 +343,17 @@ void GPU_HW_OpenGL_ES::UpdateDisplay() m_vram_texture.GetWidth(), static_cast(m_vram_texture.GetHeight()), 0, m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), -static_cast(m_vram_texture.GetHeight())); - m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT), + m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); } else { - const u32 vram_offset_x = m_crtc_state.regs.X; - const u32 vram_offset_y = m_crtc_state.regs.Y; + const u32 vram_offset_x = m_crtc_state.display_vram_left; + const u32 vram_offset_y = m_crtc_state.display_vram_top; const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; - const u32 display_width = std::min(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x); - const u32 display_height = std::min(m_crtc_state.active_display_height << BoolToUInt8(m_GPUSTAT.In480iMode()), - VRAM_HEIGHT - vram_offset_y); + const u32 display_width = m_crtc_state.display_vram_width; + 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(); @@ -436,8 +435,10 @@ void GPU_HW_OpenGL_ES::UpdateDisplay() glEnable(GL_SCISSOR_TEST); } - m_host_display->SetDisplayParameters(m_crtc_state.visible_display_width, m_crtc_state.visible_display_height, - m_crtc_state.GetActiveDisplayRectangle(), m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, + m_crtc_state.display_origin_left, m_crtc_state.display_origin_top, + m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, + m_crtc_state.display_aspect_ratio); } } diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 05560d2e8..75c7e0396 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -84,11 +84,10 @@ void GPU_SW::UpdateDisplay() if (!m_system->GetSettings().debugging.show_vram) { // TODO: Handle interlacing - const u32 vram_offset_x = m_crtc_state.regs.X; - const u32 vram_offset_y = m_crtc_state.regs.Y; - const u32 display_width = std::min(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x); - const u32 display_height = std::min(m_crtc_state.active_display_height << BoolToUInt8(m_GPUSTAT.In480iMode()), - VRAM_HEIGHT - vram_offset_y); + const u32 vram_offset_x = m_crtc_state.display_vram_left; + const u32 vram_offset_y = m_crtc_state.display_vram_top; + const u32 display_width = m_crtc_state.display_vram_width; + const u32 display_height = m_crtc_state.display_vram_height; if (m_GPUSTAT.display_disable) { @@ -110,8 +109,10 @@ void GPU_SW::UpdateDisplay() m_display_texture_buffer.data(), display_width * sizeof(u32)); m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), VRAM_WIDTH, VRAM_HEIGHT, 0, 0, display_width, display_height); - m_host_display->SetDisplayParameters(m_crtc_state.visible_display_width, m_crtc_state.visible_display_height, - m_crtc_state.GetActiveDisplayRectangle(), m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, + m_crtc_state.display_origin_left, m_crtc_state.display_origin_top, + m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, + m_crtc_state.display_aspect_ratio); } else { @@ -120,7 +121,7 @@ void GPU_SW::UpdateDisplay() m_display_texture_buffer.data(), VRAM_WIDTH * sizeof(u32)); m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT); - m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT), + m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); } } diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index dd9aba03b..335cf105b 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -20,35 +20,71 @@ void HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) std::tuple HostDisplay::CalculateDrawRect() const { +#if 0 + // convert display region to correct pixel aspect ratio + float display_width, display_height, active_left, active_top, active_width, active_height; + if (m_display_width >= m_display_height) + { + display_width = static_cast(m_display_width); + display_height = static_cast(m_display_width) / m_display_pixel_aspect_ratio; + + const float scale = display_height / static_cast(m_display_height); + active_left = static_cast(m_display_active_left); + active_top = static_cast(m_display_active_top) * scale; + active_width = static_cast(m_display_active_width); + active_height = static_cast(m_display_active_width) / m_display_pixel_aspect_ratio; + } + else + { + display_width = static_cast(m_display_height) * m_display_pixel_aspect_ratio; + display_height = static_cast(m_display_height); + + const float scale = display_width / static_cast(m_display_width); + active_left = static_cast(m_display_active_left) * scale; + active_top = static_cast(m_display_active_top); + active_width = static_cast(m_display_active_height) * m_display_pixel_aspect_ratio; + active_height = static_cast(m_display_active_height); + } +#else + const float y_scale = + (static_cast(m_display_width) / static_cast(m_display_height)) / m_display_pixel_aspect_ratio; + const float display_width = static_cast(m_display_width); + const float display_height = static_cast(m_display_height) * y_scale; + const float active_left = static_cast(m_display_active_left); + const float active_top = static_cast(m_display_active_top) * y_scale; + const float active_width = static_cast(m_display_active_width); + const float active_height = static_cast(m_display_active_height) * y_scale; +#endif + + // now fit it within the window const s32 window_width = m_window_width; const s32 window_height = m_window_height - m_display_top_margin; const float window_ratio = static_cast(window_width) / static_cast(window_height); float scale; - int left, top, width, height; - if (window_ratio >= m_display_aspect_ratio) + int top_padding = 0, left_padding = 0; + + if ((display_width / display_height) >= window_ratio) { - width = static_cast(static_cast(window_height) * m_display_aspect_ratio); - height = static_cast(window_height); - scale = static_cast(window_height) / static_cast(m_display_height); - left = (window_width - width) / 2; - top = 0; + // align in middle vertically + scale = static_cast(window_width) / display_width; + top_padding = (window_height - static_cast(display_height * scale)) / 2; } else { - width = static_cast(window_width); - height = static_cast(float(window_width) / m_display_aspect_ratio); - scale = static_cast(window_width) / static_cast(m_display_width); - left = 0; - top = (window_height - height) / 2; + // align in middle horizontally + scale = static_cast(window_height) / display_height; + left_padding = (window_width - static_cast(display_width * scale)) / 2; } - // add in padding - left += static_cast(static_cast(m_display_area.left) * scale); - top += static_cast(static_cast(m_display_area.top) * scale); - width -= static_cast(static_cast(m_display_area.left + (m_display_width - m_display_area.right)) * scale); - height -= - static_cast(static_cast(m_display_area.top + (m_display_height - m_display_area.bottom)) * scale); + int left, top, width, height; + width = static_cast(active_width * scale); + height = static_cast(active_height * scale); + left = static_cast(active_left * scale); + top = static_cast(active_top * scale); + + left += std::max(left_padding, 0); + top += std::max(top_padding, 0); // add in margin top += m_display_top_margin; @@ -153,15 +189,15 @@ bool HostDisplay::WriteDisplayTextureToFile(const char* filename, bool full_reso s32 resize_height = 0; if (apply_aspect_ratio && full_resolution) { - if (m_display_aspect_ratio > 1.0f) + if (m_display_pixel_aspect_ratio > 1.0f) { resize_width = m_display_texture_view_width; - resize_height = static_cast(static_cast(resize_width) / m_display_aspect_ratio); + resize_height = static_cast(static_cast(resize_width) / m_display_pixel_aspect_ratio); } else { resize_height = m_display_texture_view_height; - resize_width = static_cast(static_cast(resize_height) * m_display_aspect_ratio); + resize_width = static_cast(static_cast(resize_height) * m_display_pixel_aspect_ratio); } } else if (apply_aspect_ratio) diff --git a/src/core/host_display.h b/src/core/host_display.h index 19ad7e573..6bb969835 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -80,13 +80,16 @@ public: m_display_changed = true; } - void SetDisplayParameters(s32 display_width, s32 display_height, const Common::Rectangle& display_area, - float pixel_aspect_ratio) + void SetDisplayParameters(s32 display_width, s32 display_height, s32 active_left, s32 active_top, s32 active_width, + s32 active_height, float pixel_aspect_ratio) { m_display_width = display_width; m_display_height = display_height; - m_display_area = display_area; - m_display_aspect_ratio = pixel_aspect_ratio; + m_display_active_left = active_left; + m_display_active_top = active_top; + m_display_active_width = active_width; + m_display_active_height = active_height; + m_display_pixel_aspect_ratio = pixel_aspect_ratio; m_display_changed = true; } @@ -109,8 +112,11 @@ protected: s32 m_display_width = 0; s32 m_display_height = 0; - Common::Rectangle m_display_area{}; - float m_display_aspect_ratio = 1.0f; + s32 m_display_active_left = 0; + s32 m_display_active_top = 0; + s32 m_display_active_width = 0; + s32 m_display_active_height = 0; + float m_display_pixel_aspect_ratio = 1.0f; void* m_display_texture_handle = nullptr; s32 m_display_texture_width = 0;