mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 22:35:39 +00:00
GPU/Software: Reduce number of copies by one, enable 16-bit scanout
This commit is contained in:
parent
beffbaee39
commit
d3d881aa6b
|
@ -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<bool>(m_texture); }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
ALWAYS_INLINE operator bool() const { return static_cast<bool>(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<ID3D11Texture2D> texture);
|
||||
|
||||
void Destroy();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -656,17 +656,18 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
|||
{
|
||||
UpdateVRAMReadTexture();
|
||||
|
||||
m_host_display->SetDisplayTexture(
|
||||
reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_read_texture.GetGLId())), m_vram_read_texture.GetWidth(),
|
||||
static_cast<s32>(m_vram_read_texture.GetHeight()), 0, m_vram_read_texture.GetHeight(),
|
||||
m_vram_read_texture.GetWidth(), -static_cast<s32>(m_vram_read_texture.GetHeight()));
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_read_texture.GetGLId())),
|
||||
HostDisplayPixelFormat::RGBA8, m_vram_read_texture.GetWidth(),
|
||||
static_cast<s32>(m_vram_read_texture.GetHeight()), 0,
|
||||
m_vram_read_texture.GetHeight(), m_vram_read_texture.GetWidth(),
|
||||
-static_cast<s32>(m_vram_read_texture.GetHeight()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
|
||||
m_vram_texture.GetWidth(), static_cast<s32>(m_vram_texture.GetHeight()), 0,
|
||||
m_vram_texture.GetHeight(), m_vram_texture.GetWidth(),
|
||||
-static_cast<s32>(m_vram_texture.GetHeight()));
|
||||
HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
|
||||
static_cast<s32>(m_vram_texture.GetHeight()), 0, m_vram_texture.GetHeight(),
|
||||
m_vram_texture.GetWidth(), -static_cast<s32>(m_vram_texture.GetHeight()));
|
||||
}
|
||||
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
|
||||
static_cast<float>(VRAM_WIDTH) / static_cast<float>(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<void*>(static_cast<uintptr_t>(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<s32>(scaled_display_height));
|
||||
}
|
||||
|
@ -723,8 +725,8 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
|||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(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<s32>(scaled_display_height));
|
||||
|
||||
// restore state
|
||||
|
|
|
@ -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<float>(VRAM_WIDTH) / static_cast<float>(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();
|
||||
}
|
||||
|
|
|
@ -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 <algorithm>
|
||||
Log_SetChannel(GPU_SW);
|
||||
|
||||
#if defined(CPU_X64)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(CPU_AARCH64)
|
||||
#ifdef _MSC_VER
|
||||
#include <arm64_neon.h>
|
||||
#else
|
||||
#include <arm_neon.h>
|
||||
#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<HostDisplayPixelFormat out_format, typename out_type>
|
||||
static void CopyOutRow16(const u16* src_ptr, out_type* dst_ptr, u32 width);
|
||||
|
||||
template<HostDisplayPixelFormat out_format, typename out_type>
|
||||
static out_type VRAM16ToOutput(u16 value);
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE u16 VRAM16ToOutput<HostDisplayPixelFormat::RGBA5551, u16>(u16 value)
|
||||
{
|
||||
return (value & 0x3E0) | ((value >> 10) & 0x1F) | ((value & 0x1F) << 10);
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE u16 VRAM16ToOutput<HostDisplayPixelFormat::RGB565, u16>(u16 value)
|
||||
{
|
||||
return ((value & 0x3E0) << 1) | ((value & 0x20) << 1) | ((value >> 10) & 0x1F) | ((value & 0x1F) << 11);
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE u32 VRAM16ToOutput<HostDisplayPixelFormat::RGBA8, u32>(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<HostDisplayPixelFormat::BGRA8, u32>(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<HostDisplayPixelFormat::RGBA5551, u16>(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<const __m128i*>(src_ptr));
|
||||
src_ptr += 8;
|
||||
__m128i a = _mm_and_si128(value, _mm_set1_epi16(static_cast<s16>(static_cast<u16>(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<HostDisplayPixelFormat::RGBA5551, u16>(*(src_ptr++));
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE void CopyOutRow16<HostDisplayPixelFormat::RGB565, u16>(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<const __m128i*>(src_ptr));
|
||||
src_ptr += 8;
|
||||
__m128i a = _mm_slli_epi16(_mm_and_si128(value, _mm_set1_epi16(static_cast<s16>(static_cast<u16>(0x3E0)))), 1);
|
||||
__m128i b = _mm_slli_epi16(_mm_and_si128(value, _mm_set1_epi16(static_cast<s16>(static_cast<u16>(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<HostDisplayPixelFormat::RGB565, u16>(*(src_ptr++));
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE void CopyOutRow16<HostDisplayPixelFormat::RGBA8, u32>(const u16* src_ptr, u32* dst_ptr, u32 width)
|
||||
{
|
||||
for (u32 col = 0; col < width; col++)
|
||||
*(dst_ptr++) = VRAM16ToOutput<HostDisplayPixelFormat::RGBA8, u32>(*(src_ptr++));
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE void CopyOutRow16<HostDisplayPixelFormat::BGRA8, u32>(const u16* src_ptr, u32* dst_ptr, u32 width)
|
||||
{
|
||||
for (u32 col = 0; col < width; col++)
|
||||
*(dst_ptr++) = VRAM16ToOutput<HostDisplayPixelFormat::BGRA8, u32>(*(src_ptr++));
|
||||
}
|
||||
|
||||
template<HostDisplayPixelFormat display_format>
|
||||
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<void**>(&dst_ptr),
|
||||
&dst_stride))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_stride = Common::AlignUpPow2<u32>(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<display_format>(src_ptr, reinterpret_cast<OutputPixelType*>(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<OutputPixelType*>(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<display_format, OutputPixelType>(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<HostDisplayPixelFormat::RGBA5551>(src_x, src_y, width, height, field, interlaced, interleaved);
|
||||
break;
|
||||
case HostDisplayPixelFormat::RGB565:
|
||||
CopyOut15Bit<HostDisplayPixelFormat::RGB565>(src_x, src_y, width, height, field, interlaced, interleaved);
|
||||
break;
|
||||
case HostDisplayPixelFormat::RGBA8:
|
||||
CopyOut15Bit<HostDisplayPixelFormat::RGBA8>(src_x, src_y, width, height, field, interlaced, interleaved);
|
||||
break;
|
||||
case HostDisplayPixelFormat::BGRA8:
|
||||
CopyOut15Bit<HostDisplayPixelFormat::BGRA8>(src_x, src_y, width, height, field, interlaced, interleaved);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<HostDisplayPixelFormat display_format>
|
||||
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<void**>(&dst_ptr),
|
||||
&dst_stride))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_stride = Common::AlignUpPow2<u32>(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<const u8*>(&m_vram[src_y * VRAM_WIDTH + src_x]);
|
||||
const u8* src_ptr = reinterpret_cast<const u8*>(&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<u8*>(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<u8*>(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<u8*>(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<u16*>(dst_ptr);
|
||||
for (u32 col = 0; col < width; col++)
|
||||
{
|
||||
*(dst_row_ptr++) = ((static_cast<u16>(src_row_ptr[0]) >> 3) << 11) |
|
||||
((static_cast<u16>(src_row_ptr[1]) >> 2) << 5) | (static_cast<u16>(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<u16*>(dst_ptr);
|
||||
for (u32 col = 0; col < width; col++)
|
||||
{
|
||||
*(dst_row_ptr++) = ((static_cast<u16>(src_row_ptr[0]) >> 3) << 10) |
|
||||
((static_cast<u16>(src_row_ptr[1]) >> 3) << 5) | (static_cast<u16>(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<OutputPixelType*>(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<u8>(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<HostDisplayPixelFormat::RGBA5551>(src_x, src_y, skip_x, width, height, field, interlaced,
|
||||
interleaved);
|
||||
break;
|
||||
case HostDisplayPixelFormat::RGB565:
|
||||
CopyOut24Bit<HostDisplayPixelFormat::RGB565>(src_x, src_y, skip_x, width, height, field, interlaced, interleaved);
|
||||
break;
|
||||
case HostDisplayPixelFormat::RGBA8:
|
||||
CopyOut24Bit<HostDisplayPixelFormat::RGBA8>(src_x, src_y, skip_x, width, height, field, interlaced, interleaved);
|
||||
break;
|
||||
case HostDisplayPixelFormat::BGRA8:
|
||||
CopyOut24Bit<HostDisplayPixelFormat::BGRA8>(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<float>(VRAM_WIDTH) / static_cast<float>(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<bool texture_enable, bool raw_texture_enable, bool transparency_enable, bool dithering_enable>
|
||||
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;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
#include "common/heap_array.h"
|
||||
#include "gpu.h"
|
||||
#include "host_display.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
@ -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<HostDisplayPixelFormat display_format>
|
||||
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<HostDisplayPixelFormat display_format>
|
||||
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<u32> m_display_texture_buffer;
|
||||
std::unique_ptr<HostDisplayTexture> m_display_texture;
|
||||
|
||||
std::array<u16, VRAM_WIDTH * VRAM_HEIGHT> m_vram;
|
||||
HeapArray<u8, VRAM_WIDTH * VRAM_HEIGHT * sizeof(u32)> m_display_texture_buffer;
|
||||
HostDisplayPixelFormat m_16bit_display_format = HostDisplayPixelFormat::RGB565;
|
||||
HostDisplayPixelFormat m_24bit_display_format = HostDisplayPixelFormat::RGBA8;
|
||||
};
|
||||
|
|
|
@ -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<const u8*>(buffer);
|
||||
u8* dst_ptr = static_cast<u8*>(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<HostDisplayTexture> 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<u32>* 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);
|
||||
|
|
|
@ -7,6 +7,16 @@
|
|||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
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;
|
||||
|
|
|
@ -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 <tuple>
|
||||
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<LibretroDisplayTexture*>(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<u8*>(dst);
|
||||
const u8* src_ptr = static_cast<const u8*>(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<u8*>(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<const u8*>(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<LibretroDisplayTexture> Create(u32 width, u32 height, const void* initial_data,
|
||||
u32 initial_data_stride)
|
||||
{
|
||||
std::unique_ptr<LibretroDisplayTexture> tex = std::make_unique<LibretroDisplayTexture>(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<u32> 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<unsigned>(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<HostDisplayTexture> 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<LibretroDisplayTexture*>(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<const LibretroDisplayTexture*>(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<u32>(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<const LibretroDisplayTexture*>(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;
|
||||
|
|
|
@ -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<u32> 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;
|
||||
};
|
||||
|
|
|
@ -237,7 +237,7 @@ void LibretroHostInterface::GetSystemAVInfo(struct retro_system_av_info* info, b
|
|||
info->timing.sample_rate = static_cast<double>(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()
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -171,6 +171,58 @@ bool D3D11HostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y,
|
|||
static_cast<u32*>(out_data));
|
||||
}
|
||||
|
||||
static constexpr std::array<DXGI_FORMAT, static_cast<u32>(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<u32>(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<u32>(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<u32>(width), static_cast<u32>(height));
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::EndSetDisplayPixels()
|
||||
{
|
||||
m_context->Unmap(m_display_pixels_texture.GetD3DTexture(), 0);
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::SetVSync(bool enabled)
|
||||
{
|
||||
#ifndef LIBRETRO
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "opengl_host_display.h"
|
||||
#include "common/align.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include <array>
|
||||
|
@ -122,6 +123,108 @@ bool OpenGLHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y
|
|||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<std::tuple<GLenum, GLenum, GLenum>, static_cast<u32>(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<u32>(format)]) != static_cast<GLenum>(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<void*>(static_cast<uintptr_t>(m_display_pixels_texture_id)), format, width, height,
|
||||
0, 0, width, height);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::EndSetDisplayPixels()
|
||||
{
|
||||
const u32 width = static_cast<u32>(m_display_texture_view_width);
|
||||
const u32 height = static_cast<u32>(m_display_texture_view_height);
|
||||
|
||||
const auto [gl_internal_format, gl_format, gl_type] =
|
||||
s_display_pixel_format_mapping[static_cast<u32>(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<void*>(static_cast<uintptr_t>(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<u32>(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<void*>(static_cast<uintptr_t>(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)
|
||||
|
|
|
@ -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<GL::StreamBuffer> 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;
|
||||
|
|
|
@ -247,6 +247,60 @@ bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y
|
|||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<VkFormat, static_cast<u32>(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<u32>(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<u32>(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<u32>(m_display_texture_view_width),
|
||||
static_cast<u32>(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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue