From d3d881aa6b452c33792cc8d8f58436abf3fbc2a4 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 22 Oct 2020 01:25:33 +1000 Subject: [PATCH] GPU/Software: Reduce number of copies by one, enable 16-bit scanout --- src/common/d3d11/staging_texture.h | 1 + src/common/d3d11/texture.cpp | 6 +- src/common/d3d11/texture.h | 2 +- src/core/gpu_hw_d3d11.cpp | 24 +- src/core/gpu_hw_opengl.cpp | 22 +- src/core/gpu_hw_vulkan.cpp | 22 +- src/core/gpu_sw.cpp | 449 +++++++++++++++--- src/core/gpu_sw.h | 21 +- src/core/host_display.cpp | 52 +- src/core/host_display.h | 36 +- .../libretro_host_display.cpp | 166 ++++--- .../libretro_host_display.h | 15 + .../libretro_host_interface.cpp | 7 +- .../libretro_host_interface.h | 3 +- src/frontend-common/d3d11_host_display.cpp | 52 ++ src/frontend-common/d3d11_host_display.h | 4 + src/frontend-common/opengl_host_display.cpp | 109 +++++ src/frontend-common/opengl_host_display.h | 10 + src/frontend-common/vulkan_host_display.cpp | 55 +++ src/frontend-common/vulkan_host_display.h | 6 + 20 files changed, 875 insertions(+), 187 deletions(-) diff --git a/src/common/d3d11/staging_texture.h b/src/common/d3d11/staging_texture.h index 668cee2ae..04a77c163 100644 --- a/src/common/d3d11/staging_texture.h +++ b/src/common/d3d11/staging_texture.h @@ -21,6 +21,7 @@ public: ALWAYS_INLINE u32 GetHeight() const { return m_height; } ALWAYS_INLINE DXGI_FORMAT GetFormat() const { return m_format; } ALWAYS_INLINE bool IsMapped() const { return m_map.pData != nullptr; } + ALWAYS_INLINE const D3D11_MAPPED_SUBRESOURCE& GetMappedSubresource() const { return m_map; } ALWAYS_INLINE operator bool() const { return static_cast(m_texture); } diff --git a/src/common/d3d11/texture.cpp b/src/common/d3d11/texture.cpp index c4be21de3..eaddb08d5 100644 --- a/src/common/d3d11/texture.cpp +++ b/src/common/d3d11/texture.cpp @@ -29,9 +29,11 @@ D3D11_TEXTURE2D_DESC Texture::GetDesc() const } bool Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 samples, DXGI_FORMAT format, u32 bind_flags, - const void* initial_data /* = nullptr */, u32 initial_data_stride /* = 0 */) + const void* initial_data /* = nullptr */, u32 initial_data_stride /* = 0 */, bool dynamic) { - CD3D11_TEXTURE2D_DESC desc(format, width, height, 1, 1, bind_flags, D3D11_USAGE_DEFAULT, 0, samples, 0, 0); + CD3D11_TEXTURE2D_DESC desc(format, width, height, 1, 1, bind_flags, + dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT, dynamic ? D3D11_CPU_ACCESS_WRITE : 0, + samples, 0, 0); D3D11_SUBRESOURCE_DATA srd; srd.pSysMem = initial_data; diff --git a/src/common/d3d11/texture.h b/src/common/d3d11/texture.h index 5549661f2..0fc6f0180 100644 --- a/src/common/d3d11/texture.h +++ b/src/common/d3d11/texture.h @@ -34,7 +34,7 @@ public: ALWAYS_INLINE operator bool() const { return static_cast(m_texture); } bool Create(ID3D11Device* device, u32 width, u32 height, u32 samples, DXGI_FORMAT format, u32 bind_flags, - const void* initial_data = nullptr, u32 initial_data_stride = 0); + const void* initial_data = nullptr, u32 initial_data_stride = 0, bool dynamic = false); bool Adopt(ID3D11Device* device, ComPtr texture); void Destroy(); diff --git a/src/core/gpu_hw_d3d11.cpp b/src/core/gpu_hw_d3d11.cpp index fedef7a33..c209a8a1c 100644 --- a/src/core/gpu_hw_d3d11.cpp +++ b/src/core/gpu_hw_d3d11.cpp @@ -654,15 +654,15 @@ void GPU_HW_D3D11::UpdateDisplay() if (IsUsingMultisampling()) { UpdateVRAMReadTexture(); - m_host_display->SetDisplayTexture(m_vram_read_texture.GetD3DSRV(), m_vram_read_texture.GetWidth(), - m_vram_read_texture.GetHeight(), 0, 0, m_vram_read_texture.GetWidth(), - m_vram_read_texture.GetHeight()); + m_host_display->SetDisplayTexture(m_vram_read_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8, + m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight(), 0, 0, + m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight()); } else { - 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->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8, + 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, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, @@ -689,9 +689,9 @@ void GPU_HW_D3D11::UpdateDisplay() !IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() && (scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight()) { - m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), m_vram_texture.GetWidth(), - m_vram_texture.GetHeight(), scaled_vram_offset_x, scaled_vram_offset_y, - scaled_display_width, scaled_display_height); + m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8, + m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), scaled_vram_offset_x, + scaled_vram_offset_y, scaled_display_width, scaled_display_height); } else { @@ -711,9 +711,9 @@ void GPU_HW_D3D11::UpdateDisplay() SetViewportAndScissor(0, 0, scaled_display_width, scaled_display_height); DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms)); - m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), m_display_texture.GetWidth(), - m_display_texture.GetHeight(), 0, 0, scaled_display_width, - scaled_display_height); + m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8, + m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, 0, + scaled_display_width, scaled_display_height); RestoreGraphicsAPIState(); } diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index 3aee7b2e9..6b4aad62a 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -656,17 +656,18 @@ void GPU_HW_OpenGL::UpdateDisplay() { UpdateVRAMReadTexture(); - m_host_display->SetDisplayTexture( - reinterpret_cast(static_cast(m_vram_read_texture.GetGLId())), m_vram_read_texture.GetWidth(), - static_cast(m_vram_read_texture.GetHeight()), 0, m_vram_read_texture.GetHeight(), - m_vram_read_texture.GetWidth(), -static_cast(m_vram_read_texture.GetHeight())); + m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_read_texture.GetGLId())), + HostDisplayPixelFormat::RGBA8, m_vram_read_texture.GetWidth(), + static_cast(m_vram_read_texture.GetHeight()), 0, + m_vram_read_texture.GetHeight(), m_vram_read_texture.GetWidth(), + -static_cast(m_vram_read_texture.GetHeight())); } else { m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture.GetGLId())), - 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())); + HostDisplayPixelFormat::RGBA8, 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, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); @@ -693,7 +694,8 @@ void GPU_HW_OpenGL::UpdateDisplay() (scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight()) { m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture.GetGLId())), - m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), scaled_vram_offset_x, + HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(), + m_vram_texture.GetHeight(), scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y, scaled_display_width, -static_cast(scaled_display_height)); } @@ -723,8 +725,8 @@ void GPU_HW_OpenGL::UpdateDisplay() glDrawArrays(GL_TRIANGLES, 0, 3); m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_display_texture.GetGLId())), - m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, - scaled_display_height, scaled_display_width, + HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(), + m_display_texture.GetHeight(), 0, scaled_display_height, scaled_display_width, -static_cast(scaled_display_height)); // restore state diff --git a/src/core/gpu_hw_vulkan.cpp b/src/core/gpu_hw_vulkan.cpp index 96025083a..2d77fdd2b 100644 --- a/src/core/gpu_hw_vulkan.cpp +++ b/src/core/gpu_hw_vulkan.cpp @@ -975,16 +975,17 @@ void GPU_HW_Vulkan::UpdateDisplay() if (IsUsingMultisampling()) { UpdateVRAMReadTexture(); - m_host_display->SetDisplayTexture(&m_vram_read_texture, m_vram_read_texture.GetWidth(), - m_vram_read_texture.GetHeight(), 0, 0, m_vram_read_texture.GetWidth(), - m_vram_read_texture.GetHeight()); + m_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8, + m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight(), 0, 0, + m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight()); } else { m_vram_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_host_display->SetDisplayTexture(&m_vram_texture, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 0, 0, - m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); + m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, 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, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); @@ -1012,9 +1013,9 @@ void GPU_HW_Vulkan::UpdateDisplay() { m_vram_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_host_display->SetDisplayTexture(&m_vram_texture, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), - scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width, - scaled_display_height); + m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(), + m_vram_texture.GetHeight(), scaled_vram_offset_x, scaled_vram_offset_y, + scaled_display_width, scaled_display_height); } else { @@ -1047,8 +1048,9 @@ void GPU_HW_Vulkan::UpdateDisplay() m_vram_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); m_display_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_host_display->SetDisplayTexture(&m_display_texture, m_display_texture.GetWidth(), m_display_texture.GetHeight(), - 0, 0, scaled_display_width, scaled_display_height); + m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(), + m_display_texture.GetHeight(), 0, 0, scaled_display_width, + scaled_display_height); RestoreGraphicsAPIState(); } diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 561aa2afe..e398bb91c 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -1,11 +1,24 @@ #include "gpu_sw.h" +#include "common/align.h" #include "common/assert.h" +#include "common/cpu_detect.h" #include "common/log.h" +#include "common/make_array.h" #include "host_display.h" #include "system.h" #include Log_SetChannel(GPU_SW); +#if defined(CPU_X64) +#include +#elif defined(CPU_AARCH64) +#ifdef _MSC_VER +#include +#else +#include +#endif +#endif + GPU_SW::GPU_SW() { m_vram.fill(0); @@ -28,9 +41,27 @@ bool GPU_SW::Initialize(HostDisplay* host_display) if (!GPU::Initialize(host_display)) return false; - m_display_texture = host_display->CreateTexture(VRAM_WIDTH, VRAM_HEIGHT, nullptr, 0, true); - if (!m_display_texture) - return false; + static constexpr auto formats_for_16bit = make_array(HostDisplayPixelFormat::RGB565, HostDisplayPixelFormat::RGBA5551, + HostDisplayPixelFormat::RGBA8, HostDisplayPixelFormat::BGRA8); + static constexpr auto formats_for_24bit = + make_array(HostDisplayPixelFormat::RGBA8, HostDisplayPixelFormat::BGRA8, HostDisplayPixelFormat::RGB565, + HostDisplayPixelFormat::RGBA5551); + for (const HostDisplayPixelFormat format : formats_for_16bit) + { + if (m_host_display->SupportsDisplayPixelFormat(format)) + { + m_16bit_display_format = format; + break; + } + } + for (const HostDisplayPixelFormat format : formats_for_24bit) + { + if (m_host_display->SupportsDisplayPixelFormat(format)) + { + m_24bit_display_format = format; + break; + } + } return true; } @@ -42,74 +73,323 @@ void GPU_SW::Reset() m_vram.fill(0); } -void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u32 width, u32 height, bool interlaced, - bool interleaved) +template +static void CopyOutRow16(const u16* src_ptr, out_type* dst_ptr, u32 width); + +template +static out_type VRAM16ToOutput(u16 value); + +template<> +ALWAYS_INLINE u16 VRAM16ToOutput(u16 value) { + return (value & 0x3E0) | ((value >> 10) & 0x1F) | ((value & 0x1F) << 10); +} + +template<> +ALWAYS_INLINE u16 VRAM16ToOutput(u16 value) +{ + return ((value & 0x3E0) << 1) | ((value & 0x20) << 1) | ((value >> 10) & 0x1F) | ((value & 0x1F) << 11); +} + +template<> +ALWAYS_INLINE u32 VRAM16ToOutput(u16 value) +{ + u8 r = Truncate8(value & 31); + u8 g = Truncate8((value >> 5) & 31); + u8 b = Truncate8((value >> 10) & 31); + + // 00012345 -> 1234545 + b = (b << 3) | (b & 0b111); + g = (g << 3) | (g & 0b111); + r = (r << 3) | (r & 0b111); + + return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (0xFF000000u); +} + +template<> +ALWAYS_INLINE u32 VRAM16ToOutput(u16 value) +{ + u8 r = Truncate8(value & 31); + u8 g = Truncate8((value >> 5) & 31); + u8 b = Truncate8((value >> 10) & 31); + + // 00012345 -> 1234545 + b = (b << 3) | (b & 0b111); + g = (g << 3) | (g & 0b111); + r = (r << 3) | (r & 0b111); + + return ZeroExtend32(b) | (ZeroExtend32(g) << 8) | (ZeroExtend32(r) << 16) | (0xFF000000u); +} + +template<> +ALWAYS_INLINE void CopyOutRow16(const u16* src_ptr, u16* dst_ptr, u32 width) +{ + u32 col = 0; + +#if defined(CPU_X64) + const u32 aligned_width = Common::AlignDownPow2(width, 8); + for (; col < aligned_width; col += 8) + { + const __m128i single_mask = _mm_set1_epi16(0x1F); + __m128i value = _mm_loadu_si128(reinterpret_cast(src_ptr)); + src_ptr += 8; + __m128i a = _mm_and_si128(value, _mm_set1_epi16(static_cast(static_cast(0x3E0)))); + __m128i b = _mm_and_si128(_mm_srli_epi16(value, 10), single_mask); + __m128i c = _mm_slli_epi16(_mm_and_si128(value, single_mask), 10); + value = _mm_or_si128(_mm_or_si128(a, b), c); + _mm_storeu_si128(reinterpret_cast<__m128i*>(dst_ptr), value); + dst_ptr += 8; + } +#elif defined(CPU_AARCH64) + const u32 aligned_width = Common::AlignDownPow2(width, 8); + for (; col < aligned_width; col += 8) + { + const uint16x8_t single_mask = vdupq_n_u16(0x1F); + uint16x8_t value = vld1q_u16(src_ptr); + src_ptr += 8; + uint16x8_t a = vandq_u16(value, vdupq_n_u16(0x3E0)); + uint16x8_t b = vandq_u16(vshrq_n_u16(value, 10), single_mask); + uint16x8_t c = vshlq_n_u16(vandq_u16(value, single_mask), 10); + value = vorrq_u16(vorrq_u16(a, b), c); + vst1q_u16(dst_ptr, value); + dst_ptr += 8; + } +#endif + + for (; col < width; col++) + *(dst_ptr++) = VRAM16ToOutput(*(src_ptr++)); +} + +template<> +ALWAYS_INLINE void CopyOutRow16(const u16* src_ptr, u16* dst_ptr, u32 width) +{ + u32 col = 0; + +#if defined(CPU_X64) + const u32 aligned_width = Common::AlignDownPow2(width, 8); + for (; col < aligned_width; col += 8) + { + const __m128i single_mask = _mm_set1_epi16(0x1F); + __m128i value = _mm_loadu_si128(reinterpret_cast(src_ptr)); + src_ptr += 8; + __m128i a = _mm_slli_epi16(_mm_and_si128(value, _mm_set1_epi16(static_cast(static_cast(0x3E0)))), 1); + __m128i b = _mm_slli_epi16(_mm_and_si128(value, _mm_set1_epi16(static_cast(static_cast(0x20)))), 1); + __m128i c = _mm_and_si128(_mm_srli_epi16(value, 10), single_mask); + __m128i d = _mm_slli_epi16(_mm_and_si128(value, single_mask), 11); + value = _mm_or_si128(_mm_or_si128(_mm_or_si128(a, b), c), d); + _mm_storeu_si128(reinterpret_cast<__m128i*>(dst_ptr), value); + dst_ptr += 8; + } +#elif defined(CPU_AARCH64) + const u32 aligned_width = Common::AlignDownPow2(width, 8); + const uint16x8_t single_mask = vdupq_n_u16(0x1F); + for (; col < aligned_width; col += 8) + { + uint16x8_t value = vld1q_u16(src_ptr); + src_ptr += 8; + uint16x8_t a = vshlq_n_u16(vandq_u16(value, vdupq_n_u16(0x3E0)), 1); // (value & 0x3E0) << 1 + uint16x8_t b = vshlq_n_u16(vandq_u16(value, vdupq_n_u16(0x20)), 1); // (value & 0x20) << 1 + uint16x8_t c = vandq_u16(vshrq_n_u16(value, 10), single_mask); // ((value >> 10) & 0x1F) + uint16x8_t d = vshlq_n_u16(vandq_u16(value, single_mask), 11); // ((value & 0x1F) << 11) + value = vorrq_u16(vorrq_u16(vorrq_u16(a, b), c), d); + vst1q_u16(dst_ptr, value); + dst_ptr += 8; + } +#endif + + for (; col < width; col++) + *(dst_ptr++) = VRAM16ToOutput(*(src_ptr++)); +} + +template<> +ALWAYS_INLINE void CopyOutRow16(const u16* src_ptr, u32* dst_ptr, u32 width) +{ + for (u32 col = 0; col < width; col++) + *(dst_ptr++) = VRAM16ToOutput(*(src_ptr++)); +} + +template<> +ALWAYS_INLINE void CopyOutRow16(const u16* src_ptr, u32* dst_ptr, u32 width) +{ + for (u32 col = 0; col < width; col++) + *(dst_ptr++) = VRAM16ToOutput(*(src_ptr++)); +} + +template +void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field, bool interlaced, bool interleaved) +{ + u8* dst_ptr; + u32 dst_stride; + + using OutputPixelType = std::conditional_t< + display_format == HostDisplayPixelFormat::RGBA8 || display_format == HostDisplayPixelFormat::BGRA8, u32, u16>; + + if (!interlaced) + { + if (!m_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast(&dst_ptr), + &dst_stride)) + { + return; + } + } + else + { + dst_stride = Common::AlignUpPow2(width * sizeof(OutputPixelType), 4); + dst_ptr = m_display_texture_buffer.data() + (field != 0 ? dst_stride : 0); + } + + const u32 output_stride = dst_stride; const u8 interlaced_shift = BoolToUInt8(interlaced); const u8 interleaved_shift = BoolToUInt8(interleaved); // Fast path when not wrapping around. if ((src_x + width) <= VRAM_WIDTH && (src_y + height) <= VRAM_HEIGHT) { + const u32 rows = height >> interlaced_shift; dst_stride <<= interlaced_shift; - height >>= interlaced_shift; const u16* src_ptr = &m_vram[src_y * VRAM_WIDTH + src_x]; - const u32 src_stride = VRAM_WIDTH << interleaved_shift; - for (u32 row = 0; row < height; row++) + const u32 src_step = VRAM_WIDTH << interleaved_shift; + for (u32 row = 0; row < rows; row++) { - const u16* src_row_ptr = src_ptr; - u32* dst_row_ptr = dst_ptr; - for (u32 col = 0; col < width; col++) - *(dst_row_ptr++) = RGBA5551ToRGBA8888(*(src_row_ptr++)); - - src_ptr += src_stride; + CopyOutRow16(src_ptr, reinterpret_cast(dst_ptr), width); + src_ptr += src_step; dst_ptr += dst_stride; } } else { + const u32 rows = height >> interlaced_shift; dst_stride <<= interlaced_shift; - height >>= interlaced_shift; const u32 end_x = src_x + width; - for (u32 row = 0; row < height; row++) + for (u32 row = 0; row < rows; row++) { const u16* src_row_ptr = &m_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH]; - u32* dst_row_ptr = dst_ptr; - + OutputPixelType* dst_row_ptr = reinterpret_cast(dst_ptr); for (u32 col = src_x; col < end_x; col++) - *(dst_row_ptr++) = RGBA5551ToRGBA8888(src_row_ptr[col % VRAM_WIDTH]); - - src_y += (1 << interleaved_shift); - dst_ptr += dst_stride; + { + *(dst_row_ptr++) = VRAM16ToOutput(src_row_ptr[col % VRAM_WIDTH]); + src_y += (1 << interleaved_shift); + dst_ptr += dst_stride; + } } } + + if (!interlaced) + { + m_host_display->EndSetDisplayPixels(); + } + else + { + m_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride); + } } -void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u32 width, u32 height, bool interlaced, +void GPU_SW::CopyOut15Bit(HostDisplayPixelFormat display_format, u32 src_x, u32 src_y, u32 width, u32 height, u32 field, + bool interlaced, bool interleaved) +{ + switch (display_format) + { + case HostDisplayPixelFormat::RGBA5551: + CopyOut15Bit(src_x, src_y, width, height, field, interlaced, interleaved); + break; + case HostDisplayPixelFormat::RGB565: + CopyOut15Bit(src_x, src_y, width, height, field, interlaced, interleaved); + break; + case HostDisplayPixelFormat::RGBA8: + CopyOut15Bit(src_x, src_y, width, height, field, interlaced, interleaved); + break; + case HostDisplayPixelFormat::BGRA8: + CopyOut15Bit(src_x, src_y, width, height, field, interlaced, interleaved); + break; + default: + break; + } +} + +template +void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 field, bool interlaced, bool interleaved) { + u8* dst_ptr; + u32 dst_stride; + + using OutputPixelType = std::conditional_t< + display_format == HostDisplayPixelFormat::RGBA8 || display_format == HostDisplayPixelFormat::BGRA8, u32, u16>; + + if (!interlaced) + { + if (!m_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast(&dst_ptr), + &dst_stride)) + { + return; + } + } + else + { + dst_stride = Common::AlignUpPow2(width * sizeof(OutputPixelType), 4); + dst_ptr = m_display_texture_buffer.data() + (field != 0 ? dst_stride : 0); + } + + const u32 output_stride = dst_stride; const u8 interlaced_shift = BoolToUInt8(interlaced); const u8 interleaved_shift = BoolToUInt8(interleaved); + const u32 rows = height >> interlaced_shift; + dst_stride <<= interlaced_shift; - if ((src_x + width) <= VRAM_WIDTH && (src_y + height) <= VRAM_HEIGHT) + if ((src_x + width) <= VRAM_WIDTH && (src_y + (rows << interleaved_shift)) <= VRAM_HEIGHT) { - dst_stride <<= interlaced_shift; - height >>= interlaced_shift; - - const u8* src_ptr = reinterpret_cast(&m_vram[src_y * VRAM_WIDTH + src_x]); + const u8* src_ptr = reinterpret_cast(&m_vram[src_y * VRAM_WIDTH + src_x]) + (skip_x * 3); const u32 src_stride = (VRAM_WIDTH << interleaved_shift) * sizeof(u16); - for (u32 row = 0; row < height; row++) + for (u32 row = 0; row < rows; row++) { - const u8* src_row_ptr = src_ptr; - u8* dst_row_ptr = reinterpret_cast(dst_ptr); - for (u32 col = 0; col < width; col++) + if constexpr (display_format == HostDisplayPixelFormat::RGBA8) { - *(dst_row_ptr++) = *(src_row_ptr++); - *(dst_row_ptr++) = *(src_row_ptr++); - *(dst_row_ptr++) = *(src_row_ptr++); - *(dst_row_ptr++) = 0xFF; + const u8* src_row_ptr = src_ptr; + u8* dst_row_ptr = reinterpret_cast(dst_ptr); + for (u32 col = 0; col < width; col++) + { + *(dst_row_ptr++) = *(src_row_ptr++); + *(dst_row_ptr++) = *(src_row_ptr++); + *(dst_row_ptr++) = *(src_row_ptr++); + *(dst_row_ptr++) = 0xFF; + } + } + else if constexpr (display_format == HostDisplayPixelFormat::BGRA8) + { + const u8* src_row_ptr = src_ptr; + u8* dst_row_ptr = reinterpret_cast(dst_ptr); + for (u32 col = 0; col < width; col++) + { + *(dst_row_ptr++) = src_row_ptr[2]; + *(dst_row_ptr++) = src_row_ptr[1]; + *(dst_row_ptr++) = src_row_ptr[0]; + *(dst_row_ptr++) = 0xFF; + src_row_ptr += 3; + } + } + else if constexpr (display_format == HostDisplayPixelFormat::RGB565) + { + const u8* src_row_ptr = src_ptr; + u16* dst_row_ptr = reinterpret_cast(dst_ptr); + for (u32 col = 0; col < width; col++) + { + *(dst_row_ptr++) = ((static_cast(src_row_ptr[0]) >> 3) << 11) | + ((static_cast(src_row_ptr[1]) >> 2) << 5) | (static_cast(src_row_ptr[2]) >> 3); + src_row_ptr += 3; + } + } + else if constexpr (display_format == HostDisplayPixelFormat::RGBA5551) + { + const u8* src_row_ptr = src_ptr; + u16* dst_row_ptr = reinterpret_cast(dst_ptr); + for (u32 col = 0; col < width; col++) + { + *(dst_row_ptr++) = ((static_cast(src_row_ptr[0]) >> 3) << 10) | + ((static_cast(src_row_ptr[1]) >> 3) << 5) | (static_cast(src_row_ptr[2]) >> 3); + src_row_ptr += 3; + } } src_ptr += src_stride; @@ -118,39 +398,83 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u3 } else { - dst_stride <<= interlaced_shift; - height >>= interlaced_shift; - - for (u32 row = 0; row < height; row++) + for (u32 row = 0; row < rows; row++) { const u16* src_row_ptr = &m_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH]; - u32* dst_row_ptr = dst_ptr; + OutputPixelType* dst_row_ptr = reinterpret_cast(dst_ptr); for (u32 col = 0; col < width; col++) { - const u32 offset = (src_x + ((col * 3) / 2)); + const u32 offset = (src_x + (((skip_x + col) * 3) / 2)); const u16 s0 = src_row_ptr[offset % VRAM_WIDTH]; const u16 s1 = src_row_ptr[(offset + 1) % VRAM_WIDTH]; const u8 shift = static_cast(col & 1u) * 8; - *(dst_row_ptr++) = (((ZeroExtend32(s1) << 16) | ZeroExtend32(s0)) >> shift) | 0xFF000000u; + const u32 rgb = (((ZeroExtend32(s1) << 16) | ZeroExtend32(s0)) >> shift); + + if constexpr (display_format == HostDisplayPixelFormat::RGBA8) + { + *(dst_row_ptr++) = rgb | 0xFF000000u; + } + else if constexpr (display_format == HostDisplayPixelFormat::BGRA8) + { + *(dst_row_ptr++) = (rgb & 0x00FF00) | ((rgb & 0xFF) << 16) | ((rgb >> 16) & 0xFF) | 0xFF000000u; + } + else if constexpr (display_format == HostDisplayPixelFormat::RGB565) + { + *(dst_row_ptr++) = ((rgb >> 3) & 0x1F) | (((rgb >> 10) << 5) & 0x7E0) | (((rgb >> 19) << 11) & 0x3E0000); + } + else if constexpr (display_format == HostDisplayPixelFormat::RGBA5551) + { + *(dst_row_ptr++) = ((rgb >> 3) & 0x1F) | (((rgb >> 11) << 5) & 0x3E0) | (((rgb >> 19) << 10) & 0x1F0000); + } } src_y += (1 << interleaved_shift); dst_ptr += dst_stride; } } + + if (!interlaced) + { + m_host_display->EndSetDisplayPixels(); + } + else + { + m_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride); + } +} + +void GPU_SW::CopyOut24Bit(HostDisplayPixelFormat display_format, u32 src_x, u32 src_y, u32 skip_x, u32 width, + u32 height, u32 field, bool interlaced, bool interleaved) +{ + switch (display_format) + { + case HostDisplayPixelFormat::RGBA5551: + CopyOut24Bit(src_x, src_y, skip_x, width, height, field, interlaced, + interleaved); + break; + case HostDisplayPixelFormat::RGB565: + CopyOut24Bit(src_x, src_y, skip_x, width, height, field, interlaced, interleaved); + break; + case HostDisplayPixelFormat::RGBA8: + CopyOut24Bit(src_x, src_y, skip_x, width, height, field, interlaced, interleaved); + break; + case HostDisplayPixelFormat::BGRA8: + CopyOut24Bit(src_x, src_y, skip_x, width, height, field, interlaced, interleaved); + break; + default: + break; + } } void GPU_SW::ClearDisplay() { - std::memset(m_display_texture_buffer.data(), 0, sizeof(u32) * m_display_texture_buffer.size()); + std::memset(m_display_texture_buffer.data(), 0, m_display_texture_buffer.size()); } void GPU_SW::UpdateDisplay() { // fill display texture - m_display_texture_buffer.resize(VRAM_WIDTH * VRAM_HEIGHT); - if (!g_settings.debugging.show_vram) { if (IsDisplayDisabled()) @@ -162,39 +486,37 @@ void GPU_SW::UpdateDisplay() 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; - const u32 texture_offset_x = m_crtc_state.display_vram_left - m_crtc_state.regs.X; + if (IsInterlacedDisplayEnabled()) { const u32 field = GetInterlacedDisplayField(); if (m_GPUSTAT.display_area_color_depth_24) { - CopyOut24Bit(m_crtc_state.regs.X, vram_offset_y + field, m_display_texture_buffer.data() + field * VRAM_WIDTH, - VRAM_WIDTH, display_width + texture_offset_x, display_height, true, m_GPUSTAT.vertical_resolution); + CopyOut24Bit(m_24bit_display_format, m_crtc_state.regs.X, vram_offset_y + field, + m_crtc_state.display_vram_left - m_crtc_state.regs.X, display_width, display_height, field, true, + m_GPUSTAT.vertical_resolution); } else { - CopyOut15Bit(m_crtc_state.regs.X, vram_offset_y + field, m_display_texture_buffer.data() + field * VRAM_WIDTH, - VRAM_WIDTH, display_width + texture_offset_x, display_height, true, m_GPUSTAT.vertical_resolution); + CopyOut15Bit(m_16bit_display_format, m_crtc_state.display_vram_left, vram_offset_y + field, display_width, + display_height, field, true, m_GPUSTAT.vertical_resolution); } } else { if (m_GPUSTAT.display_area_color_depth_24) { - CopyOut24Bit(m_crtc_state.regs.X, vram_offset_y, m_display_texture_buffer.data(), VRAM_WIDTH, - display_width + texture_offset_x, display_height, false, false); + CopyOut24Bit(m_24bit_display_format, m_crtc_state.regs.X, vram_offset_y, + m_crtc_state.display_vram_left - m_crtc_state.regs.X, display_width, display_height, 0, false, + false); } else { - CopyOut15Bit(m_crtc_state.regs.X, vram_offset_y, m_display_texture_buffer.data(), VRAM_WIDTH, - display_width + texture_offset_x, display_height, false, false); + CopyOut15Bit(m_16bit_display_format, m_crtc_state.display_vram_left, vram_offset_y, display_width, + display_height, 0, false, false); } } - m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, display_width, display_height, - m_display_texture_buffer.data(), VRAM_WIDTH * sizeof(u32)); - m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), VRAM_WIDTH, VRAM_HEIGHT, texture_offset_x, 0, - display_width, display_height); 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, @@ -202,11 +524,7 @@ void GPU_SW::UpdateDisplay() } else { - CopyOut15Bit(0, 0, m_display_texture_buffer.data(), VRAM_WIDTH, VRAM_WIDTH, VRAM_HEIGHT, false, false); - m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT, - 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); + CopyOut15Bit(m_16bit_display_format, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 0, false, false); m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); } @@ -379,7 +697,8 @@ constexpr GPU_SW::DitherLUT GPU_SW::ComputeDitherLUT() static constexpr GPU_SW::DitherLUT s_dither_lut = GPU_SW::ComputeDitherLUT(); template -void ALWAYS_INLINE_RELEASE GPU_SW::ShadePixel(u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 texcoord_x, u8 texcoord_y) +void ALWAYS_INLINE_RELEASE GPU_SW::ShadePixel(u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 texcoord_x, + u8 texcoord_y) { VRAMPixel color; bool transparent; diff --git a/src/core/gpu_sw.h b/src/core/gpu_sw.h index d59148487..d8ad4f463 100644 --- a/src/core/gpu_sw.h +++ b/src/core/gpu_sw.h @@ -1,5 +1,7 @@ #pragma once +#include "common/heap_array.h" #include "gpu.h" +#include "host_display.h" #include #include #include @@ -47,10 +49,17 @@ protected: ////////////////////////////////////////////////////////////////////////// // Scanout ////////////////////////////////////////////////////////////////////////// - void CopyOut15Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u32 width, u32 height, bool interlaced, - bool interleaved); - void CopyOut24Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u32 width, u32 height, bool interlaced, + template + void CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field, bool interlaced, bool interleaved); + void CopyOut15Bit(HostDisplayPixelFormat display_format, u32 src_x, u32 src_y, u32 width, u32 height, u32 field, + bool interlaced, bool interleaved); + + template + void CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 field, bool interlaced, bool interleaved); + void CopyOut24Bit(HostDisplayPixelFormat display_format, u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, + u32 field, bool interlaced, bool interleaved); + void ClearDisplay() override; void UpdateDisplay() override; @@ -117,8 +126,8 @@ protected: using DrawLineFunction = void (GPU_SW::*)(const SWVertex* p0, const SWVertex* p1); DrawLineFunction GetDrawLineFunction(bool shading_enable, bool transparency_enable, bool dithering_enable); - std::vector m_display_texture_buffer; - std::unique_ptr m_display_texture; - std::array m_vram; + HeapArray m_display_texture_buffer; + HostDisplayPixelFormat m_16bit_display_format = HostDisplayPixelFormat::RGB565; + HostDisplayPixelFormat m_24bit_display_format = HostDisplayPixelFormat::RGBA8; }; diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index d5b4863b4..fde0d4632 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -1,4 +1,5 @@ #include "host_display.h" +#include "common/assert.h" #include "common/file_system.h" #include "common/log.h" #include "common/string_util.h" @@ -35,6 +36,53 @@ bool HostDisplay::ShouldSkipDisplayingFrame() return false; } +u32 HostDisplay::GetDisplayPixelFormatSize(HostDisplayPixelFormat format) +{ + switch (format) + { + case HostDisplayPixelFormat::RGBA8: + case HostDisplayPixelFormat::BGRA8: + return 4; + + case HostDisplayPixelFormat::RGBA5551: + case HostDisplayPixelFormat::RGB565: + return 2; + + default: + return 0; + } +} + +bool HostDisplay::SetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, const void* buffer, u32 pitch) +{ + void* map_ptr; + u32 map_pitch; + if (!BeginSetDisplayPixels(format, width, height, &map_ptr, &map_pitch)) + return false; + + if (pitch == map_pitch) + { + std::memcpy(map_ptr, buffer, height * map_pitch); + } + else + { + const u32 copy_size = width * GetDisplayPixelFormatSize(format); + DebugAssert(pitch >= copy_size && map_pitch >= copy_size); + + const u8* src_ptr = static_cast(buffer); + u8* dst_ptr = static_cast(map_ptr); + for (u32 i = 0; i < height; i++) + { + std::memcpy(dst_ptr, src_ptr, copy_size); + src_ptr += pitch; + dst_ptr += map_pitch; + } + } + + EndSetDisplayPixels(); + return true; +} + void HostDisplay::SetSoftwareCursor(std::unique_ptr texture, float scale /*= 1.0f*/) { m_cursor_texture = std::move(texture); @@ -327,7 +375,7 @@ bool HostDisplay::WriteTextureToFile(const void* texture_handle, u32 x, u32 y, u bool HostDisplay::WriteDisplayTextureToFile(const char* filename, bool full_resolution /* = true */, bool apply_aspect_ratio /* = true */) { - if (!m_display_texture_handle) + if (!m_display_texture_handle || m_display_texture_format != HostDisplayPixelFormat::RGBA8) return false; apply_aspect_ratio = (m_display_aspect_ratio > 0) ? apply_aspect_ratio : false; @@ -394,7 +442,7 @@ bool HostDisplay::WriteDisplayTextureToFile(const char* filename, bool full_reso bool HostDisplay::WriteDisplayTextureToBuffer(std::vector* buffer, u32 resize_width /* = 0 */, u32 resize_height /* = 0 */, bool clear_alpha /* = true */) { - if (!m_display_texture_handle) + if (!m_display_texture_handle || m_display_texture_format != HostDisplayPixelFormat::RGBA8) return false; const bool flip_y = (m_display_texture_view_height < 0); diff --git a/src/core/host_display.h b/src/core/host_display.h index ea6666aa0..5bc06121f 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -7,6 +7,16 @@ #include #include +enum class HostDisplayPixelFormat : u32 +{ + Unknown, + RGBA8, + BGRA8, + RGB565, + RGBA5551, + Count +}; + // An abstracted RGBA8 texture. class HostDisplayTexture { @@ -16,6 +26,8 @@ public: virtual void* GetHandle() const = 0; virtual u32 GetWidth() const = 0; virtual u32 GetHeight() const = 0; + + ALWAYS_INLINE HostDisplayPixelFormat GetFormat() const { return HostDisplayPixelFormat::RGBA8; } }; // Interface to the frontend's renderer. @@ -111,10 +123,11 @@ public: m_display_changed = true; } - void SetDisplayTexture(void* texture_handle, s32 texture_width, s32 texture_height, s32 view_x, s32 view_y, - s32 view_width, s32 view_height) + void SetDisplayTexture(void* texture_handle, HostDisplayPixelFormat texture_format, s32 texture_width, + s32 texture_height, s32 view_x, s32 view_y, s32 view_width, s32 view_height) { m_display_texture_handle = texture_handle; + m_display_texture_format = texture_format; m_display_texture_width = texture_width; m_display_texture_height = texture_height; m_display_texture_view_x = view_x; @@ -124,6 +137,15 @@ public: m_display_changed = true; } + void SetDisplayTextureRect(s32 view_x, s32 view_y, s32 view_width, s32 view_height) + { + m_display_texture_view_x = view_x; + m_display_texture_view_y = view_y; + m_display_texture_view_width = view_width; + m_display_texture_view_height = view_height; + m_display_changed = true; + } + void SetDisplayParameters(s32 display_width, s32 display_height, s32 active_left, s32 active_top, s32 active_width, s32 active_height, float display_aspect_ratio) { @@ -137,6 +159,15 @@ public: m_display_changed = true; } + static u32 GetDisplayPixelFormatSize(HostDisplayPixelFormat format); + + virtual bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const = 0; + + virtual bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) = 0; + virtual void EndSetDisplayPixels() = 0; + virtual bool SetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, const void* buffer, u32 pitch); + void SetDisplayLinearFiltering(bool enabled) { m_display_linear_filtering = enabled; } void SetDisplayTopMargin(s32 height) { m_display_top_margin = height; } void SetDisplayIntegerScaling(bool enabled) { m_display_integer_scaling = enabled; } @@ -201,6 +232,7 @@ protected: float m_display_frame_interval = 0.0f; void* m_display_texture_handle = nullptr; + HostDisplayPixelFormat m_display_texture_format = HostDisplayPixelFormat::Count; s32 m_display_texture_width = 0; s32 m_display_texture_height = 0; s32 m_display_texture_view_x = 0; diff --git a/src/duckstation-libretro/libretro_host_display.cpp b/src/duckstation-libretro/libretro_host_display.cpp index 2d858c29c..bf1108aff 100644 --- a/src/duckstation-libretro/libretro_host_display.cpp +++ b/src/duckstation-libretro/libretro_host_display.cpp @@ -1,4 +1,5 @@ #include "libretro_host_display.h" +#include "common/align.h" #include "common/assert.h" #include "common/log.h" #include "libretro.h" @@ -7,86 +8,54 @@ #include Log_SetChannel(LibretroHostDisplay); -class LibretroDisplayTexture : public HostDisplayTexture +static retro_pixel_format GetRetroPixelFormat(HostDisplayPixelFormat format) { -public: - LibretroDisplayTexture(u32 width, u32 height) : m_width(width), m_height(height), m_data(width * height) {} - ~LibretroDisplayTexture() override = default; - - void* GetHandle() const override { return const_cast(this); } - u32 GetWidth() const override { return m_width; } - u32 GetHeight() const override { return m_height; } - - const u32* GetData() const { return m_data.data(); } - u32 GetDataPitch() const { return m_width * sizeof(u32); } - - static void SwapAndCopy(void* dst, const void* src, u32 count) + switch (format) { - // RGBA -> BGRX conversion - u8* dst_ptr = static_cast(dst); - const u8* src_ptr = static_cast(src); + case HostDisplayPixelFormat::BGRA8: + return RETRO_PIXEL_FORMAT_XRGB8888; - for (u32 i = 0; i < count; i++) - { - u32 sval; - std::memcpy(&sval, src_ptr, sizeof(sval)); - src_ptr += sizeof(sval); - const u32 dval = (sval & 0xFF00FF00u) | ((sval & 0xFF) << 16) | ((sval >> 16) & 0xFFu); - std::memcpy(dst_ptr, &dval, sizeof(dval)); - dst_ptr += sizeof(dval); - } + case HostDisplayPixelFormat::RGB565: + return RETRO_PIXEL_FORMAT_RGB565; + + case HostDisplayPixelFormat::RGBA5551: + return RETRO_PIXEL_FORMAT_0RGB1555; + + default: + return RETRO_PIXEL_FORMAT_UNKNOWN; } - - void Read(u32 x, u32 y, u32 width, u32 height, void* data, u32 data_stride) const - { - u8* data_ptr = static_cast(data); - const u32* in_ptr = m_data.data() + y * m_width + x; - for (u32 i = 0; i < height; i++) - { - SwapAndCopy(data_ptr, in_ptr, width); - data_ptr += data_stride; - in_ptr += m_width; - } - } - - void Write(u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) - { - const u8* data_ptr = static_cast(data); - u32* out_ptr = m_data.data() + y * m_width + x; - for (u32 i = 0; i < height; i++) - { - SwapAndCopy(out_ptr, data_ptr, width); - data_ptr += data_stride; - out_ptr += m_width; - } - } - - static std::unique_ptr Create(u32 width, u32 height, const void* initial_data, - u32 initial_data_stride) - { - std::unique_ptr tex = std::make_unique(width, height); - if (initial_data) - tex->Write(0, 0, width, height, initial_data, initial_data_stride); - - return tex; - } - -private: - u32 m_width; - u32 m_height; - std::vector m_data; -}; +} LibretroHostDisplay::LibretroHostDisplay() { - // switch to a 32-bit buffer - retro_pixel_format pf = RETRO_PIXEL_FORMAT_XRGB8888; + retro_pixel_format pf = RETRO_PIXEL_FORMAT_RGB565; if (!g_retro_environment_callback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &pf)) - Log_ErrorPrint("Failed to set pixel format to XRGB8888"); + Log_ErrorPrint("Failed to set pixel format to RGB565"); + else + m_current_pixel_format = pf; } LibretroHostDisplay::~LibretroHostDisplay() = default; +bool LibretroHostDisplay::CheckPixelFormat(retro_pixel_format new_format) +{ + if (new_format == RETRO_PIXEL_FORMAT_UNKNOWN || m_current_pixel_format == new_format) + return true; + + if (!g_retro_environment_callback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &new_format)) + { + Log_ErrorPrintf("g_retro_environment_callback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, %u) failed", + static_cast(new_format)); + return false; + } + + if (!g_libretro_host_interface.UpdateSystemAVInfo(false)) + return false; + + m_current_pixel_format = new_format; + return true; +} + HostDisplay::RenderAPI LibretroHostDisplay::GetRenderAPI() const { return RenderAPI::None; @@ -179,22 +148,68 @@ bool LibretroHostDisplay::SetPostProcessingChain(const std::string_view& config) std::unique_ptr LibretroHostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) { - return LibretroDisplayTexture::Create(width, height, data, data_stride); + return nullptr; } void LibretroHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) { - static_cast(texture)->Write(x, y, width, height, data, data_stride); } bool LibretroHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, u32 out_data_stride) { - static_cast(texture_handle)->Read(x, y, width, height, out_data, out_data_stride); + return false; +} + +bool LibretroHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const +{ + // For when we can change the pixel format. + // return (GetRetroPixelFormat(format) != RETRO_PIXEL_FORMAT_UNKNOWN); + return (GetRetroPixelFormat(format) == m_current_pixel_format); +} + +bool LibretroHostDisplay::BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) +{ + const retro_pixel_format retro_pf = GetRetroPixelFormat(format); + if (!CheckPixelFormat(retro_pf)) + return false; + + m_software_fb.data = nullptr; + m_software_fb.width = width; + m_software_fb.height = height; + m_software_fb.pitch = 0; + m_software_fb.format = RETRO_PIXEL_FORMAT_UNKNOWN; + m_software_fb.access_flags = RETRO_MEMORY_ACCESS_WRITE; + m_software_fb.memory_flags = 0; + if (g_retro_environment_callback(RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER, &m_software_fb) && + m_software_fb.format == retro_pf) + { + SetDisplayTexture(m_software_fb.data, format, m_software_fb.width, m_software_fb.height, 0, 0, m_software_fb.width, + m_software_fb.height); + *out_buffer = m_software_fb.data; + *out_pitch = static_cast(m_software_fb.pitch); + return true; + } + + const u32 pitch = Common::AlignUpPow2(width * GetDisplayPixelFormatSize(format), 4); + const u32 required_size = height * pitch; + if (m_frame_buffer.size() < (required_size / 4)) + m_frame_buffer.resize(required_size / 4); + + m_frame_buffer_pitch = pitch; + SetDisplayTexture(m_frame_buffer.data(), format, width, height, 0, 0, width, height); + *out_buffer = m_frame_buffer.data(); + *out_pitch = pitch; return true; } +void LibretroHostDisplay::EndSetDisplayPixels() +{ + // noop +} + void LibretroHostDisplay::SetVSync(bool enabled) { // The libretro frontend controls this. @@ -205,10 +220,11 @@ bool LibretroHostDisplay::Render() { if (HasDisplayTexture()) { - const LibretroDisplayTexture* tex = static_cast(m_display_texture_handle); - g_retro_video_refresh_callback(tex->GetData() + m_display_texture_view_y * tex->GetWidth() + - m_display_texture_view_x, - m_display_texture_view_width, m_display_texture_view_height, tex->GetDataPitch()); + g_retro_video_refresh_callback(m_display_texture_handle, m_display_texture_view_width, + m_display_texture_view_height, m_frame_buffer_pitch); + + if (m_display_texture_handle == m_software_fb.data) + ClearDisplayTexture(); } return true; diff --git a/src/duckstation-libretro/libretro_host_display.h b/src/duckstation-libretro/libretro_host_display.h index 269926ca1..1198d182d 100644 --- a/src/duckstation-libretro/libretro_host_display.h +++ b/src/duckstation-libretro/libretro_host_display.h @@ -1,5 +1,6 @@ #pragma once #include "core/host_display.h" +#include "libretro.h" class LibretroHostDisplay final : public HostDisplay { @@ -43,4 +44,18 @@ public: void SetVSync(bool enabled) override; bool Render() override; + + bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const override; + + bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) override; + void EndSetDisplayPixels() override; + +private: + bool CheckPixelFormat(retro_pixel_format new_format); + + std::vector m_frame_buffer; + u32 m_frame_buffer_pitch = 0; + retro_framebuffer m_software_fb = {}; + retro_pixel_format m_current_pixel_format = RETRO_PIXEL_FORMAT_UNKNOWN; }; diff --git a/src/duckstation-libretro/libretro_host_interface.cpp b/src/duckstation-libretro/libretro_host_interface.cpp index f1e48cb88..7110e3190 100644 --- a/src/duckstation-libretro/libretro_host_interface.cpp +++ b/src/duckstation-libretro/libretro_host_interface.cpp @@ -237,7 +237,7 @@ void LibretroHostInterface::GetSystemAVInfo(struct retro_system_av_info* info, b info->timing.sample_rate = static_cast(AUDIO_SAMPLE_RATE); } -void LibretroHostInterface::UpdateSystemAVInfo(bool use_resolution_scale) +bool LibretroHostInterface::UpdateSystemAVInfo(bool use_resolution_scale) { struct retro_system_av_info avi; GetSystemAVInfo(&avi, use_resolution_scale); @@ -247,7 +247,12 @@ void LibretroHostInterface::UpdateSystemAVInfo(bool use_resolution_scale) avi.timing.fps); if (!g_retro_environment_callback(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avi)) + { Log_ErrorPrintf("Failed to update system AV info on resolution change"); + return false; + } + + return true; } void LibretroHostInterface::UpdateGeometry() diff --git a/src/duckstation-libretro/libretro_host_interface.h b/src/duckstation-libretro/libretro_host_interface.h index 295ef2627..6c7f83d2f 100644 --- a/src/duckstation-libretro/libretro_host_interface.h +++ b/src/duckstation-libretro/libretro_host_interface.h @@ -28,6 +28,8 @@ public: std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "") override; std::string GetBIOSDirectory() override; + bool UpdateSystemAVInfo(bool use_resolution_scale); + // Called by frontend void retro_set_environment(); void retro_get_system_av_info(struct retro_system_av_info* info); @@ -63,7 +65,6 @@ private: void UpdateControllersDigitalController(u32 index); void UpdateControllersAnalogController(u32 index); void GetSystemAVInfo(struct retro_system_av_info* info, bool use_resolution_scale); - void UpdateSystemAVInfo(bool use_resolution_scale); void UpdateGeometry(); void UpdateLogging(); diff --git a/src/frontend-common/d3d11_host_display.cpp b/src/frontend-common/d3d11_host_display.cpp index 23c2bc771..910d639b2 100644 --- a/src/frontend-common/d3d11_host_display.cpp +++ b/src/frontend-common/d3d11_host_display.cpp @@ -171,6 +171,58 @@ bool D3D11HostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, static_cast(out_data)); } +static constexpr std::array(HostDisplayPixelFormat::Count)> + s_display_pixel_format_mapping = {{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM}}; + +bool D3D11HostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const +{ + const DXGI_FORMAT dfmt = s_display_pixel_format_mapping[static_cast(format)]; + if (dfmt == DXGI_FORMAT_UNKNOWN) + return false; + + UINT support = 0; + const UINT required = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_SHADER_SAMPLE; + return (SUCCEEDED(m_device->CheckFormatSupport(dfmt, &support) && ((support & required) == required))); +} + +bool D3D11HostDisplay::BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) +{ + ClearDisplayTexture(); + + const DXGI_FORMAT dxgi_format = s_display_pixel_format_mapping[static_cast(format)]; + if (m_display_pixels_texture.GetWidth() < width || m_display_pixels_texture.GetHeight() < height || + m_display_pixels_texture.GetFormat() != dxgi_format) + { + if (!m_display_pixels_texture.Create(m_device.Get(), width, height, 1, dxgi_format, D3D11_BIND_SHADER_RESOURCE, + nullptr, 0, true)) + { + return false; + } + } + + D3D11_MAPPED_SUBRESOURCE sr; + HRESULT hr = m_context->Map(m_display_pixels_texture.GetD3DTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr); + if (FAILED(hr)) + { + Log_ErrorPrintf("Map pixels texture failed: %08X", hr); + return false; + } + + *out_buffer = sr.pData; + *out_pitch = sr.RowPitch; + + SetDisplayTexture(m_display_pixels_texture.GetD3DSRV(), format, m_display_pixels_texture.GetWidth(), + m_display_pixels_texture.GetHeight(), 0, 0, static_cast(width), static_cast(height)); + return true; +} + +void D3D11HostDisplay::EndSetDisplayPixels() +{ + m_context->Unmap(m_display_pixels_texture.GetD3DTexture(), 0); +} + void D3D11HostDisplay::SetVSync(bool enabled) { #ifndef LIBRETRO diff --git a/src/frontend-common/d3d11_host_display.h b/src/frontend-common/d3d11_host_display.h index adbf29a3d..5e48112e8 100644 --- a/src/frontend-common/d3d11_host_display.h +++ b/src/frontend-common/d3d11_host_display.h @@ -57,6 +57,10 @@ public: u32 texture_data_stride) override; bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, u32 out_data_stride) override; + bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const override; + bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) override; + void EndSetDisplayPixels() override; virtual void SetVSync(bool enabled) override; diff --git a/src/frontend-common/opengl_host_display.cpp b/src/frontend-common/opengl_host_display.cpp index b48d89bda..b3b78242b 100644 --- a/src/frontend-common/opengl_host_display.cpp +++ b/src/frontend-common/opengl_host_display.cpp @@ -1,4 +1,5 @@ #include "opengl_host_display.h" +#include "common/align.h" #include "common/assert.h" #include "common/log.h" #include @@ -122,6 +123,108 @@ bool OpenGLHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y return true; } +static constexpr std::array, static_cast(HostDisplayPixelFormat::Count)> + s_display_pixel_format_mapping = {{ + {}, // Unknown + {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8 + {GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 + {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 + {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV} // RGBA5551 + }}; + +bool OpenGLHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const +{ + return (std::get<0>(s_display_pixel_format_mapping[static_cast(format)]) != static_cast(0)); +} + +bool OpenGLHostDisplay::BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) +{ + const u32 pixel_size = GetDisplayPixelFormatSize(format); + const u32 stride = Common::AlignUpPow2(width * pixel_size, 4); + const u32 size_required = stride * height * pixel_size; + const u32 buffer_size = Common::AlignUpPow2(size_required * 2, 4 * 1024 * 1024); + if (!m_display_pixels_texture_pbo || m_display_pixels_texture_pbo->GetSize() < buffer_size) + { + m_display_pixels_texture_pbo.reset(); + m_display_pixels_texture_pbo = GL::StreamBuffer::Create(GL_PIXEL_UNPACK_BUFFER, buffer_size); + if (!m_display_pixels_texture_pbo) + return false; + } + + const auto map = m_display_pixels_texture_pbo->Map(GetDisplayPixelFormatSize(format), size_required); + m_display_texture_format = format; + m_display_pixels_texture_pbo_map_offset = map.buffer_offset; + m_display_pixels_texture_pbo_map_size = size_required; + *out_buffer = map.pointer; + *out_pitch = stride; + + if (m_display_pixels_texture_id == 0) + { + glGenTextures(1, &m_display_pixels_texture_id); + glBindTexture(GL_TEXTURE_2D, m_display_pixels_texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + } + + SetDisplayTexture(reinterpret_cast(static_cast(m_display_pixels_texture_id)), format, width, height, + 0, 0, width, height); + return true; +} + +void OpenGLHostDisplay::EndSetDisplayPixels() +{ + const u32 width = static_cast(m_display_texture_view_width); + const u32 height = static_cast(m_display_texture_view_height); + + const auto [gl_internal_format, gl_format, gl_type] = + s_display_pixel_format_mapping[static_cast(m_display_texture_format)]; + + // glTexImage2D should be quicker on Mali... + m_display_pixels_texture_pbo->Unmap(m_display_pixels_texture_pbo_map_size); + m_display_pixels_texture_pbo->Bind(); + glBindTexture(GL_TEXTURE_2D, m_display_pixels_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, + reinterpret_cast(static_cast(m_display_pixels_texture_pbo_map_offset))); + glBindTexture(GL_TEXTURE_2D, 0); + m_display_pixels_texture_pbo->Unbind(); + + m_display_pixels_texture_pbo_map_offset = 0; + m_display_pixels_texture_pbo_map_size = 0; +} + +bool OpenGLHostDisplay::SetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, const void* buffer, + u32 pitch) +{ + if (m_display_pixels_texture_id == 0) + { + glGenTextures(1, &m_display_pixels_texture_id); + glBindTexture(GL_TEXTURE_2D, m_display_pixels_texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + } + else + { + glBindTexture(GL_TEXTURE_2D, m_display_pixels_texture_id); + } + + const auto [gl_internal_format, gl_format, gl_type] = s_display_pixel_format_mapping[static_cast(format)]; + + glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, buffer); + + glBindTexture(GL_TEXTURE_2D, 0); + + SetDisplayTexture(reinterpret_cast(static_cast(m_display_pixels_texture_id)), format, width, height, + 0, 0, width, height); + return true; +} + void OpenGLHostDisplay::SetVSync(bool enabled) { if (m_gl_context->GetWindowInfo().type == WindowInfo::Type::Surfaceless) @@ -451,6 +554,12 @@ void OpenGLHostDisplay::DestroyResources() m_post_processing_stages.clear(); #endif + if (m_display_pixels_texture_id != 0) + { + glDeleteTextures(1, &m_display_pixels_texture_id); + m_display_pixels_texture_id = 0; + } + if (m_display_vao != 0) glDeleteVertexArrays(1, &m_display_vao); if (m_display_linear_sampler != 0) diff --git a/src/frontend-common/opengl_host_display.h b/src/frontend-common/opengl_host_display.h index 98de0b33d..616486ef0 100644 --- a/src/frontend-common/opengl_host_display.h +++ b/src/frontend-common/opengl_host_display.h @@ -57,6 +57,11 @@ public: u32 texture_data_stride) override; bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, u32 out_data_stride) override; + bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const override; + bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) override; + void EndSetDisplayPixels() override; + bool SetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, const void* buffer, u32 pitch) override; virtual void SetVSync(bool enabled) override; @@ -104,6 +109,11 @@ protected: GLuint m_display_linear_sampler = 0; GLuint m_uniform_buffer_alignment = 1; + GLuint m_display_pixels_texture_id = 0; + std::unique_ptr m_display_pixels_texture_pbo; + u32 m_display_pixels_texture_pbo_map_offset = 0; + u32 m_display_pixels_texture_pbo_map_size = 0; + #ifndef LIBRETRO PostProcessingChain m_post_processing_chain; GL::Texture m_post_processing_input_texture; diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp index 73d49b8ea..60d0b793a 100644 --- a/src/frontend-common/vulkan_host_display.cpp +++ b/src/frontend-common/vulkan_host_display.cpp @@ -247,6 +247,60 @@ bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y return true; } +static constexpr std::array(HostDisplayPixelFormat::Count)> s_display_pixel_format_mapping = + {{VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B5G6R5_UNORM_PACK16, + VK_FORMAT_A1R5G5B5_UNORM_PACK16}}; + +bool VulkanHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const +{ + const VkFormat vk_format = s_display_pixel_format_mapping[static_cast(format)]; + if (vk_format == VK_FORMAT_UNDEFINED) + return false; + + VkFormatProperties fp = {}; + vkGetPhysicalDeviceFormatProperties(g_vulkan_context->GetPhysicalDevice(), vk_format, &fp); + + const VkFormatFeatureFlags required = (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + return ((fp.optimalTilingFeatures & required) == required); +} + +bool VulkanHostDisplay::BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) +{ + const VkFormat vk_format = s_display_pixel_format_mapping[static_cast(format)]; + + if (m_display_pixels_texture.GetWidth() < width || m_display_pixels_texture.GetHeight() < height || + m_display_pixels_texture.GetFormat() != vk_format) + { + if (!m_display_pixels_texture.Create(width, height, 1, 1, vk_format, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)) + { + return false; + } + } + + if ((m_upload_staging_texture.GetWidth() < width || m_upload_staging_texture.GetHeight() < height) && + !m_upload_staging_texture.Create(Vulkan::StagingBuffer::Type::Upload, vk_format, width, height)) + { + return false; + } + + SetDisplayTexture(&m_display_pixels_texture, format, m_display_pixels_texture.GetWidth(), + m_display_pixels_texture.GetHeight(), 0, 0, width, height); + + *out_buffer = m_upload_staging_texture.GetMappedPointer(); + *out_pitch = m_upload_staging_texture.GetMappedStride(); + return true; +} + +void VulkanHostDisplay::EndSetDisplayPixels() +{ + m_upload_staging_texture.CopyToTexture(0, 0, m_display_pixels_texture, 0, 0, 0, 0, + static_cast(m_display_texture_view_width), + static_cast(m_display_texture_view_height)); +} + void VulkanHostDisplay::SetVSync(bool enabled) { if (!m_swap_chain) @@ -461,6 +515,7 @@ void VulkanHostDisplay::DestroyResources() m_post_processing_chain.ClearStages(); #endif + m_display_pixels_texture.Destroy(false); m_readback_staging_texture.Destroy(false); m_upload_staging_texture.Destroy(false); diff --git a/src/frontend-common/vulkan_host_display.h b/src/frontend-common/vulkan_host_display.h index 991d311dc..3ca21c232 100644 --- a/src/frontend-common/vulkan_host_display.h +++ b/src/frontend-common/vulkan_host_display.h @@ -55,6 +55,11 @@ public: bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, u32 out_data_stride) override; + bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const override; + bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer, + u32* out_pitch) override; + void EndSetDisplayPixels() override; + virtual void SetVSync(bool enabled) override; virtual bool Render() override; @@ -117,6 +122,7 @@ protected: VkSampler m_point_sampler = VK_NULL_HANDLE; VkSampler m_linear_sampler = VK_NULL_HANDLE; + Vulkan::Texture m_display_pixels_texture; Vulkan::StagingTexture m_upload_staging_texture; Vulkan::StagingTexture m_readback_staging_texture;