GPU/Software: Reduce number of copies by one, enable 16-bit scanout

This commit is contained in:
Connor McLaughlin
2020-10-22 01:25:33 +10:00
parent beffbaee39
commit d3d881aa6b
20 changed files with 875 additions and 187 deletions

View File

@ -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;

View File

@ -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;
};

View File

@ -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()

View File

@ -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();