From 2ffcad3d370f6ad65d49b615371694caf2025bef Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 26 Apr 2020 01:10:46 +1000 Subject: [PATCH] GPU: Add method to convert from window coordinates to beam ticks/lines --- src/core/gpu.cpp | 113 ++++++++++++++++++++++---------------- src/core/gpu.h | 7 +++ src/core/host_display.cpp | 62 +++++++++++++++------ src/core/host_display.h | 11 ++++ 4 files changed, 131 insertions(+), 62 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 2ca9234a1..d76b904ff 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -142,9 +142,13 @@ bool GPU::DoState(StateWrapper& sw) 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_active_start); + sw.Do(&m_crtc_state.horizontal_active_end); sw.Do(&m_crtc_state.horizontal_display_start); sw.Do(&m_crtc_state.horizontal_display_end); sw.Do(&m_crtc_state.vertical_total); + sw.Do(&m_crtc_state.vertical_active_start); + sw.Do(&m_crtc_state.vertical_active_end); sw.Do(&m_crtc_state.vertical_display_start); sw.Do(&m_crtc_state.vertical_display_end); sw.Do(&m_crtc_state.fractional_ticks); @@ -433,33 +437,31 @@ void GPU::UpdateCRTCDisplayParameters() const u16 vertical_display_start = std::min(cs.regs.Y1, vertical_total); const u16 vertical_display_end = std::min(cs.regs.Y2, vertical_total); - u16 horizontal_visible_start_tick, horizontal_visible_end_tick; - u16 vertical_visible_start_line, vertical_visible_end_line; if (m_GPUSTAT.pal_mode) { // TODO: Verify PAL numbers. switch (crop_mode) { case DisplayCropMode::None: - horizontal_visible_start_tick = 487; - horizontal_visible_end_tick = 3282; - vertical_visible_start_line = 20; - vertical_visible_end_line = 308; + cs.horizontal_active_start = 487; + cs.horizontal_active_end = 3282; + cs.vertical_active_start = 20; + cs.vertical_active_end = 308; break; case DisplayCropMode::Overscan: - horizontal_visible_start_tick = 628; - horizontal_visible_end_tick = 3188; - vertical_visible_start_line = 30; - vertical_visible_end_line = 298; + cs.horizontal_active_start = 628; + cs.horizontal_active_end = 3188; + cs.vertical_active_start = 30; + cs.vertical_active_end = 298; break; case DisplayCropMode::Borders: default: - horizontal_visible_start_tick = horizontal_display_start; - horizontal_visible_end_tick = horizontal_display_end; - vertical_visible_start_line = vertical_display_start; - vertical_visible_end_line = vertical_display_end; + cs.horizontal_active_start = horizontal_display_start; + cs.horizontal_active_end = horizontal_display_end; + cs.vertical_active_start = vertical_display_start; + cs.vertical_active_end = vertical_display_end; break; } } @@ -468,25 +470,25 @@ void GPU::UpdateCRTCDisplayParameters() switch (crop_mode) { case DisplayCropMode::None: - horizontal_visible_start_tick = 488; - horizontal_visible_end_tick = 3288; - vertical_visible_start_line = 16; - vertical_visible_end_line = 256; + cs.horizontal_active_start = 488; + cs.horizontal_active_end = 3288; + cs.vertical_active_start = 16; + cs.vertical_active_end = 256; break; case DisplayCropMode::Overscan: - horizontal_visible_start_tick = 608; - horizontal_visible_end_tick = 3168; - vertical_visible_start_line = 24; - vertical_visible_end_line = 248; + cs.horizontal_active_start = 608; + cs.horizontal_active_end = 3168; + cs.vertical_active_start = 24; + cs.vertical_active_end = 248; break; case DisplayCropMode::Borders: default: - horizontal_visible_start_tick = horizontal_display_start; - horizontal_visible_end_tick = horizontal_display_end; - vertical_visible_start_line = vertical_display_start; - vertical_visible_end_line = vertical_display_end; + cs.horizontal_active_start = horizontal_display_start; + cs.horizontal_active_end = horizontal_display_end; + cs.vertical_active_start = vertical_display_start; + cs.vertical_active_end = vertical_display_end; break; } } @@ -495,62 +497,60 @@ void GPU::UpdateCRTCDisplayParameters() // Determine screen size. cs.display_width = std::max( - ((horizontal_visible_end_tick - horizontal_visible_start_tick) + (cs.dot_clock_divider - 1)) / cs.dot_clock_divider, - 1u); - cs.display_height = std::max((vertical_visible_end_line - vertical_visible_start_line) << height_shift, 1u); + ((cs.horizontal_active_end - cs.horizontal_active_start) + (cs.dot_clock_divider - 1)) / cs.dot_clock_divider, 1u); + cs.display_height = std::max((cs.vertical_active_end - cs.vertical_active_start) << 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 (horizontal_display_start >= horizontal_visible_start_tick) + if (horizontal_display_start >= cs.horizontal_active_start) { - cs.display_origin_left = (horizontal_display_start - horizontal_visible_start_tick) / cs.dot_clock_divider; + cs.display_origin_left = (horizontal_display_start - cs.horizontal_active_start) / 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_visible_start_tick - horizontal_display_start) / cs.dot_clock_divider), + m_crtc_state.regs.X + ((cs.horizontal_active_start - horizontal_display_start) / cs.dot_clock_divider), VRAM_WIDTH - 1); } - if (horizontal_display_end <= horizontal_visible_end_tick) + if (horizontal_display_end <= cs.horizontal_active_end) { cs.display_vram_width = - std::max((((horizontal_display_end - std::max(horizontal_display_start, horizontal_visible_start_tick)) + + std::max((((horizontal_display_end - std::max(horizontal_display_start, cs.horizontal_active_start)) + (cs.dot_clock_divider - 1)) / cs.dot_clock_divider), 1u); } else { - cs.display_vram_width = std::max( - (((horizontal_visible_end_tick - std::max(horizontal_display_start, horizontal_visible_start_tick)) + - (cs.dot_clock_divider - 1)) / - cs.dot_clock_divider), - 1u); + cs.display_vram_width = + std::max((((cs.horizontal_active_end - std::max(horizontal_display_start, cs.horizontal_active_start)) + + (cs.dot_clock_divider - 1)) / + cs.dot_clock_divider), + 1u); } - if (vertical_display_start >= vertical_visible_start_line) + if (vertical_display_start >= cs.vertical_active_start) { - cs.display_origin_top = (vertical_display_start - vertical_visible_start_line) << height_shift; + cs.display_origin_top = (vertical_display_start - cs.vertical_active_start) << height_shift; cs.display_vram_top = m_crtc_state.regs.Y; } else { cs.display_origin_top = 0; - cs.display_vram_top = - m_crtc_state.regs.Y + ((vertical_visible_start_line - vertical_display_start) << height_shift); + cs.display_vram_top = m_crtc_state.regs.Y + ((cs.vertical_active_start - vertical_display_start) << height_shift); } - if (vertical_display_end <= vertical_visible_end_line) + if (vertical_display_end <= cs.vertical_active_end) { - cs.display_vram_height = (vertical_display_end - std::max(vertical_display_start, vertical_visible_start_line)) + cs.display_vram_height = (vertical_display_end - std::max(vertical_display_start, cs.vertical_active_start)) << height_shift; } else { - cs.display_vram_height = (vertical_visible_end_line - std::max(vertical_display_start, vertical_visible_start_line)) + cs.display_vram_height = (cs.vertical_active_end - std::max(vertical_display_start, cs.vertical_active_start)) << height_shift; } } @@ -705,6 +705,27 @@ void GPU::Execute(TickCount ticks) UpdateSliceTicks(); } +bool GPU::ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y, u32* out_tick, u32* out_line) const +{ + const auto [display_x, display_y] = m_host_display->ConvertWindowCoordinatesToDisplayCoordinates( + window_x, window_y, m_host_display->GetWindowWidth(), m_host_display->GetWindowHeight(), + m_host_display->GetDisplayTopMargin()); + Log_DebugPrintf("win %d,%d -> disp %d,%d (size %u,%u frac %f,%f)", window_x, window_y, display_x, display_y, + m_crtc_state.display_width, m_crtc_state.display_height, + static_cast(display_x) / static_cast(m_crtc_state.display_width), + static_cast(display_y) / static_cast(m_crtc_state.display_height)); + + if (display_x < 0 || static_cast(display_x) >= m_crtc_state.display_width || display_y < 0 || + static_cast(display_y) >= m_crtc_state.display_height) + { + return false; + } + + *out_line = (static_cast(display_y) >> BoolToUInt8(m_GPUSTAT.In480iMode())) + m_crtc_state.vertical_active_start; + *out_tick = (static_cast(display_x) * m_crtc_state.dot_clock_divider) + m_crtc_state.horizontal_active_start; + return true; +} + u32 GPU::ReadGPUREAD() { if (m_blitter_state != BlitterState::ReadingVRAM) diff --git a/src/core/gpu.h b/src/core/gpu.h index eec502891..8d819b9ec 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -162,6 +162,9 @@ public: // gpu_sw.cpp static std::unique_ptr CreateSoftwareRenderer(); + // Converts window coordinates into horizontal ticks and scanlines. Returns false if out of range. Used for lightguns. + bool ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y, u32* out_tick, u32* out_line) const; + protected: static TickCount GPUTicksToSystemTicks(TickCount gpu_ticks) { @@ -607,9 +610,13 @@ protected: u16 display_vram_height; u16 horizontal_total; + u16 horizontal_active_start; + u16 horizontal_active_end; u16 horizontal_display_start; u16 horizontal_display_end; u16 vertical_total; + u16 vertical_active_start; + u16 vertical_active_end; u16 vertical_display_start; u16 vertical_display_end; diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index ef7c3a208..981df7640 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -18,7 +18,9 @@ void HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) m_window_height = new_window_height; } -std::tuple HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, s32 top_margin) const +void HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, s32* out_left, s32* out_top, s32* out_width, + s32* out_height, s32* out_left_padding, s32* out_top_padding, float* out_scale, + float* out_y_scale) const { const float y_scale = (static_cast(m_display_width) / static_cast(m_display_height)) / m_display_pixel_aspect_ratio; @@ -28,38 +30,66 @@ std::tuple HostDisplay::CalculateDrawRect(s32 window_width, 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; + if (out_y_scale) + *out_y_scale = y_scale; // now fit it within the window const float window_ratio = static_cast(window_width) / static_cast(window_height); float scale; - int top_padding = 0, left_padding = 0; - if ((display_width / display_height) >= window_ratio) { // align in middle vertically scale = static_cast(window_width) / display_width; - top_padding = (window_height - top_margin - static_cast(display_height * scale)) / 2; + if (out_left_padding) + *out_left_padding = 0; + if (out_top_padding) + *out_top_padding = std::max((window_height - static_cast(display_height * scale)) / 2, 0); } else { // align in middle horizontally - scale = static_cast(window_height - top_margin) / display_height; - left_padding = (window_width - static_cast(display_width * scale)) / 2; + scale = static_cast(window_height) / display_height; + if (out_left_padding) + *out_left_padding = std::max((window_width - static_cast(display_width * scale)) / 2, 0); + if (out_top_padding) + *out_top_padding = 0; } - 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); + *out_width = static_cast(active_width * scale); + *out_height = static_cast(active_height * scale); + *out_left = static_cast(active_left * scale); + *out_top = static_cast(active_top * scale); + if (out_scale) + *out_scale = scale; +} - left += std::max(left_padding, 0); - top += std::max(top_padding, 0); +std::tuple HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, s32 top_margin) const +{ + s32 left, top, width, height, left_padding, top_padding; + CalculateDrawRect(window_width, window_height - top_margin, &left, &top, &width, &height, &left_padding, &top_padding, + nullptr, nullptr); + return std::make_tuple(left + left_padding, top + top_padding + top_margin, width, height); +} - // add in margin - top += top_margin; - return std::tie(left, top, width, height); +std::tuple HostDisplay::ConvertWindowCoordinatesToDisplayCoordinates(s32 window_x, s32 window_y, + s32 window_width, s32 window_height, + s32 top_margin) const +{ + s32 left, top, width, height, left_padding, top_padding; + float scale, y_scale; + CalculateDrawRect(window_width, window_height - top_margin, &left, &top, &width, &height, &left_padding, &top_padding, + &scale, &y_scale); + + // convert coordinates to active display region, then to full display region + const float scaled_display_x = static_cast(window_x - (left_padding)); + const float scaled_display_y = static_cast(window_y - (top_padding + top_margin)); + + // scale back to internal resolution + const float display_x = scaled_display_x / scale; + const float display_y = scaled_display_y / scale / y_scale; + + return std::make_tuple(static_cast(display_x), static_cast(display_y)); } bool HostDisplay::WriteTextureToFile(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, diff --git a/src/core/host_display.h b/src/core/host_display.h index a2314a0d2..013ad3988 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -30,6 +30,9 @@ public: virtual ~HostDisplay(); + ALWAYS_INLINE s32 GetWindowWidth() const { return m_window_width; } + ALWAYS_INLINE s32 GetWindowHeight() const { return m_window_height; } + virtual RenderAPI GetRenderAPI() const = 0; virtual void* GetRenderDevice() const = 0; virtual void* GetRenderContext() const = 0; @@ -96,6 +99,10 @@ public: /// Helper function for computing the draw rectangle in a larger window. std::tuple CalculateDrawRect(s32 window_width, s32 window_height, s32 top_margin) const; + /// Helper function for converting window coordinates to display coordinates. + std::tuple ConvertWindowCoordinatesToDisplayCoordinates(s32 window_x, s32 window_y, s32 window_width, + s32 window_height, s32 top_margin) const; + /// Helper function to save texture data to a PNG. If flip_y is set, the image will be flipped aka OpenGL. bool WriteTextureToFile(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, const char* filename, bool clear_alpha = true, bool flip_y = false, u32 resize_width = 0, u32 resize_height = 0); @@ -108,6 +115,10 @@ public: bool clear_alpha = true); protected: + void CalculateDrawRect(s32 window_width, s32 window_height, s32* out_left, s32* out_top, s32* out_width, + s32* out_height, s32* out_left_padding, s32* out_top_padding, float* out_scale, + float* out_y_scale) const; + s32 m_window_width = 0; s32 m_window_height = 0;