From 0b690a2bcd3accc58dd7d3dd6d6dcf79a84cee96 Mon Sep 17 00:00:00 2001 From: aliaspider Date: Sat, 15 Apr 2023 16:16:24 +0100 Subject: [PATCH] allow software renderer to use the preferred hw_render interface, and to run in full software mode if none is available. --- libretro/main.cpp | 97 ++++++---- pcsx2/CMakeLists.txt | 3 + pcsx2/GS/GS.cpp | 27 ++- pcsx2/GS/Renderers/Null/GSDeviceNull.cpp | 223 +++++++++++++++++++++++ pcsx2/GS/Renderers/Null/GSDeviceNull.h | 94 ++++++++++ 5 files changed, 403 insertions(+), 41 deletions(-) create mode 100644 pcsx2/GS/Renderers/Null/GSDeviceNull.cpp create mode 100644 pcsx2/GS/Renderers/Null/GSDeviceNull.h diff --git a/libretro/main.cpp b/libretro/main.cpp index e58e4a5c19763..b95d8aeb1fa71 100644 --- a/libretro/main.cpp +++ b/libretro/main.cpp @@ -368,12 +368,12 @@ void retro_get_system_av_info(retro_system_av_info* info) if (Options::renderer == "Software" || Options::renderer == "Null") { info->geometry.base_width = 640; - info->geometry.base_height = 480; + info->geometry.base_height = 448; } else { info->geometry.base_width = 640 * Options::upscale_multiplier; - info->geometry.base_height = 480 * Options::upscale_multiplier; + info->geometry.base_height = 448 * Options::upscale_multiplier; } info->geometry.max_width = info->geometry.base_width; @@ -498,11 +498,11 @@ static bool set_hw_render(retro_hw_context_type type) bool select_hw_render() { - if (Options::renderer == "Auto") + if (Options::renderer == "Auto" || Options::renderer == "Software") { - retro_hw_context_type context_type = RETRO_HW_CONTEXT_OPENGL_CORE; + retro_hw_context_type context_type = RETRO_HW_CONTEXT_NONE; environ_cb(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &context_type); - if (set_hw_render(context_type)) + if (context_type != RETRO_HW_CONTEXT_NONE && set_hw_render(context_type)) return true; } #ifdef _WIN32 @@ -536,6 +536,8 @@ bool select_hw_render() if (set_hw_render(RETRO_HW_CONTEXT_D3D12)) return true; #endif + if (Options::renderer == "Software") + return set_hw_render(RETRO_HW_CONTEXT_NONE); return false; } @@ -627,6 +629,9 @@ static const VkApplicationInfo *get_application_info_vulkan(void) { #endif bool retro_load_game(const struct retro_game_info* game) { + int format = RETRO_PIXEL_FORMAT_XRGB8888; + environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &format); + const char* system_base = nullptr; environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_base); std::string system = Path::Combine(system_base, "pcsx2"); @@ -670,41 +675,41 @@ bool retro_load_game(const struct retro_game_info* game) if(!select_hw_render()) return false; - switch (hw_render.context_type) - { - case RETRO_HW_CONTEXT_D3D12: - s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::DX12); - break; - case RETRO_HW_CONTEXT_D3D11: - s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::DX11); - break; + if(Options::renderer == "Software") + s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::SW); + else + switch (hw_render.context_type) + { + case RETRO_HW_CONTEXT_D3D12: + s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::DX12); + break; + case RETRO_HW_CONTEXT_D3D11: + s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::DX11); + break; #ifdef ENABLE_VULKAN - case RETRO_HW_CONTEXT_VULKAN: - s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::VK); - { - static const struct retro_hw_render_context_negotiation_interface_vulkan iface = { - RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN, - RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION, - get_application_info_vulkan, - create_device_vulkan, // Callback above. - nullptr, - }; - environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE, (void *)&iface); - } - Vulkan::LoadVulkanLibrary(); - vk_libretro_init_wraps(); - break; + case RETRO_HW_CONTEXT_VULKAN: + s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::VK); + { + static const struct retro_hw_render_context_negotiation_interface_vulkan iface = { + RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN, + RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION, + get_application_info_vulkan, + create_device_vulkan, // Callback above. + nullptr, + }; + environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE, (void *)&iface); + } + Vulkan::LoadVulkanLibrary(); + vk_libretro_init_wraps(); + break; #endif - case RETRO_HW_CONTEXT_NONE: - s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::Null); - break; - default: - if(Options::renderer == "Software") - s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::SW); - else + case RETRO_HW_CONTEXT_NONE: + s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::Null); + break; + default: s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", (int)GSRendererType::OGL); - break; - } + break; + } VMManager::ApplySettings(); @@ -719,6 +724,9 @@ bool retro_load_game(const struct retro_game_info* game) cpu_thread = std::thread(cpu_thread_entry, boot_params); + if(hw_render.context_type == RETRO_HW_CONTEXT_NONE) + GetMTGS().TryOpenGS(); + return true; } @@ -730,6 +738,12 @@ bool retro_load_game_special(unsigned game_type, const struct retro_game_info* i void retro_unload_game(void) { + if(GetMTGS().IsOpen()) + { + cpu_thread_pause(); + GetMTGS().CloseGS(); + } + VMManager::Shutdown(false); cpu_thread.join(); #ifdef ENABLE_VULKAN @@ -804,6 +818,9 @@ void retro_run(void) read_pos += samples; read_pos &= SOUND_BUFFER_MASK; + + if (EmuConfig.GS.Renderer == GSRendererType::Null) + video_cb(NULL, 0, 0, 0); } void Host::BeginPresentFrame() @@ -843,7 +860,7 @@ std::optional Host::AcquireRenderWindow(RenderAPI api) { WindowInfo wi; wi.surface_width = 640 * Options::upscale_multiplier; - wi.surface_height = 480 * Options::upscale_multiplier; + wi.surface_height = 448 * Options::upscale_multiplier; wi.surface_scale = 1.0f; wi.type = WindowInfo::Type::Libretro; if(api == RenderAPI::Vulkan) @@ -856,7 +873,7 @@ std::optional Host::UpdateRenderWindow() { WindowInfo wi; wi.surface_width = 640 * Options::upscale_multiplier; - wi.surface_height = 480 * Options::upscale_multiplier; + wi.surface_height = 448 * Options::upscale_multiplier; wi.surface_scale = 1.0f; wi.type = WindowInfo::Type::Libretro; if(hw_render.context_type == RETRO_HW_CONTEXT_VULKAN) @@ -1217,7 +1234,7 @@ std::optional Host::GetTopLevelWindowInfo() { WindowInfo wi; wi.surface_width = 640; - wi.surface_height = 480; + wi.surface_height = 448; wi.surface_scale = 1.0f; wi.type = WindowInfo::Type::Libretro; diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index b250105171384..9d7b2b3c93b61 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -1074,6 +1074,9 @@ if(LIBRETRO) SPU2/Linux/Alsa.cpp SPU2/Linux/Dialogs.cpp ) + LIST(APPEND pcsx2GSSources + GS/Renderers/Null/GSDeviceNull.cpp + ) endif() # These ones benefit a lot from LTO diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index b1f1812ff8551..a94ca76316726 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -64,6 +64,10 @@ static HRESULT s_hr = E_FAIL; #endif +#ifdef __LIBRETRO__ +#include "Renderers/Null/GSDeviceNull.h" +#endif + #include // do NOT undefine this/put it above includes, as x11 people love to redefine @@ -108,6 +112,22 @@ void GSshutdown() static RenderAPI GetAPIForRenderer(GSRendererType renderer) { +#ifdef __LIBRETRO__ + switch(hw_render.context_type) + { + case RETRO_HW_CONTEXT_D3D11: + return RenderAPI::D3D11; + case RETRO_HW_CONTEXT_D3D12: + return RenderAPI::D3D12; + case RETRO_HW_CONTEXT_VULKAN: + return RenderAPI::Vulkan; + case RETRO_HW_CONTEXT_NONE: + return RenderAPI::None; + default: + break; + } + return RenderAPI::OpenGL; +#else switch (renderer) { case GSRendererType::OGL: @@ -132,6 +152,7 @@ static RenderAPI GetAPIForRenderer(GSRendererType renderer) default: return GetAPIForRenderer(GSUtil::GetPreferredRenderer()); } +#endif } static void UpdateExclusiveFullscreen(bool force_off) @@ -209,7 +230,11 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail) g_gs_device = std::make_unique(); break; #endif - +#ifdef __LIBRETRO__ + case RenderAPI::None: + g_gs_device = std::make_unique(); + break; +#endif default: Console.Error("Unsupported render API %s", GSDevice::RenderAPIToString(s_render_api)); return false; diff --git a/pcsx2/GS/Renderers/Null/GSDeviceNull.cpp b/pcsx2/GS/Renderers/Null/GSDeviceNull.cpp new file mode 100644 index 0000000000000..30be7f7e1d4d1 --- /dev/null +++ b/pcsx2/GS/Renderers/Null/GSDeviceNull.cpp @@ -0,0 +1,223 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "GS/Renderers/Null/GSDeviceNull.h" +#include "GS.h" + +#ifdef __LIBRETRO__ +#include +extern retro_video_refresh_t video_cb; +#endif + + +GSTextureNull::GSTextureNull(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) +{ + buffer = (u32*)calloc(640 * 448, 4); + m_size.x = width; + m_size.y = height; +} +GSTextureNull::~GSTextureNull() +{ + free(buffer); +} +bool GSTextureNull::Update(const GSVector4i& r, const void* data, int pitch, int layer) +{ + u32* src = (u32*) data; +// int field = ((GSDeviceNull*)g_gs_device.get())->m_field; + int field = (((u32&)RingBuffer.Regs[0x1000]) & 0x2000) ? 0 : 1; + + for(int j = field; j < 448; j+=2) + for(int i = 0; i < 640; i++) + { + u32 col = src[i + (j >> 1) * (pitch >> 2)]; + buffer[i + j * 640] = (col & 0xFF00FF00) | (col & 0x00FF0000) >> 16 | (col & 0x000000FF) << 16; + } + + video_cb(buffer, 640, 448, 640 * 4); + + return true; +} +bool GSTextureNull::Map(GSMap& m, const GSVector4i* r, int layer) +{ + m.bits = (u8*)buffer; + m.pitch = GetWidth(); + return true; +} +void GSTextureNull::Unmap() +{ + +} + +GSTexture* GSDeviceNull::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) +{ + return new GSTextureNull(type, width, height, levels, format); +} +void GSDeviceNull::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, + const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) +{ + +} +void GSDeviceNull::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) +{ + m_field = ((int)cb.ZrH.x & 0x1) ^ 0x1; +} +void GSDeviceNull::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) +{ + +} +void GSDeviceNull::DoFXAA(GSTexture* sTex, GSTexture* dTex) +{ + +} +bool GSDeviceNull::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array& constants) +{ + return true; +} +GSDeviceNull::GSDeviceNull() +{ + +} +GSDeviceNull::~GSDeviceNull() +{ + +} + +RenderAPI GSDeviceNull::GetRenderAPI() const +{ + return RenderAPI::None; +} +bool GSDeviceNull::HasSurface() const +{ + return true; +} +void GSDeviceNull::DestroySurface() +{ + +} +bool GSDeviceNull::ChangeWindow(const WindowInfo& new_wi) +{ + return true; +} +void GSDeviceNull::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) +{ + +} +bool GSDeviceNull::SupportsExclusiveFullscreen() const +{ + return true; +} +bool GSDeviceNull::IsExclusiveFullscreen() +{ + return true; +} +bool GSDeviceNull::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) +{ + return true; +} +GSDeviceNull::PresentResult GSDeviceNull::BeginPresent(bool frame_skip) +{ + return PresentResult::OK; +} +void GSDeviceNull::EndPresent() +{ + +} +void GSDeviceNull::SetVSync(VsyncMode mode) +{ + +} +std::string GSDeviceNull::GetDriverInfo() const +{ + return ""; +} +bool GSDeviceNull::SetGPUTimingEnabled(bool enabled) +{ + return true; +} +float GSDeviceNull::GetAndResetAccumulatedGPUTime() +{ + return 0.0f; +} +void GSDeviceNull::ClearRenderTarget(GSTexture* t, const GSVector4& c) +{ + +} +void GSDeviceNull::ClearRenderTarget(GSTexture* t, u32 c) +{ + +} +void GSDeviceNull::InvalidateRenderTarget(GSTexture* t) +{ + +} +void GSDeviceNull::ClearDepth(GSTexture* t) +{ + +} +void GSDeviceNull::ClearStencil(GSTexture* t, u8 c) +{ + +} +void GSDeviceNull::PushDebugGroup(const char* fmt, ...) +{ + +} +void GSDeviceNull::PopDebugGroup() +{ + +} +void GSDeviceNull::InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) +{ + +} +std::unique_ptr GSDeviceNull::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) +{ + return nullptr; +} +void GSDeviceNull::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) +{ + +} +void GSDeviceNull::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, + ShaderConvert shader, bool linear) +{ + +} +void GSDeviceNull::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, + bool green, bool blue, bool alpha) +{ + +} +void GSDeviceNull::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, + PresentShader shader, float shaderTime, bool linear) +{ + +} +void GSDeviceNull::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) +{ + +} +void GSDeviceNull::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) +{ + +} +void GSDeviceNull::RenderHW(GSHWDrawConfig& config) +{ + +} +void GSDeviceNull::ClearSamplerCache() +{ + +} diff --git a/pcsx2/GS/Renderers/Null/GSDeviceNull.h b/pcsx2/GS/Renderers/Null/GSDeviceNull.h new file mode 100644 index 0000000000000..a77c6c995ef39 --- /dev/null +++ b/pcsx2/GS/Renderers/Null/GSDeviceNull.h @@ -0,0 +1,94 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include "GS/GSVector.h" +#include "GS/Renderers/Common/GSDevice.h" + +class GSTextureNull final : public GSTexture +{ + u32* buffer; +public: + GSTextureNull(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format); + ~GSTextureNull(); + + void* GetNativeHandle() const override { return (void*)buffer; } + bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override; + bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override; + void Unmap() override; +}; + +class GSDeviceNull final : public GSDevice +{ +public: + int m_field = 0; + GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override; + void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, + const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) final; + void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final; + void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; + void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; + bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array& constants) final; + + GSDeviceNull(); + ~GSDeviceNull() override; + + RenderAPI GetRenderAPI() const override; + bool HasSurface() const override; + void DestroySurface() override; + + bool ChangeWindow(const WindowInfo& new_wi) override; + void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override; + bool SupportsExclusiveFullscreen() const override; + bool IsExclusiveFullscreen() override; + bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override; + + PresentResult BeginPresent(bool frame_skip) override; + void EndPresent() override; + void SetVSync(VsyncMode mode) override; + + std::string GetDriverInfo() const override; + bool SetGPUTimingEnabled(bool enabled) override; + float GetAndResetAccumulatedGPUTime() override; + + void ClearRenderTarget(GSTexture* t, const GSVector4& c) override; + void ClearRenderTarget(GSTexture* t, u32 c) override; + void InvalidateRenderTarget(GSTexture* t) override; + void ClearDepth(GSTexture* t) override; + void ClearStencil(GSTexture* t, u8 c) override; + + void PushDebugGroup(const char* fmt, ...) override; + void PopDebugGroup() override; + void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override; + + std::unique_ptr CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override; + + void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override; + + void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, + ShaderConvert shader = ShaderConvert::COPY, bool linear = true) override; + void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, + bool green, bool blue, bool alpha) override; + void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, + PresentShader shader, float shaderTime, bool linear) override; + + void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override; + void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override; + + void RenderHW(GSHWDrawConfig& config) override; + + void ClearSamplerCache() override; +};