mirror of
https://github.com/libretro/pcsx2.git
synced 2024-11-23 09:19:42 +00:00
Initial libretro rebase
This commit is contained in:
parent
0367851b8e
commit
5112943bbe
@ -61,7 +61,7 @@ KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 3
|
||||
NamespaceIndentation: All
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -99,3 +99,14 @@ oprofile_data/
|
||||
CMakeSettings.json
|
||||
/ci-artifacts/
|
||||
/out/
|
||||
|
||||
bin/*
|
||||
build*/
|
||||
*.cflags
|
||||
*.config
|
||||
*.creator
|
||||
*.creator.user.*
|
||||
*.cxxflags
|
||||
*.files
|
||||
*.includes
|
||||
*.autosave
|
||||
|
2
3rdparty/soundtouch/soundtouch/STTypes.h
vendored
2
3rdparty/soundtouch/soundtouch/STTypes.h
vendored
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
93
common/GL/ContextRetroGL.cpp
Normal file
93
common/GL/ContextRetroGL.cpp
Normal 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
|
40
common/GL/ContextRetroGL.h
Normal file
40
common/GL/ContextRetroGL.h
Normal 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
|
@ -13,6 +13,7 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include "General.h"
|
||||
#include "Console.h"
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include "common/Pcsx2Types.h"
|
||||
|
@ -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"
|
||||
|
@ -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
70
libretro/CMakeLists.txt
Normal 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
81
libretro/Console.cpp
Normal 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
189
libretro/DEV9.cpp
Normal 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
103
libretro/InputRecording.cpp
Normal 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
33
libretro/SPU2.cpp
Normal 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
31
libretro/USB.cpp
Normal 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
256
libretro/build.cmake
Normal 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
253
libretro/input.cpp
Normal 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
9
libretro/input.h
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Input
|
||||
{
|
||||
void Init();
|
||||
void Update();
|
||||
void Shutdown();
|
||||
}
|
3972
libretro/libretro.h
Normal file
3972
libretro/libretro.h
Normal file
File diff suppressed because it is too large
Load Diff
80
libretro/libretro_d3d.h
Normal file
80
libretro/libretro_d3d.h
Normal 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__ */
|
151
libretro/libretro_vulkan.cpp
Normal file
151
libretro/libretro_vulkan.cpp
Normal 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
403
libretro/libretro_vulkan.h
Normal 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
1235
libretro/main.cpp
Normal file
File diff suppressed because it is too large
Load Diff
44
libretro/options.cpp
Normal file
44
libretro/options.cpp
Normal 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
203
libretro/options.h
Normal 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
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
13
pcsx2/GS.h
13
pcsx2/GS.h
@ -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();
|
||||
|
||||
|
@ -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 ||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include "common/AlignedMalloc.h"
|
||||
|
||||
template <class T>
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
215
pcsx2/GS/Renderers/Null/GSDeviceNull.cpp
Normal file
215
pcsx2/GS/Renderers/Null/GSDeviceNull.cpp
Normal 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()
|
||||
{
|
||||
|
||||
}
|
92
pcsx2/GS/Renderers/Null/GSDeviceNull.h
Normal file
92
pcsx2/GS/Renderers/Null/GSDeviceNull.h
Normal 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;
|
||||
};
|
@ -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, ¤t_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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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.");
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
111
pcsx2/MTGS.cpp
111
pcsx2/MTGS.cpp
@ -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();
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
class String;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -286,6 +286,7 @@ void VirtualMemoryReserve::Release()
|
||||
return;
|
||||
|
||||
m_allocator->Free(m_baseptr, m_size);
|
||||
m_allocator.reset();
|
||||
m_baseptr = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user