Initial libretro rebase

This commit is contained in:
libretroadmin 2023-04-26 18:26:23 +02:00
parent 0367851b8e
commit 5112943bbe
78 changed files with 8233 additions and 47 deletions

View File

@ -61,7 +61,7 @@ KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 3
NamespaceIndentation: All
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true

11
.gitignore vendored
View File

@ -99,3 +99,14 @@ oprofile_data/
CMakeSettings.json
/ci-artifacts/
/out/
bin/*
build*/
*.cflags
*.config
*.creator
*.creator.user.*
*.cxxflags
*.files
*.includes
*.autosave

View File

@ -47,7 +47,7 @@ typedef unsigned long ulong;
#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
#if (defined(__GNUC__) && !defined(ANDROID))
#if (defined(__GNUC__) && !defined(ANDROID)) && !defined(__APPLE__)
// In GCC, include soundtouch_config.h made by config scritps.
// Skip this in Android compilation that uses GCC but without configure scripts.
#include "soundtouch_config.h"

View File

@ -32,6 +32,13 @@ include(Pcsx2Utils)
check_no_parenthesis_in_path()
detectOperatingSystem()
check_compiler_version("7.0" "7.0")
if(NOT MSVC)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_FOUND})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_FOUND})
endif()
endif()
#-------------------------------------------------------------------------------
# Include specific module
@ -60,6 +67,10 @@ if (QT_BUILD)
endif()
endif()
if(LIBRETRO)
add_subdirectory(libretro)
endif()
# tests
if(ACTUALLY_ENABLE_TESTS)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

View File

@ -6,13 +6,28 @@ set(PCSX2_DEFS "")
#-------------------------------------------------------------------------------
option(DISABLE_BUILD_DATE "Disable including the binary compile date")
option(ENABLE_TESTS "Enables building the unit tests" ON)
option(LIBRETRO "Enables building the libretro core" OFF)
set(USE_SYSTEM_LIBS "AUTO" CACHE STRING "Use system libraries instead of bundled libraries. ON - Always use system and fail if unavailable, OFF - Always use bundled, AUTO - Use system if available, otherwise use bundled. Default is AUTO")
if(LIBRETRO)
set(ENABLE_TESTS OFF)
set(CMAKE_BUILD_PO FALSE)
set(BUILD_REPLAY_LOADERS FALSE)
set(CUBEB_API FALSE)
set(DISABLE_SETCAP TRUE)
set(USE_DISCORD_PRESENCE FALSE)
set(USE_ACHIEVEMENTS OFF)
set(QT_BUILD OFF)
set(USE_SYSTEM_LIBS OFF)
add_definitions(-D__LIBRETRO__)
endif()
optional_system_library(fmt)
optional_system_library(ryml)
optional_system_library(zstd)
optional_system_library(libzip)
optional_system_library(SDL2)
option(LTO_PCSX2_CORE "Enable LTO/IPO/LTCG on the subset of pcsx2 that benefits most from it but not anything else")
option(USE_VTUNE "Plug VTUNE to profile GS JIT.")
option(USE_ACHIEVEMENTS "Build with RetroAchievements support" ON)
option(USE_DISCORD_PRESENCE "Enable support for Discord Rich Presence" ON)

View File

@ -73,9 +73,12 @@ else()
endif()
endif()
if(APPLE)
add_subdirectory(3rdparty/soundtouch EXCLUDE_FROM_ALL)
endif()
check_lib(SOUNDTOUCH SoundTouch SoundTouch.h PATH_SUFFIXES soundtouch)
if(NOT QT_BUILD)
if(NOT QT_BUILD AND NOT APPLE)
find_optional_system_library(SDL2 3rdparty/sdl2 2.0.12)
endif()

View File

@ -218,7 +218,7 @@ if(APPLE)
target_link_options(common PRIVATE -fobjc-link-runtime)
endif()
if(USE_OPENGL)
if(USE_OPENGL AND NOT LIBRETRO)
if(WIN32)
target_sources(common PRIVATE
GL/ContextWGL.cpp
@ -310,6 +310,6 @@ target_include_directories(common PUBLIC ../3rdparty/include ../)
target_compile_definitions(common PUBLIC "${PCSX2_DEFS}")
target_compile_options(common PRIVATE "${PCSX2_WARNINGS}")
if(COMMAND target_precompile_headers)
if(COMMAND target_precompile_headers AND NOT CCACHE_FOUND)
target_precompile_headers(common PRIVATE PrecompiledHeader.h)
endif()

View File

@ -71,6 +71,8 @@ void CocoaTools::DestroyMetalLayer(WindowInfo* wi)
[view setWantsLayer:NO];
}
#if !defined(__LIBRETRO__)
// MARK: - Theme Change Handlers
@interface PCSX2KVOHelper : NSObject
@ -199,3 +201,5 @@ bool CocoaTools::LaunchApplication(std::string_view file)
return [[NSWorkspace sharedWorkspace] launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:@{} error:nil];
}
}
#endif

View File

@ -80,6 +80,11 @@ static void MSW_OutputDebugString(const char* text)
static bool hasDebugger = IsDebuggerPresent();
if (hasDebugger)
OutputDebugStringA(text);
else
{
printf(text);
fflush(stdout);
}
#else
fputs(text, stdout_fp);
fflush(stdout_fp);

View File

@ -28,6 +28,12 @@
#include <queue>
#include <vector>
#ifdef __LIBRETRO__
#include <libretro_d3d.h>
extern retro_environment_t environ_cb;
retro_hw_render_interface_d3d12 *d3d12;
#endif
std::unique_ptr<D3D12::Context> g_d3d12_context;
using namespace D3D12;
@ -136,6 +142,18 @@ bool Context::Create(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool e
}
g_d3d12_context.reset(new Context());
#ifdef __LIBRETRO__
d3d12 = nullptr;
if (!environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void **)&d3d12) || !d3d12) {
printf("Failed to get HW rendering interface!\n");
return false;
}
if (d3d12->interface_version != RETRO_HW_RENDER_INTERFACE_D3D12_VERSION) {
printf("HW render interface mismatch, expected %u, got %u!\n", RETRO_HW_RENDER_INTERFACE_D3D12_VERSION, d3d12->interface_version);
return false;
}
#endif
if (!g_d3d12_context->CreateDevice(dxgi_factory, adapter, enable_debug_layer) ||
!g_d3d12_context->CreateCommandQueue() || !g_d3d12_context->CreateAllocator() ||
!g_d3d12_context->CreateFence() || !g_d3d12_context->CreateDescriptorHeaps() ||
@ -171,6 +189,9 @@ u32 Context::GetAdapterVendorID() const
bool Context::CreateDevice(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool enable_debug_layer)
{
#ifdef __LIBRETRO__
m_device = d3d12->device;
#else
HRESULT hr;
// Enabling the debug layer will fail if the Graphics Tools feature is not installed.
@ -195,12 +216,12 @@ bool Context::CreateDevice(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter,
Console.Error("Failed to create D3D12 device: %08X", hr);
return false;
}
#endif
// get adapter
const LUID luid(m_device->GetAdapterLuid());
if (FAILED(dxgi_factory->EnumAdapterByLuid(luid, IID_PPV_ARGS(m_adapter.put()))))
Console.Error("Failed to get lookup adapter by device LUID");
#ifndef __LIBRETRO__
if (enable_debug_layer)
{
ComPtr<ID3D12InfoQueue> info_queue = m_device.try_query<ID3D12InfoQueue>();
@ -225,17 +246,22 @@ bool Context::CreateDevice(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter,
info_queue->PushStorageFilter(&filter);
}
}
#endif
return true;
}
bool Context::CreateCommandQueue()
{
#ifdef __LIBRETRO__
m_command_queue = d3d12->queue;
return true;
#else
const D3D12_COMMAND_QUEUE_DESC queue_desc = {D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
D3D12_COMMAND_QUEUE_FLAG_NONE};
HRESULT hr = m_device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&m_command_queue));
pxAssertRel(SUCCEEDED(hr), "Create command queue");
return SUCCEEDED(hr);
#endif
}
bool Context::CreateAllocator()

View File

@ -28,7 +28,9 @@
#include <malloc.h>
#endif
#if defined(_WIN32) && !defined(_M_ARM64)
#if defined(__LIBRETRO__)
#include "common/GL/ContextRetroGL.h"
#elif defined(_WIN32) && !defined(_M_ARM64)
#include "common/GL/ContextWGL.h"
#elif defined(__APPLE__)
#include "common/GL/ContextAGL.h"
@ -89,6 +91,9 @@ namespace GL
}
std::unique_ptr<Context> context;
#if defined(__LIBRETRO__)
context = ContextRetroGL::Create(wi, versions_to_try);
#else
#if defined(_WIN32) && !defined(_M_ARM64)
context = ContextWGL::Create(wi, versions_to_try);
#elif defined(__APPLE__)
@ -103,6 +108,7 @@ namespace GL
#if defined(WAYLAND_API)
if (wi.type == WindowInfo::Type::Wayland)
context = ContextEGLWayland::Create(wi, versions_to_try);
#endif
#endif
if (!context)

View File

@ -0,0 +1,93 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "common/PrecompiledHeader.h"
#include "common/Console.h"
#include "ContextRetroGL.h"
#include <optional>
#include <vector>
#include "common/WindowInfo.h"
#include "GS/Renderers/Common/GSDevice.h"
#include "GS/GSVector.h"
#include <libretro.h>
extern retro_video_refresh_t video_cb;
extern retro_hw_render_callback hw_render;
namespace GL
{
ContextRetroGL::ContextRetroGL(const WindowInfo& wi)
: Context(wi)
{
}
ContextRetroGL::~ContextRetroGL()
{
}
std::unique_ptr<Context> ContextRetroGL::Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try)
{
std::unique_ptr<ContextRetroGL> context = std::make_unique<ContextRetroGL>(wi);
return context;
}
void* ContextRetroGL::GetProcAddress(const char* name)
{
return reinterpret_cast<void*>(hw_render.get_proc_address(name));
}
bool ContextRetroGL::ChangeSurface(const WindowInfo& new_wi)
{
return true;
}
void ContextRetroGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
{
m_wi.surface_width = new_surface_width;
m_wi.surface_height = new_surface_height;
}
bool ContextRetroGL::SwapBuffers()
{
if(g_gs_device->GetCurrent())
video_cb(RETRO_HW_FRAME_BUFFER_VALID, g_gs_device->GetCurrent()->GetWidth(), g_gs_device->GetCurrent()->GetHeight(), 0);
else
video_cb(NULL, 0, 0, 0);
return true;
}
bool ContextRetroGL::MakeCurrent()
{
return true;
}
bool ContextRetroGL::DoneCurrent()
{
return true;
}
bool ContextRetroGL::SetSwapInterval(s32 interval)
{
return true;
}
std::unique_ptr<Context> ContextRetroGL::CreateSharedContext(const WindowInfo& wi)
{
std::unique_ptr<ContextRetroGL> context = std::make_unique<ContextRetroGL>(wi);
return context;
}
} // namespace GL

View File

@ -0,0 +1,40 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/GL/Context.h"
namespace GL
{
class ContextRetroGL : public Context
{
public:
ContextRetroGL(const WindowInfo& wi);
~ContextRetroGL() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try);
void* GetProcAddress(const char* name) override;
virtual bool ChangeSurface(const WindowInfo& new_wi) override;
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
bool SwapBuffers() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
bool SetSwapInterval(s32 interval) override;
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
};
} // namespace GL

View File

@ -13,6 +13,7 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include "General.h"
#include "Console.h"

View File

@ -15,6 +15,7 @@
#pragma once
#include <cstdio>
#include <vector>
#include <cstdio>
#include "common/Pcsx2Types.h"

View File

@ -13,6 +13,7 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <limits>
#include "common/Vulkan/Builders.h"
#include "common/Vulkan/Util.h"
#include "common/Assertions.h"

View File

@ -25,7 +25,8 @@ struct WindowInfo
Win32,
X11,
Wayland,
MacOS
MacOS,
Libretro
};
/// The type of the surface. Surfaceless indicates it will not be displayed on screen at all.

70
libretro/CMakeLists.txt Normal file
View File

@ -0,0 +1,70 @@
add_library(pcsx2_libretro SHARED)
target_sources(pcsx2_libretro PRIVATE
${CMAKE_SOURCE_DIR}/libretro/main.cpp
${CMAKE_SOURCE_DIR}/libretro/options.cpp
${CMAKE_SOURCE_DIR}/libretro/input.cpp
${CMAKE_SOURCE_DIR}/libretro/Console.cpp
${CMAKE_SOURCE_DIR}/libretro/SPU2.cpp
${CMAKE_SOURCE_DIR}/libretro/DEV9.cpp
${CMAKE_SOURCE_DIR}/libretro/USB.cpp
${CMAKE_SOURCE_DIR}/libretro/InputRecording.cpp
${CMAKE_SOURCE_DIR}/common/GL/ContextRetroGL.cpp
)
if(USE_VULKAN)
target_sources(pcsx2_libretro PRIVATE
${CMAKE_SOURCE_DIR}/libretro/libretro_vulkan.cpp
)
endif()
target_link_libraries(pcsx2_libretro PRIVATE
PCSX2_FLAGS
PCSX2
)
if(APPLE)
target_link_libraries(pcsx2_libretro PRIVATE
"-framework CoreGraphics"
"-framework IOKit"
"-framework Foundation"
)
endif()
target_include_directories(PCSX2_FLAGS INTERFACE
"${CMAKE_SOURCE_DIR}/libretro"
)
target_include_directories(common PUBLIC
"${CMAKE_SOURCE_DIR}/libretro"
)
target_include_directories(pcsx2_libretro PRIVATE
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/libretro"
"${CMAKE_SOURCE_DIR}/common/include"
"${CMAKE_SOURCE_DIR}/3rdparty/include"
"${CMAKE_SOURCE_DIR}/pcsx2"
)
#include_directories(. ${CMAKE_SOURCE_DIR}/libretro ${CMAKE_SOURCE_DIR}/common)
set_target_properties(pcsx2_libretro PROPERTIES
LIBRARY_OUTPUT_NAME pcsx2_libretro
PREFIX ""
)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(CLANG 1)
endif()
if(NOT MSVC AND NOT CLANG)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
endif()
if(PACKAGE_MODE)
install(TARGETS pcsx2_libretro DESTINATION ${BIN_DIR})
else(PACKAGE_MODE)
install(TARGETS pcsx2_libretro DESTINATION ${CMAKE_SOURCE_DIR}/bin)
endif(PACKAGE_MODE)

81
libretro/Console.cpp Normal file
View File

@ -0,0 +1,81 @@
#include <libretro.h>
#include "common/Console.h"
extern retro_log_printf_t log_cb;
static ConsoleColors log_color = Color_Default;
static void RetroLog_DoSetColor(ConsoleColors color)
{
if (color != Color_Current)
log_color = color;
}
static void RetroLog_DoWrite(const char* fmt)
{
retro_log_level level = RETRO_LOG_INFO;
switch (log_color)
{
case Color_StrongRed: // intended for errors
level = RETRO_LOG_ERROR;
break;
case Color_StrongOrange: // intended for warnings
level = RETRO_LOG_WARN;
break;
case Color_Cyan: // faint visibility, intended for logging PS2/IOP output
case Color_Yellow: // faint visibility, intended for logging PS2/IOP output
case Color_White: // faint visibility, intended for logging PS2/IOP output
level = RETRO_LOG_DEBUG;
break;
default:
case Color_Default:
case Color_Black:
case Color_Green:
case Color_Red:
case Color_Blue:
case Color_Magenta:
case Color_Orange:
case Color_Gray:
case Color_StrongBlack:
case Color_StrongGreen: // intended for infrequent state information
case Color_StrongBlue: // intended for block headings
case Color_StrongMagenta:
case Color_StrongGray:
case Color_StrongCyan:
case Color_StrongYellow:
case Color_StrongWhite:
break;
}
log_cb(level, "%s", fmt);
}
static void RetroLog_SetTitle(const char* title)
{
log_cb(RETRO_LOG_INFO, "%s\n", title);
}
static void RetroLog_Newline()
{
// RetroLog_DoWrite(L"\n");
}
static void RetroLog_DoWriteLn(const char* fmt)
{
RetroLog_DoWrite(fmt);
}
static const IConsoleWriter ConsoleWriter_Libretro =
{
RetroLog_DoWrite,
RetroLog_DoWriteLn,
RetroLog_DoSetColor,
RetroLog_DoWrite,
RetroLog_Newline,
RetroLog_SetTitle,
0, // instance-level indentation (should always be 0)
};
const IConsoleWriter* PatchesCon = &ConsoleWriter_Libretro;

189
libretro/DEV9.cpp Normal file
View File

@ -0,0 +1,189 @@
#include <DEV9/DEV9.h>
// Our IRQ call.
void (*DEV9irq)(int);
void DEV9configure()
{
}
s32 DEV9init()
{
DevCon.WriteLn("Initializing dev9null");
return 0;
}
void DEV9shutdown()
{
DevCon.WriteLn("Shutting down Dev9null.");
}
s32 DEV9open()
{
DevCon.WriteLn("Opening Dev9null.");
// Get anything ready we need to. Opening and creating hard
// drive files, for example.
return 0;
}
void DEV9close()
{
DevCon.WriteLn("Closing Dev9null.");
// Close files opened.
}
u8 DEV9read8(u32 addr)
{
u8 value = 0;
switch (addr) {
// case 0x1F80146E: // DEV9 hardware type (0x32 for an expansion bay)
case 0x10000038: /*value = dev9Ru8(addr);*/
break; // We need to have at least one case to avoid warnings.
default:
//value = dev9Ru8(addr);
DevCon.WriteLn("*Unknown 8 bit read at address %lx", addr);
break;
}
return value;
}
u16 DEV9read16(u32 addr)
{
u16 value = 0;
switch (addr) {
// Addresses you may want to catch here include:
// case 0x1F80146E: // DEV9 hardware type (0x32 for an expansion bay)
// case 0x10000002: // The Smart Chip revision. Should be 0x11
// case 0x10000004: // More type info: bit 0 - smap; bit 1 - hd; bit 5 - flash
// case 0x1000000E: // Similar to the last; bit 1 should be set if a hd is hooked up.
// case 0x10000028: // intr_stat
// case 0x10000038: // hard drives seem to like reading and writing the max dma size per transfer here.
// case 0x1000002A: // intr_mask
// case 0x10000040: // pio_data
// case 0x10000044: // nsector
// case 0x10000046: // sector
// case 0x10000048: // lcyl
// case 0x1000004A: // hcyl
// case 0x1000004C: // select
// case 0x1000004E: // status
// case 0x1000005C: // status
// case 0x10000064: // if_ctrl
case 0x10000038: /*value = dev9Ru16(addr);*/
break;
default:
//value = dev9Ru16(addr);
DevCon.WriteLn("*Unknown 16 bit read at address %lx", addr);
break;
}
return value;
}
u32 DEV9read32(u32 addr)
{
u32 value = 0;
switch (addr) {
case 0x10000038: /*value = dev9Ru32(addr);*/
break;
default:
//value = dev9Ru32(addr);
DevCon.WriteLn("*Unknown 32 bit read at address %lx", addr);
break;
}
return value;
}
void DEV9write8(u32 addr, u8 value)
{
switch (addr) {
case 0x10000038: /*dev9Ru8(addr) = value;*/
break;
default:
DevCon.WriteLn("*Unknown 8 bit write; address %lx = %x", addr, value);
//dev9Ru8(addr) = value;
break;
}
}
void DEV9write16(u32 addr, u16 value)
{
switch (addr) {
// Remember that list on DEV9read16? You'll want to write to a
// lot of them, too.
case 0x10000038: /*dev9Ru16(addr) = value;*/
break;
default:
DevCon.WriteLn("*Unknown 16 bit write; address %lx = %x", addr, value);
//dev9Ru16(addr) = value;
break;
}
}
void DEV9write32(u32 addr, u32 value)
{
switch (addr) {
case 0x10000038: /*dev9Ru32(addr) = value;*/
break;
default:
DevCon.WriteLn("*Unknown 32 bit write; address %lx = %x", addr, value);
//dev9Ru32(addr) = value;
break;
}
}
s32 DEV9dmaRead(s32 channel, u32 *data, u32 bytesLeft, u32 *bytesProcessed)
{
// You'll want to put your own DMA8 reading code here.
// Time to interact with your fake (or real) hardware.
DevCon.WriteLn("Reading DMA8 Mem.");
*bytesProcessed = bytesLeft;
return 0;
}
s32 DEV9dmaWrite(s32 channel, u32 *data, u32 bytesLeft, u32 *bytesProcessed)
{
// See above.
DevCon.WriteLn("Writing DMA8 Mem.");
*bytesProcessed = bytesLeft;
return 0;
}
void DEV9dmaInterrupt(s32 channel)
{
// See above.
}
void DEV9readDMA8Mem(u32 *pMem, int size)
{
// You'll want to put your own DMA8 reading code here.
// Time to interact with your fake (or real) hardware.
DevCon.WriteLn("Reading DMA8 Mem.");
}
void DEV9writeDMA8Mem(u32 *pMem, int size)
{
// See above.
DevCon.WriteLn("Writing DMA8 Mem.");
}
int DEV9irqHandler(void)
{
return 0;
}
void DEV9setSettingsDir(const char *dir)
{
}
void DEV9async(u32 cycles)
{
}
void DEV9CheckChanges(const Pcsx2Config& old_config)
{
}

103
libretro/InputRecording.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "Recording/InputRecording.h"
InputRecording g_InputRecording;
bool InputRecording::create(const std::string& fileName, const bool fromSaveState, const std::string& authorName)
{
return true;
}
bool InputRecording::play(const std::string& filename)
{
return true;
}
void InputRecording::closeActiveFile()
{
}
void InputRecording::stop()
{
}
void InputRecording::handleControllerDataUpdate()
{
}
void InputRecording::saveControllerData(const PadData& data, const int port, const int slot)
{
}
std::optional<PadData> InputRecording::updateControllerData(const int port, const int slot)
{
return {};
}
void InputRecording::processRecordQueue()
{
}
std::string InputRecording::resolveGameName()
{
return "";
}
void InputRecording::incFrameCounter()
{
}
u64 InputRecording::getFrameCounter() const
{
return 0;
}
bool InputRecording::isActive() const
{
return false;
}
void InputRecording::handleExceededFrameCounter()
{
}
void InputRecording::handleReset()
{
}
void InputRecording::handleLoadingSavestate()
{
}
bool InputRecording::isTypeSavestate() const
{
return false;
}
void InputRecording::setStartingFrame(u32 startingFrame)
{
}
void InputRecording::adjustFrameCounterOnReRecord(u32 newFrameCounter)
{
}
InputRecordingControls& InputRecording::getControls()
{
return m_controls;
}
const InputRecordingFile& InputRecording::getData() const
{
return m_file;
}
void InputRecording::initializeState()
{
}
bool InputRecordingFile::close() noexcept
{
return true;
}

33
libretro/SPU2.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "SPU2/Global.h"
#include "SPU2/spu2.h"
#include "SPU2/SndOut.h"
void SndBuffer::Cleanup()
{
}
void SndBuffer::ClearContents()
{
}
void SndBuffer::ResetBuffers()
{
}
void SPU2::SetOutputPaused(bool paused)
{
}
void SPU2::SetOutputVolume(s32 volume)
{
}
bool SPU2::IsAudioCaptureActive()
{
return false;
}
void SPU2::SetAudioCaptureActive(bool active)
{
}

31
libretro/USB.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "USB/USB.h"
#include "SaveState.h"
void USB::CheckForConfigChanges(const Pcsx2Config& old_config)
{
}
bool USB::DoState(StateWrapper& sw)
{
return true;
}
void USBconfigure() {}
s32 USBinit() { return 0; }
void USBasync(u32 cycles) {}
void USBshutdown()
{
}
void USBclose() {}
void USBreset() {}
bool USBopen() { return true; }
s32 USBfreeze(FreezeAction mode, freezeData* data) { return 0; }
u8 USBread8(u32 addr) { return 0; }
u16 USBread16(u32 addr) { return 0; }
u32 USBread32(u32 addr) { return 0; }
void USBwrite8(u32 addr, u8 value) {}
void USBwrite16(u32 addr, u16 value) {}
void USBwrite32(u32 addr, u32 value) {}

256
libretro/build.cmake Normal file
View File

@ -0,0 +1,256 @@
LIST(REMOVE_ITEM pcsx2SPU2Sources
SPU2/Debug.cpp
SPU2/Wavedump_wav.cpp
SPU2/SndOut.cpp
SPU2/SndOut_SDL.cpp
SPU2/SndOut_Portaudio.cpp
SPU2/Timestretcher.cpp
SPU2/wx/wxConfig.cpp
SPU2/Linux/Alsa.cpp
SPU2/Linux/Config.cpp
SPU2/Linux/ConfigSoundTouch.cpp
SPU2/Linux/Dialogs.cpp
)
list(REMOVE_ITEM pcsx2SPU2Headers
SPU2/Host/Config.cpp
SPU2/Host/ConfigDebug.cpp
SPU2/Host/ConfigSoundTouch.cpp
)
set(pcsx2GuiSources
gui/AppAssert.cpp
gui/AppConfig.cpp
gui/AppCoreThread.cpp
gui/AppEventSources.cpp
gui/AppHost.cpp
gui/AppUserMode.cpp
gui/AppInit.cpp
gui/AppMain.cpp
gui/AppRes.cpp
gui/CheckedStaticBox.cpp
gui/ConsoleLogger.cpp
gui/Dialogs/AboutBoxDialog.cpp
gui/Dialogs/GSDumpDialog.cpp
gui/Dialogs/AssertionDialog.cpp
gui/Dialogs/BaseConfigurationDialog.cpp
gui/Dialogs/ConfirmationDialogs.cpp
gui/Dialogs/ConvertMemoryCardDialog.cpp
gui/Dialogs/CreateMemoryCardDialog.cpp
gui/Dialogs/FirstTimeWizard.cpp
gui/Dialogs/ImportSettingsDialog.cpp
gui/Dialogs/LogOptionsDialog.cpp
gui/Dialogs/McdConfigDialog.cpp
gui/Dialogs/PickUserModeDialog.cpp
gui/Dialogs/SysConfigDialog.cpp
gui/Dialogs/PINEDialog.cpp
gui/Debugger/BreakpointWindow.cpp
gui/Debugger/CtrlDisassemblyView.cpp
gui/Debugger/CtrlRegisterList.cpp
gui/Debugger/CtrlMemView.cpp
gui/Debugger/CtrlMemSearch.cpp
gui/Debugger/DebuggerLists.cpp
gui/Debugger/DisassemblyDialog.cpp
gui/Debugger/DebugEvents.cpp
gui/DriveList.cpp
gui/ExecutorThread.cpp
gui/FastFormatString.cpp
gui/FileUtils.cpp
gui/FrameForGS.cpp
gui/GlobalCommands.cpp
gui/IniInterface.cpp
gui/i18n.cpp
gui/IsoDropTarget.cpp
gui/MainFrame.cpp
gui/MainMenuClicks.cpp
gui/MessageBoxes.cpp
gui/Mutex.cpp
gui/MSWstuff.cpp
gui/Panels/BaseApplicableConfigPanel.cpp
gui/Panels/BiosSelectorPanel.cpp
gui/Panels/CpuPanel.cpp
gui/Panels/DirPickerPanel.cpp
gui/Panels/GameFixesPanel.cpp
gui/Panels/GSWindowPanel.cpp
gui/Panels/LogOptionsPanels.cpp
gui/Panels/MemoryCardListPanel.cpp
gui/Panels/MemoryCardListView.cpp
gui/Panels/MiscPanelStuff.cpp
gui/Panels/PathsPanel.cpp
gui/Panels/SpeedhacksPanel.cpp
gui/Panels/VideoPanel.cpp
gui/PathUtils.cpp
gui/PersistentThread.cpp
gui/pxCheckBox.cpp
gui/pxRadioPanel.cpp
gui/pxStaticText.cpp
gui/pxTranslate.cpp
gui/pxWindowTextWriter.cpp
gui/RecentIsoList.cpp
gui/Saveslots.cpp
gui/StringHelpers.cpp
gui/SysCoreThread.cpp
gui/SysState.cpp
gui/SysThreadBase.cpp
gui/ThreadingDialogs.cpp
gui/UpdateUI.cpp
gui/wxAppWithHelpers.cpp
gui/wxGuiTools.cpp
gui/wxHelpers.cpp
gui/wxSettingsInterface.cpp
)
LIST(REMOVE_ITEM pcsx2GuiSources
# gui/AppHost.cpp
gui/CheckedStaticBox.cpp
gui/Dialogs/AboutBoxDialog.cpp
gui/Dialogs/GSDumpDialog.cpp
gui/Dialogs/AssertionDialog.cpp
gui/Dialogs/BaseConfigurationDialog.cpp
gui/Dialogs/ConfirmationDialogs.cpp
gui/Dialogs/ConvertMemoryCardDialog.cpp
gui/Dialogs/CreateMemoryCardDialog.cpp
gui/Dialogs/FirstTimeWizard.cpp
gui/Dialogs/ImportSettingsDialog.cpp
gui/Dialogs/LogOptionsDialog.cpp
gui/Dialogs/McdConfigDialog.cpp
gui/Dialogs/PickUserModeDialog.cpp
gui/Dialogs/SysConfigDialog.cpp
gui/Dialogs/PINEDialog.cpp
gui/Debugger/BreakpointWindow.cpp
gui/Debugger/CtrlDisassemblyView.cpp
gui/Debugger/CtrlRegisterList.cpp
gui/Debugger/CtrlMemView.cpp
gui/Debugger/CtrlMemSearch.cpp
gui/Debugger/DebuggerLists.cpp
gui/Debugger/DisassemblyDialog.cpp
gui/Debugger/DebugEvents.cpp
gui/CpuUsageProvider.cpp
gui/DriveList.cpp
gui/FrameForGS.cpp
gui/GlobalCommands.cpp
gui/IsoDropTarget.cpp
gui/MainFrame.cpp
gui/MainMenuClicks.cpp
gui/MessageBoxes.cpp
gui/Panels/BaseApplicableConfigPanel.cpp
gui/Panels/BiosSelectorPanel.cpp
gui/Panels/CpuPanel.cpp
gui/Panels/DirPickerPanel.cpp
gui/Panels/GameFixesPanel.cpp
gui/Panels/GSWindowPanel.cpp
gui/Panels/LogOptionsPanels.cpp
gui/Panels/MemoryCardListPanel.cpp
gui/Panels/MemoryCardListView.cpp
gui/Panels/MiscPanelStuff.cpp
gui/Panels/PathsPanel.cpp
gui/Panels/PluginSelectorPanel.cpp
gui/Panels/SpeedhacksPanel.cpp
gui/Panels/VideoPanel.cpp
gui/pxCheckBox.cpp
gui/pxRadioPanel.cpp
gui/pxStaticText.cpp
gui/pxWindowTextWriter.cpp
gui/RecentIsoList.cpp
gui/Saveslots.cpp
gui/SysState.cpp
gui/ThreadingDialogs.cpp
gui/UpdateUI.cpp
gui/wxGuiTools.cpp
gui/wxHelpers.cpp
)
LIST(REMOVE_ITEM pcsx2GuiHeaders
gui/ThreadingDialogs.cpp
)
LIST(REMOVE_ITEM pcsx2LinuxSources
gui/CpuUsageProviderLnx.cpp
Linux/LnxKeyCodes.cpp
)
LIST(REMOVE_ITEM pcsx2WindowsSources
windows/PatchBrowser.cpp
)
LIST(REMOVE_ITEM pcsx2PADSources
PAD/Linux/Device.cpp
PAD/Linux/InputManager.cpp
PAD/Linux/SDL/joystick.cpp
PAD/Linux/keyboard.cpp
PAD/Linux/KeyStatus.cpp
PAD/Linux/wx_dialog/dialog.cpp
PAD/Linux/wx_dialog/opPanel.cpp
PAD/Linux/wx_dialog/GamepadConfiguration.cpp
PAD/Linux/wx_dialog/JoystickConfiguration.cpp
PAD/Linux/Config.cpp
PAD/Linux/linux.cpp
)
LIST(REMOVE_ITEM pcsx2GSSources
GS/GSCapture.cpp
GS/GSPng.cpp
GS/Renderers/Common/GSOsdManager.cpp
GS/Window/GSLinuxDialog.cpp
GS/Window/GSwxDialog.cpp
)
set(pcsx2DEV9Sources)
set(pcsx2DEV9Headers)
set(pcsx2USBSources)
set(pcsx2USBHeaders)
set(pcsx2RecordingSources)
set(pcsx2RecordingVirtualPadResources)
set(pcsx2RecordingHeaders)
set(pcsx2ZipToolsSources)
set(pcsx2ZipToolsHeaders)
set(pcsx2FrontendSources)
target_link_libraries(PCSX2 PRIVATE
${wxWidgets_LIBRARIES}
${AIO_LIBRARIES}
${GLIB_LIBRARIES}
${GLIB_GIO_LIBRARIES}
)
#add_link_options(-fuse-ld=gold)
#add_link_options(-Wl,--gc-sections,--print-symbol-counts,sym.log)
target_sources(PCSX2 PRIVATE
${CMAKE_SOURCE_DIR}/libretro/main.cpp
${CMAKE_SOURCE_DIR}/libretro/options.cpp
${CMAKE_SOURCE_DIR}/libretro/input.cpp
${CMAKE_SOURCE_DIR}/common/GL/ContextRetroGL.cpp
# USB/USBNull.cpp
${pcsx2LTOSources}
${pcsx2GuiSources}
)
target_link_libraries(PCSX2 PRIVATE
PCSX2_FLAGS
)
include_directories(. ${CMAKE_SOURCE_DIR}/libretro ${CMAKE_SOURCE_DIR}/common)
set_target_properties(PCSX2 PROPERTIES
LIBRARY_OUTPUT_NAME pcsx2_libretro
PREFIX ""
)
# set(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}")
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(CLANG 1)
endif()
if(NOT MSVC AND NOT CLANG)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
endif()
if(PACKAGE_MODE)
install(TARGETS PCSX2 DESTINATION ${BIN_DIR})
else(PACKAGE_MODE)
install(TARGETS PCSX2 DESTINATION ${CMAKE_SOURCE_DIR}/bin)
endif(PACKAGE_MODE)

253
libretro/input.cpp Normal file
View File

@ -0,0 +1,253 @@
#include "PrecompiledHeader.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <libretro.h>
#include "input.h"
//#include "PS2Edefs.h"
#include "PAD/Host/StateManagement.h"
#include "PAD/Host/KeyStatus.h"
#include "Frontend/InputManager.h"
extern retro_environment_t environ_cb;
static retro_input_poll_t poll_cb;
static retro_input_state_t input_cb;
struct retro_rumble_interface rumble;
//PADconf g_conf;
static struct retro_input_descriptor desc[] = {
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L2"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R2"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "L3"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "R3"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select"},
{0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "L-Analog X"},
{0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "L-Analog Y"},
{0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "R-Analog X"},
{0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "R-Analog Y"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L2"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R2"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "L3"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "R3"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start"},
{1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select"},
{1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "L-Analog X"},
{1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "L-Analog Y"},
{1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "R-Analog X"},
{1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "R-Analog Y"},
{0},
};
namespace Input
{
static u32 button_mask[2];
static int pad_lx[2];
static int pad_ly[2];
static int pad_rx[2];
static int pad_ry[2];
static int keymap[] =
{
RETRO_DEVICE_ID_JOYPAD_L2, // PAD_L2
RETRO_DEVICE_ID_JOYPAD_R2, // PAD_R2
RETRO_DEVICE_ID_JOYPAD_L, // PAD_L1
RETRO_DEVICE_ID_JOYPAD_R, // PAD_R1
RETRO_DEVICE_ID_JOYPAD_X, // PAD_TRIANGLE
RETRO_DEVICE_ID_JOYPAD_A, // PAD_CIRCLE
RETRO_DEVICE_ID_JOYPAD_B, // PAD_CROSS
RETRO_DEVICE_ID_JOYPAD_Y, // PAD_SQUARE
RETRO_DEVICE_ID_JOYPAD_SELECT, // PAD_SELECT
RETRO_DEVICE_ID_JOYPAD_L3, // PAD_L3
RETRO_DEVICE_ID_JOYPAD_R3, // PAD_R3
RETRO_DEVICE_ID_JOYPAD_START, // PAD_START
RETRO_DEVICE_ID_JOYPAD_UP, // PAD_UP
RETRO_DEVICE_ID_JOYPAD_RIGHT, // PAD_RIGHT
RETRO_DEVICE_ID_JOYPAD_DOWN, // PAD_DOWN
RETRO_DEVICE_ID_JOYPAD_LEFT, // PAD_LEFT
};
void Init()
{
environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble);
static const struct retro_controller_description ds2_desc[] = {
{"DualShock 2", RETRO_DEVICE_ANALOG},
};
static const struct retro_controller_info ports[] = {
{ds2_desc, sizeof(ds2_desc) / sizeof(*ds2_desc)},
{ds2_desc, sizeof(ds2_desc) / sizeof(*ds2_desc)},
{},
};
environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
// environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
button_mask[0] = 0xFFFFFFFF;
button_mask[1] = 0xFFFFFFFF;
}
void Shutdown()
{
}
void Update()
{
poll_cb();
Pad::rumble_all();
u32 mask = input_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
button_mask[0] = 0xFFFF0000;
for (int i = 0; i < 16; i++)
button_mask[0] |= !(mask & (1 << keymap[i])) << i;
mask = input_cb(1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
button_mask[1] = 0xFFFF0000;
for (int i = 0; i < 16; i++)
button_mask[1] |= !(mask & (1 << keymap[i])) << i;
pad_lx[0] = input_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X);
pad_ly[0] = input_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y);
pad_rx[0] = input_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
pad_ry[0] = input_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
pad_lx[1] = input_cb(1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X);
pad_ly[1] = input_cb(1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y);
pad_rx[1] = input_cb(1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
pad_ry[1] = input_cb(1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
}
} // namespace Input
void retro_set_input_poll(retro_input_poll_t cb)
{
poll_cb = cb;
}
void retro_set_input_state(retro_input_state_t cb)
{
input_cb = cb;
}
void retro_set_controller_port_device(unsigned port, unsigned device)
{
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
}
//void Device::DoRumble(unsigned type, unsigned pad)
//{
// if (pad >= GAMEPAD_NUMBER)
// return;
// if (type == 0)
// rumble.set_rumble_state(pad, RETRO_RUMBLE_WEAK, 0xFFFF);
// else
// rumble.set_rumble_state(pad, RETRO_RUMBLE_STRONG, 0xFFFF);
//}
u32 PAD::KeyStatus::GetButtons(u32 pad)
{
return Input::button_mask[pad];
}
u8 PAD::KeyStatus::GetPressure(u32 pad, u32 index)
{
int val = 0;
switch (index)
{
case PAD_R_LEFT:
case PAD_R_RIGHT:
val = Input::pad_lx[pad];
break;
case PAD_R_DOWN:
case PAD_R_UP:
val = Input::pad_ly[pad];
break;
case PAD_L_LEFT:
case PAD_L_RIGHT:
val = Input::pad_rx[pad];
break;
case PAD_L_DOWN:
case PAD_L_UP:
val = Input::pad_ry[pad];
break;
default:
if (index < 16)
val = !(Input::button_mask[pad] & (1 << Input::keymap[index]));
break;
}
if (index < 16)
{
#if 0
return 0xFF - (val >> 7);
#else
return val ? 0x00 : 0xFF;
#endif
}
return 0x80 + (val >> 8);
}
PAD::KeyStatus::KeyStatus()
{
}
void PAD::KeyStatus::Init()
{
}
void PAD::KeyStatus::Set(u32 pad, u32 index, float value)
{
}
void InputManager::PollSources()
{
}
void InputManager::CloseSources()
{
}
void InputManager::ReloadSources(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock)
{
}
void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si)
{
}
void InputManager::PauseVibration()
{
}
const char* InputManager::InputSourceToString(InputSourceType clazz)
{
return "";
}

9
libretro/input.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
namespace Input
{
void Init();
void Update();
void Shutdown();
}

3972
libretro/libretro.h Normal file

File diff suppressed because it is too large Load Diff

80
libretro/libretro_d3d.h Normal file
View File

@ -0,0 +1,80 @@
/* Copyright (C) 2010-2023 The RetroArch team
*
* ---------------------------------------------------------------------------------------------
* The following license statement only applies to this libretro API header (libretro_d3d.h)
* ---------------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the
* "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRETRO_DIRECT3D_H__
#define LIBRETRO_DIRECT3D_H__
#include "libretro.h"
#include <d3d11.h>
#include <d3dcompiler.h>
#define RETRO_HW_RENDER_INTERFACE_D3D11_VERSION 1
struct retro_hw_render_interface_d3d11
{
/* Must be set to RETRO_HW_RENDER_INTERFACE_D3D11. */
enum retro_hw_render_interface_type interface_type;
/* Must be set to RETRO_HW_RENDER_INTERFACE_D3D11_VERSION. */
unsigned interface_version;
/* Opaque handle to the d3d11 backend in the frontend
* which must be passed along to all function pointers
* in this interface.
*/
void* handle;
ID3D11Device *device;
ID3D11DeviceContext *context;
D3D_FEATURE_LEVEL featureLevel;
pD3DCompile D3DCompile;
};
#include <d3d12.h>
#include <d3dcompiler.h>
#define RETRO_HW_RENDER_INTERFACE_D3D12_VERSION 1
struct retro_hw_render_interface_d3d12
{
/* Must be set to RETRO_HW_RENDER_INTERFACE_D3D12. */
enum retro_hw_render_interface_type interface_type;
/* Must be set to RETRO_HW_RENDER_INTERFACE_D3D12_VERSION. */
unsigned interface_version;
/* Opaque handle to the d3d12 backend in the frontend
* which must be passed along to all function pointers
* in this interface.
*/
void* handle;
ID3D12Device *device;
ID3D12CommandQueue *queue;
pD3DCompile D3DCompile;
D3D12_RESOURCE_STATES required_state;
void (*set_texture)(void* handle, ID3D12Resource* texture, DXGI_FORMAT format);
};
#endif /* LIBRETRO_DIRECT3D_H__ */

View File

@ -0,0 +1,151 @@
#include <cstring>
#include <cassert>
#include <vector>
#define VK_NO_PROTOTYPES
#include "libretro/libretro_vulkan.h"
static retro_hw_render_interface_vulkan *vulkan;
extern retro_log_printf_t log_cb;
static struct {
VkInstance instance;
VkPhysicalDevice gpu;
const char **required_device_extensions;
unsigned num_required_device_extensions;
const char **required_device_layers;
unsigned num_required_device_layers;
const VkPhysicalDeviceFeatures *required_features;
} vk_init_info;
extern "C"
{
extern PFN_vkCreateInstance pcsx2_vkCreateInstance;
extern PFN_vkDestroyInstance pcsx2_vkDestroyInstance;
#define LIBRETRO_VK_WARP_LIST() \
LIBRETRO_VK_WARP_FUNC(vkGetDeviceProcAddr); \
LIBRETRO_VK_WARP_FUNC(vkCreateDevice); \
LIBRETRO_VK_WARP_FUNC(vkDestroyDevice); \
LIBRETRO_VK_WARP_FUNC(vkQueueSubmit); \
LIBRETRO_VK_WARP_FUNC(vkQueueWaitIdle);
#define LIBRETRO_VK_WARP_FUNC(x) \
extern PFN_##x pcsx2_##x; \
PFN_##x x##_org
LIBRETRO_VK_WARP_FUNC(vkGetInstanceProcAddr);
LIBRETRO_VK_WARP_LIST();
}
static VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance_libretro(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) {
*pInstance = vk_init_info.instance;
return VK_SUCCESS;
}
static void add_name_unique(std::vector<const char *> &list, const char *value) {
for (const char *name : list)
if (!strcmp(value, name))
return;
list.push_back(value);
}
static VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice_libretro(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {
VkDeviceCreateInfo info = *pCreateInfo;
std::vector<const char *> EnabledLayerNames(info.ppEnabledLayerNames, info.ppEnabledLayerNames + info.enabledLayerCount);
std::vector<const char *> EnabledExtensionNames(info.ppEnabledExtensionNames, info.ppEnabledExtensionNames + info.enabledExtensionCount);
VkPhysicalDeviceFeatures EnabledFeatures = *info.pEnabledFeatures;
for (unsigned i = 0; i < vk_init_info.num_required_device_layers; i++)
add_name_unique(EnabledLayerNames, vk_init_info.required_device_layers[i]);
for (unsigned i = 0; i < vk_init_info.num_required_device_extensions; i++)
add_name_unique(EnabledExtensionNames, vk_init_info.required_device_extensions[i]);
add_name_unique(EnabledExtensionNames, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME);
for (unsigned i = 0; i < sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); i++) {
if (((VkBool32 *)vk_init_info.required_features)[i])
((VkBool32 *)&EnabledFeatures)[i] = VK_TRUE;
}
info.enabledLayerCount = (uint32_t)EnabledLayerNames.size();
info.ppEnabledLayerNames = info.enabledLayerCount ? EnabledLayerNames.data() : nullptr;
info.enabledExtensionCount = (uint32_t)EnabledExtensionNames.size();
info.ppEnabledExtensionNames = info.enabledExtensionCount ? EnabledExtensionNames.data() : nullptr;
info.pEnabledFeatures = &EnabledFeatures;
return vkCreateDevice_org(physicalDevice, &info, pAllocator, pDevice);
}
static VKAPI_ATTR void VKAPI_CALL vkDestroyInstance_libretro(VkInstance instance, const VkAllocationCallbacks *pAllocator) {}
static VKAPI_ATTR void VKAPI_CALL vkDestroyDevice_libretro(VkDevice device, const VkAllocationCallbacks *pAllocator) {}
VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit_libretro(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) {
vulkan->lock_queue(vulkan->handle);
VkResult res = vkQueueSubmit_org(queue, submitCount, pSubmits, fence);
vulkan->unlock_queue(vulkan->handle);
return res;
}
VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle_libretro(VkQueue queue) {
vulkan->lock_queue(vulkan->handle);
VkResult res = vkQueueWaitIdle_org(queue);
vulkan->unlock_queue(vulkan->handle);
return res;
}
#undef LIBRETRO_VK_WARP_FUNC
#define LIBRETRO_VK_WARP_FUNC(x) \
if (!strcmp(pName, #x)) { \
x##_org = (PFN_##x)fptr; \
return (PFN_vkVoidFunction)x##_libretro; \
}
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr_libretro(VkDevice device, const char *pName) {
PFN_vkVoidFunction fptr = vkGetDeviceProcAddr_org(device, pName);
if (!fptr)
return fptr;
LIBRETRO_VK_WARP_LIST();
return fptr;
}
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr_libretro(VkInstance instance, const char *pName) {
PFN_vkVoidFunction fptr = vkGetInstanceProcAddr_org(instance, pName);
if (!fptr)
return fptr;
LIBRETRO_VK_WARP_LIST();
return fptr;
}
void vk_libretro_init_wraps()
{
vkGetInstanceProcAddr_org = pcsx2_vkGetInstanceProcAddr;
pcsx2_vkGetInstanceProcAddr = vkGetInstanceProcAddr_libretro;
pcsx2_vkCreateInstance = vkCreateInstance_libretro;
pcsx2_vkDestroyInstance = vkDestroyInstance_libretro;
}
void vk_libretro_init(VkInstance instance, VkPhysicalDevice gpu, const char **required_device_extensions, unsigned num_required_device_extensions, const char **required_device_layers, unsigned num_required_device_layers, const VkPhysicalDeviceFeatures *required_features) {
vk_init_info.instance = instance;
vk_init_info.gpu = gpu;
vk_init_info.required_device_extensions = required_device_extensions;
vk_init_info.num_required_device_extensions = num_required_device_extensions;
vk_init_info.required_device_layers = required_device_layers;
vk_init_info.num_required_device_layers = num_required_device_layers;
vk_init_info.required_features = required_features;
}
void vk_libretro_set_hwrender_interface(retro_hw_render_interface_vulkan *hw_render_interface) {
vulkan = (retro_hw_render_interface_vulkan *)hw_render_interface;
}
void vk_libretro_shutdown() {
memset(&vk_init_info, 0, sizeof(vk_init_info));
vulkan = nullptr;
}

403
libretro/libretro_vulkan.h Normal file
View File

@ -0,0 +1,403 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------------
* The following license statement only applies to this libretro API header (libretro_vulkan.h)
* ---------------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the
* "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRETRO_VULKAN_H__
#define LIBRETRO_VULKAN_H__
#include "vulkan/vulkan.h"
#include "libretro.h"
#define RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION 5
#define RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION 1
struct retro_vulkan_image
{
VkImageView image_view;
VkImageLayout image_layout;
VkImageViewCreateInfo create_info;
};
typedef void (*retro_vulkan_set_image_t)(void* handle, const struct retro_vulkan_image* image,
uint32_t num_semaphores, const VkSemaphore* semaphores,
uint32_t src_queue_family);
typedef uint32_t (*retro_vulkan_get_sync_index_t)(void* handle);
typedef uint32_t (*retro_vulkan_get_sync_index_mask_t)(void* handle);
typedef void (*retro_vulkan_set_command_buffers_t)(void* handle, uint32_t num_cmd,
const VkCommandBuffer* cmd);
typedef void (*retro_vulkan_wait_sync_index_t)(void* handle);
typedef void (*retro_vulkan_lock_queue_t)(void* handle);
typedef void (*retro_vulkan_unlock_queue_t)(void* handle);
typedef void (*retro_vulkan_set_signal_semaphore_t)(void* handle, VkSemaphore semaphore);
typedef const VkApplicationInfo* (*retro_vulkan_get_application_info_t)(void);
struct retro_vulkan_context
{
VkPhysicalDevice gpu;
VkDevice device;
VkQueue queue;
uint32_t queue_family_index;
VkQueue presentation_queue;
uint32_t presentation_queue_family_index;
};
typedef bool (*retro_vulkan_create_device_t)(
struct retro_vulkan_context* context, VkInstance instance, VkPhysicalDevice gpu,
VkSurfaceKHR surface, PFN_vkGetInstanceProcAddr get_instance_proc_addr,
const char** required_device_extensions, unsigned num_required_device_extensions,
const char** required_device_layers, unsigned num_required_device_layers,
const VkPhysicalDeviceFeatures* required_features);
typedef void (*retro_vulkan_destroy_device_t)(void);
/* Note on thread safety:
* The Vulkan API is heavily designed around multi-threading, and
* the libretro interface for it should also be threading friendly.
* A core should be able to build command buffers and submit
* command buffers to the GPU from any thread.
*/
struct retro_hw_render_context_negotiation_interface_vulkan
{
/* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN. */
enum retro_hw_render_context_negotiation_interface_type interface_type;
/* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION. */
unsigned interface_version;
/* If non-NULL, returns a VkApplicationInfo struct that the frontend can use instead of
* its "default" application info.
*/
retro_vulkan_get_application_info_t get_application_info;
/* If non-NULL, the libretro core will choose one or more physical devices,
* create one or more logical devices and create one or more queues.
* The core must prepare a designated PhysicalDevice, Device, Queue and queue family index
* which the frontend will use for its internal operation.
*
* If gpu is not VK_NULL_HANDLE, the physical device provided to the frontend must be this
* PhysicalDevice.
* The core is still free to use other physical devices.
*
* The frontend will request certain extensions and layers for a device which is created.
* The core must ensure that the queue and queue_family_index support GRAPHICS and COMPUTE.
*
* If surface is not VK_NULL_HANDLE, the core must consider presentation when creating the queues.
* If presentation to "surface" is supported on the queue, presentation_queue must be equal to
* queue.
* If not, a second queue must be provided in presentation_queue and presentation_queue_index.
* If surface is not VK_NULL_HANDLE, the instance from frontend will have been created with
* supported for
* VK_KHR_surface extension.
*
* The core is free to set its own queue priorities.
* Device provided to frontend is owned by the frontend, but any additional device resources must
* be freed by core
* in destroy_device callback.
*
* If this function returns true, a PhysicalDevice, Device and Queues are initialized.
* If false, none of the above have been initialized and the frontend will attempt
* to fallback to "default" device creation, as if this function was never called.
*/
retro_vulkan_create_device_t create_device;
/* If non-NULL, this callback is called similar to context_destroy for HW_RENDER_INTERFACE.
* However, it will be called even if context_reset was not called.
* This can happen if the context never succeeds in being created.
* destroy_device will always be called before the VkInstance
* of the frontend is destroyed if create_device was called successfully so that the core has a
* chance of
* tearing down its own device resources.
*
* Only auxillary resources should be freed here, i.e. resources which are not part of
* retro_vulkan_context.
*/
retro_vulkan_destroy_device_t destroy_device;
};
struct retro_hw_render_interface_vulkan
{
/* Must be set to RETRO_HW_RENDER_INTERFACE_VULKAN. */
enum retro_hw_render_interface_type interface_type;
/* Must be set to RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION. */
unsigned interface_version;
/* Opaque handle to the Vulkan backend in the frontend
* which must be passed along to all function pointers
* in this interface.
*
* The rationale for including a handle here (which libretro v1
* doesn't currently do in general) is:
*
* - Vulkan cores should be able to be freely threaded without lots of fuzz.
* This would break frontends which currently rely on TLS
* to deal with multiple cores loaded at the same time.
* - Fixing this in general is TODO for an eventual libretro v2.
*/
void* handle;
/* The Vulkan instance the context is using. */
VkInstance instance;
/* The physical device used. */
VkPhysicalDevice gpu;
/* The logical device used. */
VkDevice device;
/* Allows a core to fetch all its needed symbols without having to link
* against the loader itself. */
PFN_vkGetDeviceProcAddr get_device_proc_addr;
PFN_vkGetInstanceProcAddr get_instance_proc_addr;
/* The queue the core must use to submit data.
* This queue and index must remain constant throughout the lifetime
* of the context.
*
* This queue will be the queue that supports graphics and compute
* if the device supports compute.
*/
VkQueue queue;
unsigned queue_index;
/* Before calling retro_video_refresh_t with RETRO_HW_FRAME_BUFFER_VALID,
* set which image to use for this frame.
*
* If num_semaphores is non-zero, the frontend will wait for the
* semaphores provided to be signaled before using the results further
* in the pipeline.
*
* Semaphores provided by a single call to set_image will only be
* waited for once (waiting for a semaphore resets it).
* E.g. set_image, video_refresh, and then another
* video_refresh without set_image,
* but same image will only wait for semaphores once.
*
* For this reason, ownership transfer will only occur if semaphores
* are waited on for a particular frame in the frontend.
*
* Using semaphores is optional for synchronization purposes,
* but if not using
* semaphores, an image memory barrier in vkCmdPipelineBarrier
* should be used in the graphics_queue.
* Example:
*
* vkCmdPipelineBarrier(cmd,
* srcStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
* dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
* image_memory_barrier = {
* srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
* dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
* });
*
* The use of pipeline barriers instead of semaphores is encouraged
* as it is simpler and more fine-grained. A layout transition
* must generally happen anyways which requires a
* pipeline barrier.
*
* The image passed to set_image must have imageUsage flags set to at least
* VK_IMAGE_USAGE_TRANSFER_SRC_BIT and VK_IMAGE_USAGE_SAMPLED_BIT.
* The core will naturally want to use flags such as
* VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT and/or
* VK_IMAGE_USAGE_TRANSFER_DST_BIT depending
* on how the final image is created.
*
* The image must also have been created with MUTABLE_FORMAT bit set if
* 8-bit formats are used, so that the frontend can reinterpret sRGB
* formats as it sees fit.
*
* Images passed to set_image should be created with TILING_OPTIMAL.
* The image layout should be transitioned to either
* VK_IMAGE_LAYOUT_GENERIC or VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL.
* The actual image layout used must be set in image_layout.
*
* The image must be a 2D texture which may or not be layered
* and/or mipmapped.
*
* The image must be suitable for linear sampling.
* While the image_view is typically the only field used,
* the frontend may want to reinterpret the texture as sRGB vs.
* non-sRGB for example so the VkImageViewCreateInfo used to
* create the image view must also be passed in.
*
* The data in the pointer to the image struct will not be copied
* as the pNext field in create_info cannot be reliably deep-copied.
* The image pointer passed to set_image must be valid until
* retro_video_refresh_t has returned.
*
* If frame duping is used when passing NULL to retro_video_refresh_t,
* the frontend is free to either use the latest image passed to
* set_image or reuse the older pointer passed to set_image the
* frame RETRO_HW_FRAME_BUFFER_VALID was last used.
*
* Essentially, the lifetime of the pointer passed to
* set_image should be extended if frame duping is used
* so that the frontend can reuse the older pointer.
*
* The image itself however, must not be touched by the core until
* wait_sync_index has been completed later. The frontend may perform
* layout transitions on the image, so even read-only access is not defined.
* The exception to read-only rule is if GENERAL layout is used for the image.
* In this case, the frontend is not allowed to perform any layout transitions,
* so concurrent reads from core and frontend are allowed.
*
* If frame duping is used, or if set_command_buffers is used,
* the frontend will not wait for any semaphores.
*
* The src_queue_family is used to specify which queue family
* the image is currently owned by. If using multiple queue families
* (e.g. async compute), the frontend will need to acquire ownership of the
* image before rendering with it and release the image afterwards.
*
* If src_queue_family is equal to the queue family (queue_index),
* no ownership transfer will occur.
* Similarly, if src_queue_family is VK_QUEUE_FAMILY_IGNORED,
* no ownership transfer will occur.
*
* The frontend will always release ownership back to src_queue_family.
* Waiting for frontend to complete with wait_sync_index() ensures that
* the frontend has released ownership back to the application.
* Note that in Vulkan, transfering ownership is a two-part process.
*
* Example frame:
* - core releases ownership from src_queue_index to queue_index with VkImageMemoryBarrier.
* - core calls set_image with src_queue_index.
* - Frontend will acquire the image with src_queue_index -> queue_index as well, completing the
* ownership transfer.
* - Frontend renders the frame.
* - Frontend releases ownership with queue_index -> src_queue_index.
* - Next time image is used, core must acquire ownership from queue_index ...
*
* Since the frontend releases ownership, we cannot necessarily dupe the frame because
* the core needs to make the roundtrip of ownership transfer.
*/
retro_vulkan_set_image_t set_image;
/* Get the current sync index for this frame which is obtained in
* frontend by calling e.g. vkAcquireNextImageKHR before calling
* retro_run().
*
* This index will correspond to which swapchain buffer is currently
* the active one.
*
* Knowing this index is very useful for maintaining safe asynchronous CPU
* and GPU operation without stalling.
*
* The common pattern for synchronization is to receive fences when
* submitting command buffers to Vulkan (vkQueueSubmit) and add this fence
* to a list of fences for frame number get_sync_index().
*
* Next time we receive the same get_sync_index(), we can wait for the
* fences from before, which will usually return immediately as the
* frontend will generally also avoid letting the GPU run ahead too much.
*
* After the fence has signaled, we know that the GPU has completed all
* GPU work related to work submitted in the frame we last saw get_sync_index().
*
* This means we can safely reuse or free resources allocated in this frame.
*
* In theory, even if we wait for the fences correctly, it is not technically
* safe to write to the image we earlier passed to the frontend since we're
* not waiting for the frontend GPU jobs to complete.
*
* The frontend will guarantee that the appropriate pipeline barrier
* in graphics_queue has been used such that
* VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT cannot
* start until the frontend is done with the image.
*/
retro_vulkan_get_sync_index_t get_sync_index;
/* Returns a bitmask of how many swapchain images we currently have
* in the frontend.
*
* If bit #N is set in the return value, get_sync_index can return N.
* Knowing this value is useful for preallocating per-frame management
* structures ahead of time.
*
* While this value will typically remain constant throughout the
* applications lifecycle, it may for example change if the frontend
* suddently changes fullscreen state and/or latency.
*
* If this value ever changes, it is safe to assume that the device
* is completely idle and all synchronization objects can be deleted
* right away as desired.
*/
retro_vulkan_get_sync_index_mask_t get_sync_index_mask;
/* Instead of submitting the command buffer to the queue first, the core
* can pass along its command buffer to the frontend, and the frontend
* will submit the command buffer together with the frontends command buffers.
*
* This has the advantage that the overhead of vkQueueSubmit can be
* amortized into a single call. For this mode, semaphores in set_image
* will be ignored, so vkCmdPipelineBarrier must be used to synchronize
* the core and frontend.
*
* The command buffers in set_command_buffers are only executed once,
* even if frame duping is used.
*
* If frame duping is used, set_image should be used for the frames
* which should be duped instead.
*
* Command buffers passed to the frontend with set_command_buffers
* must not actually be submitted to the GPU until retro_video_refresh_t
* is called.
*
* The frontend must submit the command buffer before submitting any
* other command buffers provided by set_command_buffers. */
retro_vulkan_set_command_buffers_t set_command_buffers;
/* Waits on CPU for device activity for the current sync index to complete.
* This is useful since the core will not have a relevant fence to sync with
* when the frontend is submitting the command buffers. */
retro_vulkan_wait_sync_index_t wait_sync_index;
/* If the core submits command buffers itself to any of the queues provided
* in this interface, the core must lock and unlock the frontend from
* racing on the VkQueue.
*
* Queue submission can happen on any thread.
* Even if queue submission happens on the same thread as retro_run(),
* the lock/unlock functions must still be called.
*
* NOTE: Queue submissions are heavy-weight. */
retro_vulkan_lock_queue_t lock_queue;
retro_vulkan_unlock_queue_t unlock_queue;
/* Sets a semaphore which is signaled when the image in set_image can safely be reused.
* The semaphore is consumed next call to retro_video_refresh_t.
* The semaphore will be signalled even for duped frames.
* The semaphore will be signalled only once, so set_signal_semaphore should be called every
* frame.
* The semaphore may be VK_NULL_HANDLE, which disables semaphore signalling for next call to
* retro_video_refresh_t.
*
* This is mostly useful to support use cases where you're rendering to a single image that
* is recycled in a ping-pong fashion with the frontend to save memory (but potentially less
* throughput).
*/
retro_vulkan_set_signal_semaphore_t set_signal_semaphore;
};
#endif

1235
libretro/main.cpp Normal file

File diff suppressed because it is too large Load Diff

44
libretro/options.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <libretro.h>
#include "options.h"
namespace Options
{
static std::vector<OptionBase*>& GetOptionGroup(Groups group)
{
/* this garentees that 'list' is constructed first before being accessed other global constructors.*/
static std::vector<OptionBase*> list[OPTIONS_GROUPS_MAX];
return list[group];
}
void OptionBase::Register(Groups group)
{
GetOptionGroup(group).push_back(this);
}
void SetVariables()
{
std::vector<retro_variable> vars;
for (int grp = 0; grp < OPTIONS_GROUPS_MAX; grp++)
for (OptionBase* option : GetOptionGroup((Groups)grp))
if (!option->empty())
vars.push_back(option->getVariable());
if (vars.empty())
return;
vars.push_back({});
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars.data());
}
void CheckVariables()
{
bool updated = false;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && !updated)
return;
for (int grp = 0; grp < OPTIONS_GROUPS_MAX; grp++)
for (OptionBase* option : GetOptionGroup((Groups)grp))
option->SetDirty();
}
} // namespace Options

203
libretro/options.h Normal file
View File

@ -0,0 +1,203 @@
#pragma once
#include <libretro.h>
#include <string>
#include <vector>
#include <type_traits>
extern retro_environment_t environ_cb;
namespace Options
{
enum Groups
{
OPTIONS_BASE,
OPTIONS_GFX,
OPTIONS_EMU,
OPTIONS_GROUPS_MAX
};
class OptionBase
{
public:
void SetDirty() { m_dirty = true; }
retro_variable getVariable() { return {m_id, m_options.c_str()}; }
virtual bool empty() = 0;
protected:
OptionBase(const char* id, const char* name, Groups group)
: m_id(id)
, m_name(name)
{
m_options = m_name;
m_options.push_back(';');
Register(group);
}
const char* m_id;
const char* m_name;
bool m_dirty = true;
std::string m_options;
private:
void Register(Groups group);
};
template <typename T, Groups group = OPTIONS_BASE>
class Option : public OptionBase
{
static_assert(group < OPTIONS_GROUPS_MAX, "invalid option group index");
Option(Option&) = delete;
Option(Option&&) = delete;
Option& operator=(Option&) = delete;
public:
Option(const char* id, const char* name)
: OptionBase(id, name, group)
{
}
Option(const char* id, const char* name, T initial)
: OptionBase(id, name, group)
{
push_back(initial ? "enabled" : "disabled", initial);
push_back(!initial ? "enabled" : "disabled", !initial);
}
Option(const char* id, const char* name, std::initializer_list<std::pair<const char*, T>> list)
: OptionBase(id, name, group)
{
for (auto option : list)
push_back(option.first, option.second);
}
Option(const char* id, const char* name, std::initializer_list<const char*> list)
: OptionBase(id, name, group)
{
for (auto option : list)
push_back(option);
}
Option(const char* id, const char* name, T first, std::initializer_list<const char*> list)
: OptionBase(id, name, group)
{
for (auto option : list)
push_back(option, first + (int)m_list.size());
}
Option(const char* id, const char* name, T first, int count, int step = 1)
: OptionBase(id, name, group)
{
for (T i = first; i < first + count; i += step)
push_back(std::to_string(i), i);
}
void push_back(std::string option, T value)
{
if (m_list.empty())
{
m_options += std::string(" ") + option;
m_value = value;
}
else
m_options += std::string("|") + option;
m_list.push_back({option, value});
}
template <bool is_str = !std::is_integral<T>() && std::is_constructible<T, const char*>()>
void push_back(const char* option)
{
push_back(option, option);
}
#if 0
template <>
void push_back<false>(const char* option)
{
if (m_list.empty())
push_back(option, 0);
else
push_back(option, m_list.back().second + 1);
}
#endif
bool Updated()
{
if (m_dirty && !m_locked)
{
m_dirty = false;
retro_variable var{m_id};
T value = m_list.front().second;
if (environ_cb && environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
for (auto option : m_list)
{
if (option.first == var.value)
{
value = option.second;
break;
}
}
}
if (m_value != value)
{
m_value = value;
return true;
}
}
return false;
}
void UpdateAndLock()
{
m_locked = false;
Updated();
m_locked = true;
}
operator T()
{
Updated();
return m_value;
}
T Get()
{
return (T) * this;
}
template <typename S>
bool operator==(S value)
{
return (T) * this == value;
}
template <typename S>
bool operator!=(S value)
{
return (T) * this != value;
}
virtual bool empty() override { return m_list.empty(); }
private:
bool m_locked = false;
T m_value;
std::vector<std::pair<std::string, T>> m_list;
};
template <typename T>
using EmuOption = Option<T, OPTIONS_EMU>;
template <typename T>
using GfxOption = Option<T, OPTIONS_GFX>;
void SetVariables();
void CheckVariables();
extern GfxOption<int> upscale_multiplier;
extern GfxOption<std::string> renderer;
} // namespace Options

View File

@ -1423,6 +1423,7 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode)
void cdvdUpdateTrayState()
{
//#ifndef __LIBRETRO__
if (cdvd.Tray.cdvdActionSeconds > 0)
{
if (--cdvd.Tray.cdvdActionSeconds == 0)
@ -1462,6 +1463,7 @@ void cdvdUpdateTrayState()
}
}
}
//#endif
}
void cdvdVsync()

View File

@ -36,6 +36,10 @@ else()
)
endif()
if(CCACHE_FOUND)
set(CommonFlags ${CommonFlags} -Werror=date-time)
endif()
if(GCC_VERSION VERSION_EQUAL "8.0" OR GCC_VERSION VERSION_GREATER "8.0")
# gs is pretty bad at this
target_compile_options(PCSX2_FLAGS INTERFACE -Wno-packed-not-aligned -Wno-class-memaccess)
@ -1028,6 +1032,52 @@ set(pcsx2x86Headers
x86/R5900_Profiler.h
)
if(LIBRETRO)
set(pcsx2DEV9Sources)
set(pcsx2DEV9Headers)
set(pcsx2USBSources)
set(pcsx2USBHeaders)
set(pcsx2RecordingSources)
set(pcsx2RecordingVirtualPadResources)
set(pcsx2RecordingHeaders)
set(pcsx2ZipToolsSources)
set(pcsx2ZipToolsHeaders)
LIST(REMOVE_ITEM pcsx2PADSources
PAD/Host/KeyStatus.cpp
)
LIST(REMOVE_ITEM pcsx2FrontendSources
Frontend/FullscreenUI.cpp
Frontend/ImGuiManager.cpp
Frontend/ImGuiOverlays.cpp
Frontend/CommonHotkeys.cpp
Frontend/InputManager.cpp
)
LIST(REMOVE_ITEM pcsx2FrontendSources
Frontend/DInputSource.cpp
Frontend/XInputSource.cpp
)
LIST(REMOVE_ITEM pcsx2SPU2Sources
# SPU2/Debug.cpp
# SPU2/Wavedump_wav.cpp
SPU2/SndOut.cpp
SPU2/SndOut_SDL.cpp
SPU2/SndOut_Portaudio.cpp
SPU2/SndOut_XAudio2.cpp
SPU2/Config.cpp
SPU2/ConfigSoundTouch.cpp
SPU2/Timestretcher.cpp
SPU2/wx/wxConfig.cpp
SPU2/Linux/Alsa.cpp
SPU2/Linux/Dialogs.cpp
)
LIST(APPEND pcsx2GSSources
GS/Renderers/Null/GSDeviceNull.cpp
)
endif()
# These ones benefit a lot from LTO
set(pcsx2LTOSources
${pcsx2Sources}
@ -1130,10 +1180,15 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
)
target_link_libraries(PCSX2_FLAGS INTERFACE
demangler
simpleini
)
if(NOT LIBRETRO)
target_link_libraries(PCSX2_FLAGS INTERFACE
demangler
)
endif()
if(WIN32)
target_link_libraries(PCSX2_FLAGS INTERFACE
WIL::WIL
@ -1177,7 +1232,7 @@ target_include_directories(PCSX2_FLAGS INTERFACE
"${FFMPEG_INCLUDE_DIRS}"
)
if(COMMAND target_precompile_headers)
if(COMMAND target_precompile_headers AND NOT CCACHE_FOUND)
message("Using precompiled headers.")
target_precompile_headers(PCSX2_FLAGS INTERFACE PrecompiledHeader.h)
endif()

View File

@ -15,6 +15,7 @@
#pragma once
#include "PrecompiledHeader.h"
#include "common/emitter/tools.h"
#include "common/General.h"
#include <array>
@ -1282,7 +1283,9 @@ struct Pcsx2Config
FramerateOptions Framerate;
SPU2Options SPU2;
DEV9Options DEV9;
#ifndef __LIBRETRO__
USBOptions USB;
#endif
TraceLogFilters Trace;

View File

@ -85,9 +85,11 @@ static bool s_discord_presence_active = false;
bool CommonHost::InitializeCriticalFolders()
{
#ifndef __LIBRETRO__
SetAppRoot();
SetResourcesDirectory();
SetDataDirectory();
#endif
// logging of directories in case something goes wrong super early
Console.WriteLn("AppRoot Directory: %s", EmuFolders::AppRoot.c_str());

View File

@ -43,7 +43,11 @@ void CommonHost::Internal::ResetVMHotkeyState()
static void HotkeyAdjustTargetSpeed(double delta)
{
#ifdef ENABLE_ACHIEVEMENTS
const double min_speed = Achievements::ChallengeModeActive() ? 1.0 : 0.1;
#else
const double min_speed = 1.0;
#endif
EmuConfig.Framerate.NominalScalar = std::max(min_speed, EmuConfig.GS.LimitScalar + delta);
VMManager::SetLimiterMode(LimiterModeType::Nominal);
Host::AddIconOSDMessage("SpeedChanged", ICON_FA_CLOCK,

View File

@ -1170,6 +1170,7 @@ std::string GameList::GetNewCoverImagePathForEntry(const Entry* entry, const cha
bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial, ProgressCallback* progress,
std::function<void(const Entry*, std::string)> save_callback)
{
#ifndef __LIBRETRO__
if (!progress)
progress = ProgressCallback::NullProgressCallback;
@ -1290,5 +1291,6 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
progress->IncrementProgressValue();
}
#endif
return true;
}

View File

@ -412,8 +412,9 @@ void CommonHost::UpdateLogging(SettingsInterface& si)
// Input Recording Logs
SysConsole.recordingConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableInputRecordingLogs", true);
SysConsole.controlInfo.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableControllerLogs", false);
#ifndef __LIBRETRO__
UpdateLoggingSinks(system_console_enabled, file_logging_enabled);
#endif
}
void CommonHost::SetDefaultLoggingSettings(SettingsInterface& si)

View File

@ -45,6 +45,7 @@ void gsReset()
void gsUpdateFrequency(Pcsx2Config& config)
{
#ifndef __LIBRETRO__
if (config.GS.FrameLimitEnable)
{
switch (config.LimiterMode)
@ -66,6 +67,7 @@ void gsUpdateFrequency(Pcsx2Config& config)
}
}
else
#endif
{
config.GS.LimitScalar = 0.0f;
}

View File

@ -362,7 +362,11 @@ public:
std::mutex m_lock_Stack;
#endif
#ifdef __LIBRETRO__
std::thread::id m_thread;
#else
std::thread m_thread;
#endif
Threading::ThreadHandle m_thread_handle;
std::atomic_bool m_open_flag{false};
std::atomic_bool m_shutdown_flag{false};
@ -407,6 +411,13 @@ public:
void PostVsyncStart(bool registers_written);
void InitAndReadFIFO(u8* mem, u32 qwc);
#ifdef __LIBRETRO__
void StepFrame();
void Flush();
void SignalVsync();
void MainLoop(bool flush_all = false);
#endif
void RunOnGSThread(AsyncCallType func);
void ApplySettings();
void ResizeDisplayWindow(int width, int height, float scale);
@ -420,10 +431,10 @@ public:
u32* width, u32* height, std::vector<u32>* pixels);
void SetRunIdle(bool enabled);
protected:
bool TryOpenGS();
void CloseGS();
protected:
void ThreadEntryPoint();
void MainLoop();

View File

@ -64,6 +64,10 @@ static HRESULT s_hr = E_FAIL;
#endif
#ifdef __LIBRETRO__
#include "Renderers/Null/GSDeviceNull.h"
#endif
#include <fstream>
// do NOT undefine this/put it above includes, as x11 people love to redefine
@ -81,7 +85,7 @@ int GSinit()
GSUtil::Init();
#ifdef _WIN32
#if defined(_WIN32) && !defined(__LIBRETRO__)
s_hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
@ -92,7 +96,7 @@ void GSshutdown()
{
GSclose();
#ifdef _WIN32
#if defined(_WIN32) && !defined(__LIBRETRO__)
if (SUCCEEDED(s_hr))
{
::CoUninitialize();
@ -107,9 +111,26 @@ 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:
case GSRendererType::SW:
return RenderAPI::OpenGL;
case GSRendererType::VK:
@ -131,6 +152,7 @@ static RenderAPI GetAPIForRenderer(GSRendererType renderer)
default:
return GetAPIForRenderer(GSUtil::GetPreferredRenderer());
}
#endif
}
static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool recreate_window)
@ -162,7 +184,11 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool
g_gs_device = std::make_unique<GSDeviceVK>();
break;
#endif
#ifdef __LIBRETRO__
case RenderAPI::None:
g_gs_device = std::make_unique<GSDeviceNull>();
break;
#endif
default:
Console.Error("Unsupported render API %s", GSDevice::RenderAPIToString(new_api));
return false;
@ -761,6 +787,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// Handle OSD scale changes by pushing a window resize through.
#ifndef __LIBRETRO__
if (new_config.OsdScale != old_config.OsdScale)
ImGuiManager::WindowResized();
@ -771,7 +798,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
pxFailRel("Failed to do full GS reopen");
return;
}
#endif
// Options which aren't using the global struct yet, so we need to recreate all GS objects.
if (
GSConfig.SWExtraThreads != old_config.SWExtraThreads ||

View File

@ -147,3 +147,12 @@ namespace Host
/// Returns the desired vsync mode, depending on the runtime environment.
VsyncMode GetEffectiveVSyncMode();
}
#ifdef __LIBRETRO__
#include <libretro.h>
#include "options.h"
extern retro_hw_render_callback hw_render;
#define GL_DEFAULT_FRAMEBUFFER hw_render.get_current_framebuffer()
#else
#define GL_DEFAULT_FRAMEBUFFER 0
#endif

View File

@ -222,6 +222,14 @@ void GSDevice::ReleaseWindow()
m_window_info = WindowInfo();
}
void GSDevice::ResetAPIState()
{
}
void GSDevice::RestoreAPIState()
{
}
bool GSDevice::GetHostRefreshRate(float* refresh_rate)
{
if (m_window_info.surface_refresh_rate > 0.0f)

View File

@ -836,6 +836,9 @@ public:
virtual bool Create();
virtual void Destroy();
virtual void ResetAPIState();
virtual void RestoreAPIState();
/// Returns the graphics API used by this device.
virtual RenderAPI GetRenderAPI() const = 0;

View File

@ -15,6 +15,7 @@
#pragma once
#include <climits>
#include "common/AlignedMalloc.h"
template <class T>

View File

@ -574,9 +574,10 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
if (skip_frame)
{
g_gs_device->ResetAPIState();
if (BeginPresentFrame(true))
EndPresentFrame();
g_gs_device->RestoreAPIState();
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
return;
}
@ -595,10 +596,15 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
if (current && !blank_frame)
{
src_rect = CalculateDrawSrcRect(current);
#ifdef __LIBRETRO__
src_uv = GSVector4(0, 0, 1, 1);
draw_rect = GSVector4(0, 0, current->GetWidth(), current->GetHeight());
#else
src_uv = GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy();
draw_rect = CalculateDrawDstRect(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight(),
src_rect, current->GetSize(), s_display_alignment, g_gs_device->UsesLowerLeftOrigin(),
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets));
#endif
s_last_draw_rect = draw_rect;
if (GSConfig.CASMode != GSCASMode::Disabled)
@ -621,6 +627,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
}
}
g_gs_device->ResetAPIState();
if (BeginPresentFrame(false))
{
if (current && !blank_frame)
@ -637,7 +644,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
if (GSConfig.OsdShowGPU)
PerformanceMetrics::OnGPUPresent(g_gs_device->GetAndResetAccumulatedGPUTime());
}
g_gs_device->RestoreAPIState();
PerformanceMetrics::Update(registers_written, fb_sprite_frame, false);
// snapshot
@ -836,6 +843,7 @@ void GSRenderer::StopGSDump()
void GSRenderer::PresentCurrentFrame()
{
g_gs_device->ResetAPIState();
if (BeginPresentFrame(false))
{
GSTexture* current = g_gs_device->GetCurrent();
@ -857,6 +865,7 @@ void GSRenderer::PresentCurrentFrame()
EndPresentFrame();
}
g_gs_device->RestoreAPIState();
}
void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float* display_x, float* display_y)

View File

@ -25,6 +25,9 @@ GSTexture::GSTexture() = default;
bool GSTexture::Save(const std::string& fn)
{
#ifdef __LIBRETRO__
return true;
#else
#ifdef PCSX2_DEVBUILD
GSPng::Format format = GSPng::RGB_A_PNG;
#else
@ -52,6 +55,7 @@ bool GSTexture::Save(const std::string& fn)
const int compression = GSConfig.PNGCompressionLevel;
return GSPng::Save(format, fn, dl->GetMapPointer(), m_size.x, m_size.y, dl->GetMapPitch(), compression, g_gs_device->IsRBSwapped());
#endif
}
void GSTexture::Swap(GSTexture* tex)

View File

@ -35,6 +35,11 @@
#include <d3dcompiler.h>
#include <dxgidebug.h>
#ifdef __LIBRETRO__
#include "libretro_d3d.h"
extern retro_environment_t environ_cb;
#endif
// #define REPORT_LEAKED_OBJECTS 1
static constexpr std::array<float, 4> s_present_clear_color = {};
@ -81,7 +86,24 @@ bool GSDevice11::Create()
{
if (!GSDevice::Create())
return false;
#ifdef __LIBRETRO__
retro_hw_render_interface_d3d11 *d3d11 = nullptr;
if (!environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void **)&d3d11) || !d3d11) {
printf("Failed to get HW rendering interface!\n");
return false;
}
if (d3d11->interface_version != RETRO_HW_RENDER_INTERFACE_D3D11_VERSION) {
printf("HW render interface mismatch, expected %u, got %u!\n", RETRO_HW_RENDER_INTERFACE_D3D11_VERSION, d3d11->interface_version);
return false;
}
if (FAILED(d3d11->device->QueryInterface(&m_dev)) || FAILED(d3d11->context->QueryInterface(&m_ctx)))
{
Console.Error("Direct3D 11.1 is required and not supported.");
return false;
}
#else
UINT create_flags = 0;
if (GSConfig.UseDebugDevice)
create_flags |= D3D11_CREATE_DEVICE_DEBUG;
@ -151,7 +173,7 @@ bool GSDevice11::Create()
hr = m_dxgi_factory->CheckFeatureSupport(
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
#endif
if (!AcquireWindow(true) || (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain()))
return false;
@ -559,11 +581,16 @@ void GSDevice11::SetFeatures()
bool GSDevice11::HasSurface() const
{
#ifdef __LIBRETRO__
return true;
#else
return static_cast<bool>(m_swap_chain);
#endif
}
bool GSDevice11::GetHostRefreshRate(float* refresh_rate)
{
#ifndef __LIBRETRO__
if (m_swap_chain && m_is_exclusive_fullscreen)
{
DXGI_SWAP_CHAIN_DESC desc;
@ -577,6 +604,7 @@ bool GSDevice11::GetHostRefreshRate(float* refresh_rate)
return true;
}
}
#endif
return GSDevice::GetHostRefreshRate(refresh_rate);
}
@ -588,6 +616,7 @@ void GSDevice11::SetVSync(VsyncMode mode)
bool GSDevice11::CreateSwapChain()
{
#ifndef __LIBRETRO__
constexpr DXGI_FORMAT swap_chain_format = DXGI_FORMAT_R8G8B8A8_UNORM;
if (m_window_info.type != WindowInfo::Type::Win32)
@ -684,6 +713,7 @@ bool GSDevice11::CreateSwapChain()
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
#endif
if (!CreateSwapChainRTV())
{
@ -691,14 +721,17 @@ bool GSDevice11::CreateSwapChain()
return false;
}
#ifndef __LIBRETRO__
// Render a frame as soon as possible to clear out whatever was previously being displayed.
m_ctx->ClearRenderTargetView(m_swap_chain_rtv.get(), s_present_clear_color.data());
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
#endif
return true;
}
bool GSDevice11::CreateSwapChainRTV()
{
#ifndef __LIBRETRO__
wil::com_ptr_nothrow<ID3D11Texture2D> backbuffer;
HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.put()));
if (FAILED(hr))
@ -739,12 +772,14 @@ bool GSDevice11::CreateSwapChainRTV()
m_window_info.surface_refresh_rate = 0.0f;
}
}
#endif
return true;
}
void GSDevice11::DestroySwapChain()
{
#ifndef __LIBRETRO__
if (!m_swap_chain)
return;
@ -757,6 +792,7 @@ void GSDevice11::DestroySwapChain()
m_swap_chain.reset();
m_is_exclusive_fullscreen = false;
#endif
}
bool GSDevice11::UpdateWindow()
@ -832,6 +868,7 @@ std::string GSDevice11::GetDriverInfo() const
void GSDevice11::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
#ifndef __LIBRETRO__
if (!m_swap_chain || m_is_exclusive_fullscreen)
return;
@ -849,6 +886,7 @@ void GSDevice11::ResizeWindow(s32 new_window_width, s32 new_window_height, float
if (!CreateSwapChainRTV())
pxFailRel("Failed to recreate swap chain RTV after resize");
#endif
}
bool GSDevice11::SupportsExclusiveFullscreen() const
@ -858,6 +896,7 @@ bool GSDevice11::SupportsExclusiveFullscreen() const
GSDevice::PresentResult GSDevice11::BeginPresent(bool frame_skip)
{
#ifndef __LIBRETRO__
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
@ -895,12 +934,14 @@ GSDevice::PresentResult GSDevice11::BeginPresent(bool frame_skip)
const GSVector2i size = GetWindowSize();
SetViewport(size);
SetScissor(GSVector4i::loadh(size));
#endif
return PresentResult::OK;
}
void GSDevice11::EndPresent()
{
#ifndef __LIBRETRO__
RenderImGui();
// See note in BeginPresent() for why it's conditional on vsync-off.
@ -915,6 +956,7 @@ void GSDevice11::EndPresent()
if (m_gpu_timing_enabled)
KickTimestampQuery();
#endif
// clear out the swap chain view, it might get resized..
OMSetRenderTargets(nullptr, nullptr, nullptr);
@ -1302,6 +1344,16 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear)
{
#ifdef __LIBRETRO__
ID3D11RenderTargetView *nullView = nullptr;
m_ctx->OMSetRenderTargets(1, &nullView, nullptr);
ID3D11ShaderResourceView* srv = *(GSTexture11*)sTex;
m_ctx->PSSetShaderResources(0, 1, &srv);
extern retro_video_refresh_t video_cb;
video_cb(RETRO_HW_FRAME_BUFFER_VALID, sTex->GetWidth(), sTex->GetHeight(), 0);
#else
ASSERT(sTex);
GSVector2i ds;
@ -1366,6 +1418,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
//
PSSetShaderResources(nullptr, nullptr);
#endif
}
void GSDevice11::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
@ -2365,3 +2418,42 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
Recycle(hdr_rt);
}
}
void GSDevice11::ResetAPIState()
{
// Clear out the GS, since the imgui draw doesn't get rid of it.
m_ctx->GSSetShader(nullptr, nullptr, 0);
}
void GSDevice11::RestoreAPIState()
{
const UINT vb_offset = 0;
m_ctx->IASetVertexBuffers(0, 1, m_vb.addressof(), &m_state.vb_stride, &vb_offset);
m_ctx->IASetIndexBuffer(m_ib.get(), DXGI_FORMAT_R16_UINT, 0);
m_ctx->IASetInputLayout(m_state.layout);
m_ctx->IASetPrimitiveTopology(m_state.topology);
m_ctx->VSSetShader(m_state.vs, nullptr, 0);
m_ctx->VSSetConstantBuffers(0, 1, &m_state.vs_cb);
m_ctx->PSSetShader(m_state.ps, nullptr, 0);
m_ctx->PSSetConstantBuffers(0, 1, &m_state.ps_cb);
m_ctx->IASetIndexBuffer(m_state.index_buffer, DXGI_FORMAT_R16_UINT, 0);
const CD3D11_VIEWPORT vp(0.0f, 0.0f,
static_cast<float>(m_state.viewport.x), static_cast<float>(m_state.viewport.y),
0.0f, 1.0f);
m_ctx->RSSetViewports(1, &vp);
m_ctx->RSSetScissorRects(1, reinterpret_cast<const D3D11_RECT*>(&m_state.scissor));
m_ctx->RSSetState(m_rs.get());
m_ctx->OMSetDepthStencilState(m_state.dss, m_state.sref);
const float blend_factors[4] = { m_state.bf, m_state.bf, m_state.bf, m_state.bf };
m_ctx->OMSetBlendState(m_state.bs, blend_factors, 0xFFFFFFFFu);
PSUpdateShaderState();
if (m_state.rt_view)
m_ctx->OMSetRenderTargets(1, &m_state.rt_view, m_state.dsv);
else
m_ctx->OMSetRenderTargets(0, nullptr, m_state.dsv);
}

View File

@ -145,8 +145,10 @@ private:
wil::com_ptr_nothrow<ID3D11DeviceContext1> m_ctx;
wil::com_ptr_nothrow<ID3DUserDefinedAnnotation> m_annotation;
#ifndef __LIBRETRO__
wil::com_ptr_nothrow<IDXGISwapChain1> m_swap_chain;
wil::com_ptr_nothrow<ID3D11RenderTargetView> m_swap_chain_rtv;
#endif
wil::com_ptr_nothrow<ID3D11Buffer> m_vb;
wil::com_ptr_nothrow<ID3D11Buffer> m_ib;
@ -377,4 +379,7 @@ public:
ID3D11Device1* operator->() { return m_dev.get(); }
operator ID3D11Device1*() { return m_dev.get(); }
operator ID3D11DeviceContext1*() { return m_ctx.get(); }
void ResetAPIState() override;
void RestoreAPIState() override;
};

View File

@ -117,6 +117,9 @@ void GSTexture11::Unmap()
bool GSTexture11::Save(const std::string& fn)
{
#ifdef __LIBRETRO__
return true;
#else
D3D11_TEXTURE2D_DESC desc = m_desc;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
@ -208,6 +211,7 @@ bool GSTexture11::Save(const std::string& fn)
GSDevice11::GetInstance()->GetD3DContext()->Unmap(res.get(), 0);
return success;
#endif
}
void GSTexture11::GenerateMipmap()

View File

@ -38,6 +38,12 @@
#include <sstream>
#include <limits>
#ifdef __LIBRETRO__
#include "libretro_d3d.h"
extern retro_hw_render_interface_d3d12 *d3d12;
extern retro_video_refresh_t video_cb;
#endif
static bool IsDATMConvertShader(ShaderConvert i) { return (i == ShaderConvert::DATM_0 || i == ShaderConvert::DATM_1); }
static bool IsDATEModePrimIDInit(u32 flag) { return flag == 1 || flag == 2; }
@ -115,7 +121,11 @@ RenderAPI GSDevice12::GetRenderAPI() const
bool GSDevice12::HasSurface() const
{
#ifdef __LIBRETRO__
return true;
#else
return static_cast<bool>(m_swap_chain);
#endif
}
bool GSDevice12::Create()
@ -225,6 +235,7 @@ void GSDevice12::Destroy()
bool GSDevice12::GetHostRefreshRate(float* refresh_rate)
{
#ifndef __LIBRETRO__
if (m_swap_chain && m_is_exclusive_fullscreen)
{
DXGI_SWAP_CHAIN_DESC desc;
@ -238,6 +249,7 @@ bool GSDevice12::GetHostRefreshRate(float* refresh_rate)
return true;
}
}
#endif
return GSDevice::GetHostRefreshRate(refresh_rate);
}
@ -249,6 +261,7 @@ void GSDevice12::SetVSync(VsyncMode mode)
bool GSDevice12::CreateSwapChain()
{
#ifndef __LIBRETRO__
constexpr DXGI_FORMAT swap_chain_format = DXGI_FORMAT_R8G8B8A8_UNORM;
if (m_window_info.type != WindowInfo::Type::Win32)
@ -324,6 +337,7 @@ bool GSDevice12::CreateSwapChain()
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
#endif
if (!CreateSwapChainRTV())
{
@ -331,6 +345,7 @@ bool GSDevice12::CreateSwapChain()
return false;
}
#ifndef __LIBRETRO__
// Render a frame as soon as possible to clear out whatever was previously being displayed.
EndRenderPass();
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
@ -341,11 +356,13 @@ bool GSDevice12::CreateSwapChain()
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PRESENT);
ExecuteCommandList(false);
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
#endif
return true;
}
bool GSDevice12::CreateSwapChainRTV()
{
#ifndef __LIBRETRO__
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc);
if (FAILED(hr))
@ -394,19 +411,23 @@ bool GSDevice12::CreateSwapChainRTV()
}
m_current_swap_chain_buffer = 0;
#endif
return true;
}
void GSDevice12::DestroySwapChainRTVs()
{
#ifndef __LIBRETRO__
for (D3D12::Texture& buffer : m_swap_chain_buffers)
buffer.Destroy(false);
m_swap_chain_buffers.clear();
m_current_swap_chain_buffer = 0;
#endif
}
void GSDevice12::DestroySwapChain()
{
#ifndef __LIBRETRO__
if (!m_swap_chain)
return;
@ -419,6 +440,7 @@ void GSDevice12::DestroySwapChain()
m_swap_chain.reset();
m_is_exclusive_fullscreen = false;
#endif
}
bool GSDevice12::UpdateWindow()
@ -441,8 +463,10 @@ bool GSDevice12::UpdateWindow()
void GSDevice12::DestroySurface()
{
#ifndef __LIBRETRO__
ExecuteCommandList(true);
DestroySwapChain();
#endif
}
std::string GSDevice12::GetDriverInfo() const
@ -489,6 +513,7 @@ std::string GSDevice12::GetDriverInfo() const
void GSDevice12::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
#ifndef __LIBRETRO__
if (!m_swap_chain)
return;
@ -508,6 +533,7 @@ void GSDevice12::ResizeWindow(s32 new_window_width, s32 new_window_height, float
if (!CreateSwapChainRTV())
pxFailRel("Failed to recreate swap chain RTV after resize");
#endif
}
bool GSDevice12::SupportsExclusiveFullscreen() const
@ -522,6 +548,7 @@ GSDevice::PresentResult GSDevice12::BeginPresent(bool frame_skip)
if (m_device_lost)
return PresentResult::DeviceLost;
#ifndef __LIBRETRO__
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
@ -549,11 +576,13 @@ GSDevice::PresentResult GSDevice12::BeginPresent(bool frame_skip)
0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
cmdlist->RSSetViewports(1, &vp);
cmdlist->RSSetScissorRects(1, &scissor);
#endif
return PresentResult::OK;
}
void GSDevice12::EndPresent()
{
#ifndef __LIBRETRO__
RenderImGui();
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
@ -572,6 +601,7 @@ void GSDevice12::EndPresent()
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
else
m_swap_chain->Present(static_cast<UINT>(vsync), 0);
#endif
InvalidateCachedState();
}
@ -855,6 +885,14 @@ void GSDevice12::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
void GSDevice12::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
PresentShader shader, float shaderTime, bool linear)
{
#ifdef __LIBRETRO__
GSTexture12* texture = (GSTexture12*)sTex;
texture->TransitionToState(d3d12->required_state);
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None);
d3d12->set_texture(d3d12->handle, texture->GetTexture().GetResource(), texture->GetTexture().GetResource()->GetDesc().Format);
video_cb(RETRO_HW_FRAME_BUFFER_VALID, texture->GetWidth(), texture->GetHeight(), 0);
#else
DisplayConstantBuffer cb;
cb.SetSource(sRect, sTex->GetSize());
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(GetWindowWidth(), GetWindowHeight()));
@ -864,6 +902,7 @@ void GSDevice12::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
DoStretchRect(static_cast<GSTexture12*>(sTex), sRect, static_cast<GSTexture12*>(dTex), dRect,
m_present[static_cast<int>(shader)].get(), linear, true);
#endif
}
void GSDevice12::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
@ -3368,3 +3407,13 @@ void GSDevice12::UploadHWDrawVerticesAndIndices(const GSHWDrawConfig& config)
IASetIndexBuffer(config.indices, config.nindices);
}
}
void GSDevice12::ResetAPIState()
{
EndRenderPass();
}
void GSDevice12::RestoreAPIState()
{
InvalidateCachedState();
}

View File

@ -139,9 +139,11 @@ public:
private:
ComPtr<IDXGIFactory5> m_dxgi_factory;
#ifndef __LIBRETRO__
ComPtr<IDXGISwapChain1> m_swap_chain;
std::vector<D3D12::Texture> m_swap_chain_buffers;
u32 m_current_swap_chain_buffer = 0;
#endif
bool m_allow_tearing_supported = false;
bool m_using_allow_tearing = false;
@ -454,4 +456,7 @@ private:
// current pipeline selector - we save this in the struct to avoid re-zeroing it every draw
PipelineSelector m_pipeline_selector = {};
void ResetAPIState() override;
void RestoreAPIState() override;
};

View File

@ -1294,6 +1294,7 @@ void GSDeviceMTL::EndPresent()
FlushEncoders();
FrameCompleted();
m_current_drawable = nullptr;
#if !defined(__LIBRETRO__)
if (m_capture_start_frame)
{
if (@available(macOS 10.15, iOS 13, *))
@ -1342,6 +1343,7 @@ void GSDeviceMTL::EndPresent()
}
}
}
#endif
}}
void GSDeviceMTL::SetVSync(VsyncMode mode)

View File

@ -0,0 +1,215 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "GS/Renderers/Null/GSDeviceNull.h"
#include "GS.h"
#ifdef __LIBRETRO__
#include <libretro.h>
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<u32, NUM_CAS_CONSTANTS>& 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::UpdateWindow()
{
return true;
}
void GSDeviceNull::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
}
bool GSDeviceNull::SupportsExclusiveFullscreen() const
{
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<GSDownloadTexture> 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()
{
}

View File

@ -0,0 +1,92 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#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<u32, NUM_CAS_CONSTANTS>& constants) final;
GSDeviceNull();
~GSDeviceNull() override;
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override;
void DestroySurface() override;
bool UpdateWindow() override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const 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<GSDownloadTexture> 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;
};

View File

@ -76,7 +76,7 @@ void GSDeviceOGL::SetVSync(VsyncMode mode)
// Window framebuffer has to be bound to call SetSwapInterval.
GLint current_fbo = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
if (mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
m_gl_context->SetSwapInterval(static_cast<s32>(mode != VsyncMode::Off));
@ -228,7 +228,7 @@ bool GSDeviceOGL::Create()
// Always read from the first buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
}
// ****************************************************************
@ -747,8 +747,9 @@ GSDevice::PresentResult GSDeviceOGL::BeginPresent(bool frame_skip)
void GSDeviceOGL::EndPresent()
{
#ifndef __LIBRETRO__
RenderImGui();
#endif
if (m_gpu_timing_enabled)
PopTimestampQuery();
@ -875,6 +876,80 @@ float GSDeviceOGL::GetAndResetAccumulatedGPUTime()
return value;
}
void GSDeviceOGL::ResetAPIState()
{
if (GLState::point_size)
glDisable(GL_PROGRAM_POINT_SIZE);
if (GLState::line_width != 1.0f)
glLineWidth(1.0f);
// clear out DSB
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
}
void GSDeviceOGL::RestoreAPIState()
{
glBindVertexArray(m_vao);
if(GLState::fbo)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
else
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
glViewportIndexedf(0, 0, 0, static_cast<float>(GLState::viewport.x), static_cast<float>(GLState::viewport.y));
glScissorIndexed(0, GLState::scissor.x, GLState::scissor.y, GLState::scissor.width(), GLState::scissor.height());
glBlendEquationSeparate(GLState::eq_RGB, GL_FUNC_ADD);
glBlendFuncSeparate(GLState::f_sRGB, GLState::f_dRGB, GL_ONE, GL_ZERO);
const float bf = static_cast<float>(GLState::bf) / 128.0f;
glBlendColor(bf, bf, bf, bf);
if (GLState::blend)
{
glEnable(GL_BLEND);
}
else
{
glDisable(GL_BLEND);
}
const OMColorMaskSelector msel{ GLState::wrgba };
glColorMask(msel.wr, msel.wg, msel.wb, msel.wa);
GLState::depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
glDepthFunc(GLState::depth_func);
glDepthMask(GLState::depth_mask);
if (GLState::stencil)
{
glEnable(GL_STENCIL_TEST);
}
else
{
glDisable(GL_STENCIL_TEST);
}
glStencilFunc(GLState::stencil_func, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GLState::stencil_pass);
glBindSampler(0, GLState::ps_ss);
for (GLuint i = 0; i < sizeof(GLState::tex_unit) / sizeof(GLState::tex_unit[0]); i++)
glBindTextureUnit(i, GLState::tex_unit[i]);
if (GLState::point_size)
glEnable(GL_PROGRAM_POINT_SIZE);
if (GLState::line_width != 1.0f)
glLineWidth(GLState::line_width);
// Force UBOs to be reuploaded, we don't know what else was bound there.
std::memset(&m_vs_cb_cache, 0xFF, sizeof(m_vs_cb_cache));
std::memset(&m_ps_cb_cache, 0xFF, sizeof(m_ps_cb_cache));
}
void GSDeviceOGL::DrawPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
@ -1324,8 +1399,15 @@ void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
}
else
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo_write);
if(m_fbo_read)
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
else
glBindFramebuffer(GL_READ_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
if(m_fbo_write)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo_write);
else
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sid, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, did, 0);
@ -1334,8 +1416,12 @@ void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
glBlitFramebuffer(r.x, r.y, r.x + w, r.y + h, destX + r.x, destY + r.y, destX + r.x + w, destY + r.y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glEnable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
if(GLState::fbo)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
else
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
glBindFramebuffer(GL_READ_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
}
}
@ -2089,7 +2175,10 @@ void GSDeviceOGL::OMSetFBO(GLuint fbo)
if (GLState::fbo != fbo)
{
GLState::fbo = fbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
if(fbo)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
else
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
}
}

View File

@ -304,6 +304,9 @@ public:
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
void ResetAPIState() override;
void RestoreAPIState() override;
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);

View File

@ -339,6 +339,9 @@ void GSTextureOGL::GenerateMipmap()
bool GSTextureOGL::Save(const std::string& fn)
{
#ifdef __LIBRETRO__
return true;
#else
// Collect the texture data
u32 pitch = 4 * m_size.x;
u32 buf_size = pitch * m_size.y * 2; // Note *2 for security (depth/stencil)
@ -356,7 +359,7 @@ bool GSTextureOGL::Save(const std::string& fn)
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_texture_id, 0);
glReadPixels(0, 0, m_size.x, m_size.y, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, image.get());
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
fmt = GSPng::RGB_A_PNG;
}
@ -388,10 +391,11 @@ bool GSTextureOGL::Save(const std::string& fn)
glReadPixels(0, 0, m_size.x, m_size.y, GL_RED, GL_UNSIGNED_BYTE, image.get());
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
}
return GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch, GSConfig.PNGCompressionLevel);
#endif
}
void GSTextureOGL::Swap(GSTexture* tex)
@ -518,7 +522,7 @@ void GSDownloadTextureOGL::CopyFromTexture(
glReadPixels(src.left, src.top, src.width(), src.height(), glTex->GetIntFormat(), glTex->GetIntType(), m_cpu_buffer + copy_offset);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER);
if (m_cpu_buffer)
{

View File

@ -87,6 +87,9 @@ void GSTextureSW::Unmap()
bool GSTextureSW::Save(const std::string& fn)
{
#ifdef __LIBRETRO__
return true;
#else
#ifdef PCSX2_DEVBUILD
GSPng::Format fmt = GSPng::RGB_A_PNG;
#else
@ -94,6 +97,7 @@ bool GSTextureSW::Save(const std::string& fn)
#endif
int compression = GSConfig.PNGCompressionLevel;
return GSPng::Save(fmt, fn, static_cast<u8*>(m_data), m_size.x, m_size.y, m_pitch, compression);
#endif
}
void GSTextureSW::Swap(GSTexture* tex)

View File

@ -479,6 +479,16 @@ float GSDeviceVK::GetAndResetAccumulatedGPUTime()
return g_vulkan_context->GetAndResetAccumulatedGPUTime();
}
void GSDeviceVK::ResetAPIState()
{
EndRenderPass();
}
void GSDeviceVK::RestoreAPIState()
{
InvalidateCachedState();
}
#ifdef ENABLE_OGL_DEBUG
static std::array<float, 3> Palette(float phase, const std::array<float, 3>& a, const std::array<float, 3>& b,
const std::array<float, 3>& c, const std::array<float, 3>& d)
@ -700,7 +710,11 @@ bool GSDeviceVK::CheckFeatures()
m_features.provoking_vertex_last = g_vulkan_context->GetOptionalExtensions().vk_ext_provoking_vertex;
m_features.dual_source_blend = features.dualSrcBlend && !GSConfig.DisableDualSourceBlend;
m_features.clip_control = true;
#ifdef __APPLE__
m_features.vs_expand = false;
#else
m_features.vs_expand = g_vulkan_context->GetOptionalExtensions().vk_khr_shader_draw_parameters;
#endif
if (!m_features.dual_source_blend)
Console.Warning("Vulkan driver is missing dual-source blending. This will have an impact on performance.");

View File

@ -249,6 +249,9 @@ public:
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
void ResetAPIState() override;
void RestoreAPIState() override;
void PushDebugGroup(const char* fmt, ...) override;
void PopDebugGroup() override;
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;

View File

@ -369,6 +369,7 @@ void GSDumpReplayerCpuClear(u32 addr, u32 size)
void GSDumpReplayer::RenderUI()
{
#ifndef __LIBRETRO__
const float scale = ImGuiManager::GetGlobalScale();
const float shadow_offset = std::ceil(1.0f * scale);
const float margin = std::ceil(10.0f * scale);
@ -398,4 +399,5 @@ void GSDumpReplayer::RenderUI()
DRAW_LINE(font, text.c_str(), IM_COL32(255, 255, 255, 255));
#undef DRAW_LINE
#endif
}

View File

@ -262,6 +262,7 @@ struct Gif_Path
// Waits on the MTGS to process gs packets
void mtgsReadWait()
{
#ifndef __LIBRETRO__
if (IsDevBuild)
{
DevCon.WriteLn(Color_Red, "Gif Path[%d] - MTGS Wait! [r=0x%x]", idx + 1, getReadAmount());
@ -269,6 +270,7 @@ struct Gif_Path
DevCon.WriteLn(Color_Green, "Gif Path[%d] - MTGS Wait! [r=0x%x]", idx + 1, getReadAmount());
return;
}
#endif
Gif_MTGS_Wait(isMTVU());
}

View File

@ -50,7 +50,6 @@ using namespace Threading;
alignas(32) MTGS_BufferedData RingBuffer;
#ifdef RINGBUF_DEBUG_STACK
#include <list>
std::list<uint> ringposStack;
@ -81,18 +80,30 @@ SysMtgsThread::~SysMtgsThread()
void SysMtgsThread::StartThread()
{
#ifdef __LIBRETRO__
if (m_thread != std::thread::id())
#else
if (m_thread.joinable())
#endif
return;
pxAssertRel(!m_open_flag.load(), "GS thread should not be opened when starting");
m_sem_event.Reset();
m_shutdown_flag.store(false, std::memory_order_release);
#ifdef __LIBRETRO__
GSinit();
#else
m_thread = std::thread(&SysMtgsThread::ThreadEntryPoint, this);
#endif
}
void SysMtgsThread::ShutdownThread()
{
#ifdef __LIBRETRO__
if (m_thread == std::thread::id())
#else
if (!m_thread.joinable())
#endif
return;
// just go straight to shutdown, don't wait-for-open again
@ -102,7 +113,11 @@ void SysMtgsThread::ShutdownThread()
// make sure the thread actually exits
m_sem_event.NotifyOfWork();
#ifdef __LIBRETRO__
m_thread = {};
#else
m_thread.join();
#endif
}
void SysMtgsThread::ThreadEntryPoint()
@ -146,10 +161,10 @@ void SysMtgsThread::ThreadEntryPoint()
// wait until we're asked to try again...
continue;
}
#ifndef __LIBRETRO__
// we're ready to go
MainLoop();
#endif
// when we come back here, it's because we closed (or shutdown)
// that means the emu thread should be blocked, waiting for us to be done
pxAssertRel(!m_open_flag.load(std::memory_order_relaxed), "Open flag is clear on close");
@ -270,16 +285,32 @@ union PacketTagType
bool SysMtgsThread::TryOpenGS()
{
#ifdef __LIBRETRO__
if(IsOpen())
return true;
m_thread = std::this_thread::get_id();
#endif
std::memcpy(RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS));
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
return false;
GSSetGameCRC(ElfCRC);
#ifdef __LIBRETRO__
m_open_flag.store(true, std::memory_order_release);
// notify emu thread that we finished opening (or failed)
m_open_or_close_done.Post();
#endif
return true;
}
#ifdef __LIBRETRO__
void SysMtgsThread::MainLoop(bool flush_all)
#else
void SysMtgsThread::MainLoop()
#endif
{
// Threading info: run in MTGS thread
// m_ReadPos is only update by the MTGS thread so it is safe to load it with a relaxed atomic
@ -292,6 +323,14 @@ void SysMtgsThread::MainLoop()
while (true)
{
#ifdef __LIBRETRO__
if (flush_all)
{
if(!m_sem_event.CheckForWork())
return;
}
else
#endif
if (m_run_idle_flag.load(std::memory_order_acquire) && VMManager::GetState() != VMState::Running)
{
if (!m_sem_event.CheckForWork())
@ -468,7 +507,10 @@ void SysMtgsThread::MainLoop()
((GSRegSIGBLID&)RingBuffer.Regs[0x1080]) = (GSRegSIGBLID&)remainder[2];
// CSR & 0x2000; is the pageflip id.
GSvsync((((u32&)RingBuffer.Regs[0x1000]) & 0x2000) ? 0 : 1, remainder[4] != 0);
#ifdef __LIBRETRO__
if(!flush_all)
#endif
GSvsync((((u32&)RingBuffer.Regs[0x1000]) & 0x2000) ? 0 : 1, remainder[4] != 0);
m_QueuedFrameCount.fetch_sub(1);
if (m_VsyncSignalListener.exchange(false))
@ -534,13 +576,22 @@ void SysMtgsThread::MainLoop()
uint newringpos = (m_ReadPos.load(std::memory_order_relaxed) + ringposinc) & RingBufferMask;
#ifndef __LIBRETRO__
if (EmuConfig.GS.SynchronousMTGS)
{
pxAssert(m_WritePos == newringpos);
}
#endif
m_ReadPos.store(newringpos, std::memory_order_release);
#ifdef __LIBRETRO__
if(!flush_all && tag.command == GS_RINGTYPE_VSYNC) {
m_sem_event.NotifyOfWork();
return;
}
#endif
if (m_SignalRingEnable.load(std::memory_order_acquire))
{
// The EEcore has requested a signal after some amount of processed data.
@ -577,10 +628,43 @@ void SysMtgsThread::MainLoop()
m_ReadPos.store(m_WritePos.load(std::memory_order_acquire), std::memory_order_relaxed);
m_sem_event.Kill();
}
#ifdef __LIBRETRO__
void SysMtgsThread::StepFrame()
{
pxAssert(std::this_thread::get_id() == m_thread);
MainLoop(false);
}
void SysMtgsThread::Flush()
{
pxAssert(std::this_thread::get_id() == m_thread);
MainLoop(true);
}
void SysMtgsThread::SignalVsync()
{
if (m_VsyncSignalListener.exchange(false))
m_sem_Vsync.Post();
}
#endif
void SysMtgsThread::CloseGS()
{
#ifdef __LIBRETRO__
if( m_SignalRingEnable.exchange(false) )
{
//Console.Warning( "(MTGS Thread) Dangling RingSignal on empty buffer! signalpos=0x%06x", m_SignalRingPosition.exchange(0) ) );
m_SignalRingPosition.store(0, std::memory_order_release);
m_sem_OnRingReset.Post();
}
if (m_VsyncSignalListener.exchange(false))
m_sem_Vsync.Post();
#endif
GSclose();
#ifdef __LIBRETRO__
m_open_flag.store(false, std::memory_order_release);
m_open_or_close_done.Post();
#endif
}
// Waits for the GS to empty out the entire ring buffer contents.
@ -589,7 +673,15 @@ void SysMtgsThread::CloseGS()
// If isMTVU, then this implies this function is being called from the MTVU thread...
void SysMtgsThread::WaitGS(bool syncRegs, bool weakWait, bool isMTVU)
{
#ifdef __LIBRETRO__
if(std::this_thread::get_id() == m_thread)
{
GetMTGS().Flush();
return;
}
#else
pxAssertDev(std::this_thread::get_id() != m_thread.get_id(), "This method is only allowed from threads *not* named MTGS.");
#endif
if (!pxAssertDev(IsOpen(), "MTGS Warning! WaitGS issued on a closed thread."))
return;
@ -861,9 +953,10 @@ bool SysMtgsThread::WaitForOpen()
return true;
StartThread();
#ifndef __LIBRETRO__
// request open, and kick the thread.
m_open_flag.store(true, std::memory_order_release);
#endif
m_sem_event.NotifyOfWork();
// wait for it to finish its stuff
@ -879,23 +972,27 @@ bool SysMtgsThread::WaitForOpen()
void SysMtgsThread::WaitForClose()
{
#ifndef __LIBRETRO__
if (!IsOpen())
return;
// ask the thread to stop processing work, by clearing the open flag
m_open_flag.store(false, std::memory_order_release);
#endif
// and kick the thread if it's sleeping
m_sem_event.NotifyOfWork();
// and wait for it to finish up..
m_open_or_close_done.Wait();
m_thread = {};
}
void SysMtgsThread::Freeze(FreezeAction mode, MTGS_FreezeData& data)
{
pxAssertRel(IsOpen(), "GS thread is open");
#ifndef __LIBRETRO__
pxAssertDev(std::this_thread::get_id() != m_thread.get_id(), "This method is only allowed from threads *not* named MTGS.");
#endif
SendPointerPacket(GS_RINGTYPE_FREEZE, (int)mode, &data);
WaitGS();
}

View File

@ -90,7 +90,9 @@ void SaveStateBase::mtvuFreeze()
VU_Thread::VU_Thread()
{
#ifndef __LIBRETRO__ /* can't call thread functions/locks from dlload in windows */
Reset();
#endif
}
VU_Thread::~VU_Thread()

View File

@ -252,6 +252,7 @@ const char* PAD::GetDefaultPadType(u32 pad)
void PAD::SetDefaultControllerConfig(SettingsInterface& si)
{
#ifndef __LIBRETRO__
si.ClearSection("InputSources");
si.ClearSection("Hotkeys");
si.ClearSection("Pad");
@ -315,6 +316,7 @@ void PAD::SetDefaultControllerConfig(SettingsInterface& si)
// PCSX2 Controller Settings - Controller 1 / Controller 2 / ...
// Use the automapper to set this up.
MapController(si, 0, InputManager::GetGenericBindingMapping("Keyboard"));
#endif
}
void PAD::SetDefaultHotkeyConfig(SettingsInterface& si)
@ -528,6 +530,7 @@ void PAD::ClearPortBindings(SettingsInterface& si, u32 port)
void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si,
bool copy_pad_config, bool copy_pad_bindings, bool copy_hotkey_bindings)
{
#ifndef __LIBRETRO__
if (copy_pad_config)
{
dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort1");
@ -614,6 +617,7 @@ void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface&
for (const HotkeyInfo* hki : hotkeys)
dest_si->CopyStringListValue(src_si, "Hotkeys", hki->name);
}
#endif
}
static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& section,

View File

@ -135,10 +135,12 @@ void Pad::rumble(unsigned port)
currentVibrate[0] = nextVibrate[0];
currentVibrate[1] = nextVibrate[1];
#ifndef __LIBRETRO__
InputManager::SetPadVibrationIntensity(port,
std::min(static_cast<float>(currentVibrate[0]) * g_key_status.GetVibrationScale(port, 0) * (1.0f / 255.0f), 1.0f),
std::min(static_cast<float>(currentVibrate[1]) * g_key_status.GetVibrationScale(port, 1) * (1.0f / 255.0f), 1.0f)
);
#endif
}
void Pad::stop_vibrate_all()

View File

@ -406,22 +406,26 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(char* buf, char* ret_buffer, u32
}
case MsgSaveState:
{
#ifndef __LIBRETRO__
if (!m_vm->HasActiveMachine())
goto error;
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size))
goto error;
StateCopy_SaveToSlot(FromArray<u8>(&buf[buf_cnt], 0));
buf_cnt += 1;
#endif
break;
}
}
case MsgLoadState:
{
#ifndef __LIBRETRO__
if (!m_vm->HasActiveMachine())
goto error;
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size))
goto error;
StateCopy_LoadFromSlot(FromArray<u8>(&buf[buf_cnt], 0), false);
buf_cnt += 1;
#endif
break;
}
case MsgTitle:

View File

@ -1189,6 +1189,7 @@ void Pcsx2Config::FramerateOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapEntry(SlomoScalar);
}
#ifndef __LIBRETRO__
Pcsx2Config::USBOptions::USBOptions()
{
for (u32 i = 0; i < static_cast<u32>(Ports.size()); i++)
@ -1244,6 +1245,8 @@ bool Pcsx2Config::USBOptions::operator!=(const USBOptions& right) const
return !this->operator==(right);
}
#endif
#ifdef ENABLE_ACHIEVEMENTS
Pcsx2Config::AchievementsOptions::AchievementsOptions()
@ -1352,7 +1355,9 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap)
Debugger.LoadSave(wrap);
Trace.LoadSave(wrap);
#ifndef __LIBRETRO__
USB.LoadSave(wrap);
#endif
#ifdef ENABLE_ACHIEVEMENTS
Achievements.LoadSave(wrap);

View File

@ -632,7 +632,7 @@ __forceinline
/*(PlayMode&4) ? StereoOut32::Empty : */ ApplyVolume(Cores[0].ReadInput(), Cores[0].InpVol),
// CDDA is on Core 1:
(PlayMode & 8) ? StereoOut32::Empty : ApplyVolume(Cores[1].ReadInput(), Cores[1].InpVol)};
(PlayMode & 8) ? StereoOut32(0, 0) : ApplyVolume(Cores[1].ReadInput(), Cores[1].InpVol)};
#ifdef PCSX2_DEVBUILD
WaveDump::WriteCore(0, CoreSrc_Input, InputData[0]);
@ -644,10 +644,10 @@ __forceinline
MixCoreVoices(VoiceData[0], 0);
MixCoreVoices(VoiceData[1], 1);
StereoOut32 Ext(Cores[0].Mix(VoiceData[0], InputData[0], StereoOut32::Empty));
StereoOut32 Ext(Cores[0].Mix(VoiceData[0], InputData[0], StereoOut32(0, 0)));
if ((PlayMode & 4) || (Cores[0].Mute != 0))
Ext = StereoOut32::Empty;
Ext = StereoOut32(0, 0);
else
{
Ext = clamp_mix(ApplyVolume(Ext, Cores[0].MasterVol));

View File

@ -149,7 +149,7 @@ StereoOut32 V_Core::DoReverb(const StereoOut32& Input)
{
if (EffectsBufferSize <= 0)
{
return StereoOut32::Empty;
return StereoOut32(0, 0);
}
RevbDownBuf[0][RevbSampleBufPos & 63] = Input.Left;

View File

@ -404,10 +404,12 @@ __forceinline void TimeUpdate(u32 cClocks)
lClocks = cClocks - dClocks;
}
#ifndef __LIBRETRO__
if (EmuConfig.SPU2.SynchMode == Pcsx2Config::SPU2Options::SynchronizationMode::ASync)
SndBuffer::UpdateTempoChangeAsyncMixing();
else
TickInterval = 768; // Reset to default, in case the user hotswitched from async to something else.
#endif
//Update Mixing Progress
while (dClocks >= TickInterval)

View File

@ -22,6 +22,7 @@
#include <string>
#include <type_traits>
#include <vector>
#include <array>
class String;

View File

@ -152,6 +152,7 @@ namespace HostMemoryMap
/// Attempts to find a spot near static variables for the main memory
static VirtualMemoryManagerPtr makeMemoryManager(const char* name, const char* file_mapping_name, size_t size, size_t offset_from_base)
{
#if !defined(__LIBRETRO__) || defined(_WIN32)
// Everything looks nicer when the start of all the sections is a nice round looking number.
// Also reduces the variation in the address due to small changes in code.
// Breaks ASLR but so does anything else that tries to make addresses constant for our debugging pleasure
@ -169,7 +170,11 @@ static VirtualMemoryManagerPtr makeMemoryManager(const char* name, const char* f
// VTLB will throw a fit if we try to put EE main memory here
continue;
}
#if defined(__LIBRETRO__) && defined(_DEBUG) && !defined(_WIN32)
auto mgr = std::make_shared<VirtualMemoryManager>(name, file_mapping_name, base, size, /*upper_bounds=*/0, /*strict=*/false);
#else
auto mgr = std::make_shared<VirtualMemoryManager>(name, file_mapping_name, base, size, /*upper_bounds=*/0, /*strict=*/true);
#endif
if (mgr->IsOk())
{
return mgr;
@ -182,6 +187,7 @@ static VirtualMemoryManagerPtr makeMemoryManager(const char* name, const char* f
{
pxAssertRel(0, "Failed to find a good place for the memory allocation, recompilers may fail");
}
#endif
return std::make_shared<VirtualMemoryManager>(name, file_mapping_name, 0, size);
}

View File

@ -193,9 +193,11 @@ void VMManager::SetState(VMState state)
const bool paused = (state == VMState::Paused);
if (paused)
{
#ifndef __LIBRETRO__
if (THREAD_VU1)
vu1Thread.WaitVU();
GetMTGS().WaitGS(false);
#endif
}
else
{
@ -251,7 +253,7 @@ bool VMManager::Internal::InitializeGlobals()
// On Win32, we have a bunch of things which use COM (e.g. SDL, XAudio2, etc).
// We need to initialize COM first, before anything else does, because otherwise they might
// initialize it in single-threaded/apartment mode, which can't be changed to multithreaded.
#ifdef _WIN32
#if defined(_WIN32) && !defined(__LIBRETRO__)
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr))
{
@ -293,7 +295,7 @@ void VMManager::Internal::ReleaseGlobals()
SPU2::Shutdown();
GSshutdown();
#ifdef _WIN32
#if defined(_WIN32) && !defined(__LIBRETRO__)
CoUninitialize();
#endif
}
@ -1649,7 +1651,7 @@ void VMManager::Internal::VSyncOnCPUThread()
}
Host::CPUThreadVSync();
#ifndef __LIBRETRO__
if (EmuConfig.EnableRecordingTools)
{
// This code is called _before_ Counter's vsync end, and _after_ vsync start
@ -1666,6 +1668,7 @@ void VMManager::Internal::VSyncOnCPUThread()
// so we can either read from it, or overwrite it!
g_InputRecording.handleControllerDataUpdate();
}
#endif
}
void VMManager::CheckForCPUConfigChanges(const Pcsx2Config& old_config)

View File

@ -286,6 +286,7 @@ void VirtualMemoryReserve::Release()
return;
m_allocator->Free(m_baseptr, m_size);
m_allocator.reset();
m_baseptr = nullptr;
m_size = 0;
}