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