mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-22 21:09:52 +00:00
Libretro port.
This commit is contained in:
parent
686717eb71
commit
c4f7790618
@ -106,6 +106,8 @@ travis_script() {
|
||||
|
||||
if [ "$QT" = "TRUE" ]; then
|
||||
./b.sh --qt
|
||||
elif [ "$LIBRETRO" = "TRUE" ]; then
|
||||
./b.sh --libretro
|
||||
else
|
||||
./b.sh --headless
|
||||
fi
|
||||
|
@ -64,6 +64,14 @@ matrix:
|
||||
compiler: "gcc"
|
||||
env: PPSSPP_BUILD_TYPE=Linux
|
||||
QT=TRUE
|
||||
- os: linux
|
||||
compiler: "gcc"
|
||||
env: PPSSPP_BUILD_TYPE=Linux
|
||||
LIBRETRO=TRUE
|
||||
- os: linux
|
||||
compiler: "clang"
|
||||
env: PPSSPP_BUILD_TYPE=Linux
|
||||
LIBRETRO=TRUE
|
||||
- os: osx
|
||||
osx_image: xcode8
|
||||
compiler: "clang macos"
|
||||
|
@ -115,6 +115,7 @@ option(MOBILE_DEVICE "Set to ON when targeting a mobile device" ${MOBILE_DEVICE}
|
||||
option(HEADLESS "Set to OFF to not generate the PPSSPPHeadless target" ${HEADLESS})
|
||||
option(UNITTEST "Set to ON to generate the unittest target" ${UNITTEST})
|
||||
option(SIMULATOR "Set to ON when targeting an x86 simulator of an ARM platform" ${SIMULATOR})
|
||||
option(LIBRETRO "Set to ON to generate the libretro target" OFF)
|
||||
# :: Options
|
||||
option(USE_FFMPEG "Build with FFMPEG support" ${USE_FFMPEG})
|
||||
option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_FFMPEG})
|
||||
@ -135,6 +136,14 @@ if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LIBRETRO)
|
||||
add_definitions(-D__LIBRETRO__)
|
||||
add_definitions(-DGLEW_NO_GLU)
|
||||
if(NOT MSVC)
|
||||
add_compile_options(-fPIC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
set(CoreLibName ppsspp_jni)
|
||||
set(CoreLinkType SHARED)
|
||||
@ -1884,6 +1893,10 @@ if(UNITTEST)
|
||||
setup_target_project(unitTest unittest)
|
||||
endif()
|
||||
|
||||
if(LIBRETRO)
|
||||
add_subdirectory(libretro)
|
||||
endif()
|
||||
|
||||
if (TargetBin)
|
||||
if (IOS OR APPLE)
|
||||
if (APPLE AND NOT IOS)
|
||||
|
3
b.sh
3
b.sh
@ -37,6 +37,9 @@ do
|
||||
--headless) echo "Headless mode enabled"
|
||||
CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
|
||||
;;
|
||||
--libretro) echo "Build Libretro core"
|
||||
CMAKE_ARGS="-DLIBRETRO=ON ${CMAKE_ARGS}"
|
||||
;;
|
||||
--unittest) echo "Build unittest"
|
||||
CMAKE_ARGS="-DUNITTEST=ON ${CMAKE_ARGS}"
|
||||
;;
|
||||
|
4
libretro/.gitignore
vendored
Normal file
4
libretro/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.bc
|
||||
*.so
|
||||
*.dll
|
||||
*.dylib
|
21
libretro/CMakeLists.txt
Normal file
21
libretro/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
set(LIBRETRO_SRCS
|
||||
libretro.cpp
|
||||
LibretroGraphicsContext.cpp
|
||||
LibretroGLContext.cpp
|
||||
LibretroVulkanContext.cpp
|
||||
libretro_vulkan.cpp
|
||||
)
|
||||
|
||||
include_directories(libretro)
|
||||
|
||||
add_library(ppsspp_libretro SHARED ${LIBRETRO_SRCS})
|
||||
set(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}")
|
||||
set_target_properties(ppsspp_libretro PROPERTIES PREFIX "")
|
||||
if(ANDROID)
|
||||
set_target_properties(ppsspp_libretro PROPERTIES SUFFIX "_android.so")
|
||||
endif()
|
||||
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(ppsspp_libretro "-Wl,-Bsymbolic")
|
||||
endif()
|
||||
target_link_libraries(ppsspp_libretro ${LinkCommon})
|
51
libretro/LibretroGLContext.cpp
Normal file
51
libretro/LibretroGLContext.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/System.h"
|
||||
#include "gfx_es2/gpu_features.h"
|
||||
|
||||
#include "libretro/LibretroGLContext.h"
|
||||
|
||||
bool LibretroGLContext::Init()
|
||||
{
|
||||
if (!LibretroHWRenderContext::Init())
|
||||
return false;
|
||||
|
||||
libretro_get_proc_address = hw_render_.get_proc_address;
|
||||
|
||||
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibretroGLContext::Shutdown()
|
||||
{
|
||||
LibretroGraphicsContext::Shutdown();
|
||||
libretro_get_proc_address = nullptr;
|
||||
#if 0
|
||||
NativeShutdownGraphics();
|
||||
finalize_glslang();
|
||||
#endif
|
||||
}
|
||||
|
||||
void LibretroGLContext::CreateDrawContext()
|
||||
{
|
||||
if (!glewInitDone)
|
||||
{
|
||||
#if !defined(IOS) && !defined(USING_GLES2)
|
||||
if (glewInit() != GLEW_OK)
|
||||
{
|
||||
ERROR_LOG(G3D, "glewInit() failed.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
glewInitDone = true;
|
||||
CheckGLExtensions();
|
||||
}
|
||||
draw_ = Draw::T3DCreateGLContext();
|
||||
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
}
|
||||
void LibretroGLContext::DestroyDrawContext()
|
||||
{
|
||||
LibretroHWRenderContext::DestroyDrawContext();
|
||||
renderManager_ = nullptr;
|
||||
}
|
47
libretro/LibretroGLContext.h
Normal file
47
libretro/LibretroGLContext.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "gfx/gl_common.h"
|
||||
#include "libretro/LibretroGraphicsContext.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
|
||||
class LibretroGLContext : public LibretroHWRenderContext {
|
||||
public:
|
||||
LibretroGLContext()
|
||||
:
|
||||
#ifdef USING_GLES2
|
||||
HWRenderContext(RETRO_HW_CONTEXT_OPENGLES2)
|
||||
#elif defined(HAVE_OPENGL_CORE)
|
||||
HWRenderContext(RETRO_HW_CONTEXT_OPENGL_CORE, 3, 1)
|
||||
#else
|
||||
LibretroHWRenderContext(RETRO_HW_CONTEXT_OPENGL)
|
||||
#endif
|
||||
{
|
||||
hw_render_.bottom_left_origin = true;
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
void Shutdown() override;
|
||||
void CreateDrawContext() override;
|
||||
void DestroyDrawContext() override;
|
||||
void SetRenderTarget() override
|
||||
{
|
||||
extern GLuint g_defaultFBO;
|
||||
g_defaultFBO = hw_render_.get_current_framebuffer();
|
||||
}
|
||||
|
||||
void ThreadStart() override { renderManager_->ThreadStart(); }
|
||||
bool ThreadFrame() override { return renderManager_->ThreadFrame(); }
|
||||
void ThreadEnd() override { renderManager_->ThreadEnd(); }
|
||||
void StopThread() override
|
||||
{
|
||||
renderManager_->WaitUntilQueueIdle();
|
||||
renderManager_->StopThread();
|
||||
}
|
||||
|
||||
GPUCore GetGPUCore() override { return GPUCORE_GLES; }
|
||||
const char *Ident() override { return "OpenGL"; }
|
||||
|
||||
private:
|
||||
GLRenderManager *renderManager_ = nullptr;
|
||||
bool glewInitDone = false;
|
||||
};
|
116
libretro/LibretroGraphicsContext.cpp
Normal file
116
libretro/LibretroGraphicsContext.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
|
||||
#include "libretro/libretro.h"
|
||||
#include "libretro/LibretroGraphicsContext.h"
|
||||
#include "libretro/LibretroGLContext.h"
|
||||
#ifndef NO_VULKAN
|
||||
#include "libretro/LibretroVulkanContext.h"
|
||||
#endif
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
|
||||
retro_video_refresh_t LibretroGraphicsContext::video_cb;
|
||||
retro_hw_get_proc_address_t libretro_get_proc_address;
|
||||
|
||||
void retro_set_video_refresh(retro_video_refresh_t cb) { LibretroGraphicsContext::video_cb = cb; }
|
||||
static void context_reset() { ((LibretroHWRenderContext *)Libretro::ctx)->ContextReset(); }
|
||||
static void context_destroy() { ((LibretroHWRenderContext *)Libretro::ctx)->ContextDestroy(); }
|
||||
|
||||
bool LibretroHWRenderContext::Init() { return Libretro::environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render_); }
|
||||
|
||||
LibretroHWRenderContext::LibretroHWRenderContext(retro_hw_context_type context_type, unsigned version_major, unsigned version_minor)
|
||||
{
|
||||
hw_render_.context_type = context_type;
|
||||
hw_render_.version_major = version_major;
|
||||
hw_render_.version_minor = version_minor;
|
||||
hw_render_.context_reset = context_reset;
|
||||
hw_render_.context_destroy = context_destroy;
|
||||
hw_render_.depth = true;
|
||||
}
|
||||
|
||||
void LibretroHWRenderContext::ContextReset()
|
||||
{
|
||||
INFO_LOG(G3D, "Context reset");
|
||||
|
||||
// needed to restart the thread
|
||||
// TODO: find a way to move this to ContextDestroy.
|
||||
if (Libretro::useEmuThread && draw_ && Libretro::emuThreadState != Libretro::EmuThreadState::PAUSED)
|
||||
DestroyDrawContext();
|
||||
|
||||
if (!draw_)
|
||||
{
|
||||
CreateDrawContext();
|
||||
PSP_CoreParameter().thin3d = draw_;
|
||||
draw_->CreatePresets();
|
||||
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
|
||||
}
|
||||
|
||||
if (gpu)
|
||||
gpu->DeviceRestore();
|
||||
}
|
||||
|
||||
void LibretroHWRenderContext::ContextDestroy()
|
||||
{
|
||||
INFO_LOG(G3D, "Context destroy");
|
||||
|
||||
if (Libretro::useEmuThread)
|
||||
{
|
||||
#if 0
|
||||
Libretro::EmuThreadPause();
|
||||
#else
|
||||
Libretro::EmuThreadStop();
|
||||
#endif
|
||||
}
|
||||
|
||||
gpu->DeviceLost();
|
||||
}
|
||||
|
||||
LibretroGraphicsContext *LibretroGraphicsContext::CreateGraphicsContext()
|
||||
{
|
||||
LibretroGraphicsContext *ctx;
|
||||
|
||||
ctx = new LibretroGLContext();
|
||||
|
||||
if (ctx->Init())
|
||||
return ctx;
|
||||
|
||||
delete ctx;
|
||||
|
||||
#ifndef NO_VULKAN
|
||||
ctx = new LibretroVulkanContext();
|
||||
|
||||
if (ctx->Init())
|
||||
return ctx;
|
||||
|
||||
delete ctx;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
ctx = new LibretroD3D11Context();
|
||||
|
||||
if (ctx->Init())
|
||||
return ctx;
|
||||
|
||||
delete ctx;
|
||||
|
||||
ctx = new LibretroD3D9Context();
|
||||
|
||||
if (ctx->Init())
|
||||
return ctx;
|
||||
|
||||
delete ctx;
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
ctx = new LibretroSoftwareContext();
|
||||
|
||||
if (ctx->Init())
|
||||
return ctx;
|
||||
|
||||
delete ctx;
|
||||
#endif
|
||||
|
||||
return new LibretroNullContext();
|
||||
}
|
139
libretro/LibretroGraphicsContext.h
Normal file
139
libretro/LibretroGraphicsContext.h
Normal file
@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
|
||||
#include "libretro/libretro.h"
|
||||
#include "Common/GraphicsContext.h"
|
||||
#include "thin3d/thin3d_create.h"
|
||||
|
||||
#include "Core/System.h"
|
||||
#include "GPU/GPUState.h"
|
||||
|
||||
class LibretroGraphicsContext : public GraphicsContext {
|
||||
public:
|
||||
LibretroGraphicsContext() {}
|
||||
~LibretroGraphicsContext() override {}
|
||||
|
||||
virtual bool Init() = 0;
|
||||
virtual void SetRenderTarget() {}
|
||||
virtual GPUCore GetGPUCore() = 0;
|
||||
virtual const char *Ident() = 0;
|
||||
|
||||
void Shutdown() override
|
||||
{
|
||||
DestroyDrawContext();
|
||||
PSP_CoreParameter().thin3d = nullptr;
|
||||
}
|
||||
void SwapInterval(int interval) override {}
|
||||
void Resize() override {}
|
||||
|
||||
virtual void CreateDrawContext() {}
|
||||
virtual void DestroyDrawContext()
|
||||
{
|
||||
if (!draw_)
|
||||
return;
|
||||
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, -1, -1);
|
||||
delete draw_;
|
||||
draw_ = nullptr;
|
||||
}
|
||||
Draw::DrawContext *GetDrawContext() override { return draw_; }
|
||||
|
||||
static LibretroGraphicsContext *CreateGraphicsContext();
|
||||
|
||||
static retro_video_refresh_t video_cb;
|
||||
|
||||
protected:
|
||||
Draw::DrawContext *draw_ = nullptr;
|
||||
};
|
||||
|
||||
class LibretroHWRenderContext : public LibretroGraphicsContext {
|
||||
public:
|
||||
LibretroHWRenderContext(retro_hw_context_type context_type, unsigned version_major = 0, unsigned version_minor = 0);
|
||||
bool Init() override;
|
||||
void SetRenderTarget() override {}
|
||||
void SwapBuffers() override
|
||||
{
|
||||
if (gstate_c.skipDrawReason)
|
||||
video_cb(NULL, 0, 0, 0);
|
||||
else
|
||||
video_cb(RETRO_HW_FRAME_BUFFER_VALID, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight, 0);
|
||||
}
|
||||
virtual void ContextReset();
|
||||
virtual void ContextDestroy();
|
||||
|
||||
protected:
|
||||
retro_hw_render_callback hw_render_ = {};
|
||||
};
|
||||
|
||||
extern "C" retro_hw_get_proc_address_t libretro_get_proc_address;
|
||||
|
||||
#ifdef _WIN32
|
||||
class LibretroD3D9Context : public LibretroHWRenderContext {
|
||||
public:
|
||||
LibretroD3D9Context() : LibretroHWRenderContext(RETRO_HW_CONTEXT_DIRECT3D, 9) {}
|
||||
bool Init() override { return false; }
|
||||
#if 0
|
||||
void InitDrawContext() override
|
||||
{
|
||||
draw_ = Draw::T3DCreateDX9Context();
|
||||
draw_->CreatePresets();
|
||||
}
|
||||
#endif
|
||||
GPUCore GetGPUCore() override { return GPUCORE_DIRECTX9; }
|
||||
const char *Ident() override { return "DirectX 9"; }
|
||||
};
|
||||
|
||||
class LibretroD3D11Context : public LibretroHWRenderContext {
|
||||
public:
|
||||
LibretroD3D11Context() : LibretroHWRenderContext(RETRO_HW_CONTEXT_DIRECT3D, 11) {}
|
||||
bool Init() override { return false; }
|
||||
#if 0
|
||||
void InitDrawContext() override
|
||||
{
|
||||
draw_ = Draw::T3DCreateD3D11Context();
|
||||
draw_->CreatePresets();
|
||||
}
|
||||
#endif
|
||||
GPUCore GetGPUCore() override { return GPUCORE_DIRECTX11; }
|
||||
const char *Ident() override { return "DirectX 11"; }
|
||||
};
|
||||
#endif
|
||||
|
||||
class LibretroSoftwareContext : public LibretroGraphicsContext {
|
||||
public:
|
||||
LibretroSoftwareContext() {}
|
||||
bool Init() override { return true; }
|
||||
void SwapBuffers() override { video_cb(NULL, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight, 0); }
|
||||
GPUCore GetGPUCore() override { return GPUCORE_SOFTWARE; }
|
||||
const char *Ident() override { return "Software"; }
|
||||
};
|
||||
|
||||
class LibretroNullContext : public LibretroGraphicsContext {
|
||||
public:
|
||||
LibretroNullContext() {}
|
||||
|
||||
bool Init() override { return true; }
|
||||
void SwapBuffers() override { video_cb(NULL, 0, 0, 0); }
|
||||
GPUCore GetGPUCore() override { return GPUCORE_NULL; }
|
||||
const char *Ident() override { return "NULL"; }
|
||||
};
|
||||
|
||||
namespace Libretro {
|
||||
extern LibretroGraphicsContext *ctx;
|
||||
extern retro_environment_t environ_cb;
|
||||
|
||||
enum class EmuThreadState
|
||||
{
|
||||
DISABLED,
|
||||
START_REQUESTED,
|
||||
RUNNING,
|
||||
PAUSE_REQUESTED,
|
||||
PAUSED,
|
||||
QUIT_REQUESTED,
|
||||
STOPPED,
|
||||
};
|
||||
extern bool useEmuThread;
|
||||
extern std::atomic<EmuThreadState> emuThreadState;
|
||||
void EmuThreadStart();
|
||||
void EmuThreadStop();
|
||||
void EmuThreadPause();
|
||||
} // namespace Libretro
|
160
libretro/LibretroVulkanContext.cpp
Normal file
160
libretro/LibretroVulkanContext.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
|
||||
|
||||
#include "Common/Vulkan/VulkanLoader.h"
|
||||
#include "Common/Vulkan/VulkanContext.h"
|
||||
#include "Common/Vulkan/VulkanDebug.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "util/text/parsers.h"
|
||||
|
||||
#include "libretro/LibretroVulkanContext.h"
|
||||
#include "libretro/libretro_vulkan.h"
|
||||
|
||||
static VulkanContext *vk;
|
||||
|
||||
void vk_libretro_init(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);
|
||||
void vk_libretro_shutdown();
|
||||
void vk_libretro_set_hwrender_interface(retro_hw_render_interface *hw_render_interface);
|
||||
void vk_libretro_wait_for_presentation();
|
||||
|
||||
void LibretroVulkanContext::SwapBuffers()
|
||||
{
|
||||
vk_libretro_wait_for_presentation();
|
||||
LibretroHWRenderContext::SwapBuffers();
|
||||
}
|
||||
|
||||
static bool create_device(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)
|
||||
{
|
||||
init_glslang();
|
||||
|
||||
vk = new VulkanContext;
|
||||
if (!vk->InitError().empty())
|
||||
{
|
||||
ERROR_LOG(G3D, "%s", vk->InitError().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
vk_libretro_init(instance, gpu, surface, get_instance_proc_addr, required_device_extensions, num_required_device_extensions, required_device_layers, num_required_device_layers, required_features);
|
||||
|
||||
vk->CreateInstance({});
|
||||
|
||||
int physical_device = 0;
|
||||
while (gpu && vk->GetPhysicalDevice(physical_device) != gpu)
|
||||
physical_device++;
|
||||
|
||||
if (!gpu)
|
||||
physical_device = vk->GetBestPhysicalDevice();
|
||||
|
||||
vk->ChooseDevice(physical_device);
|
||||
vk->CreateDevice();
|
||||
#ifdef _WIN32
|
||||
vk->InitSurface(WINDOWSYSTEM_WIN32, nullptr, nullptr);
|
||||
#elif defined(__ANDROID__)
|
||||
vk->InitSurface(WINDOWSYSTEM_ANDROID, nullptr, nullptr);
|
||||
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
vk->InitSurface(WINDOWSYSTEM_XLIB, nullptr, nullptr);
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
vk->InitSurface(WINDOWSYSTEM_XCB, nullptr, nullptr);
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
vk->InitSurface(WINDOWSYSTEM_WAYLAND, nullptr, nullptr);
|
||||
#endif
|
||||
|
||||
if (!vk->InitQueue())
|
||||
return false;
|
||||
|
||||
context->gpu = vk->GetPhysicalDevice(physical_device);
|
||||
context->device = vk->GetDevice();
|
||||
context->queue = vk->GetGraphicsQueue();
|
||||
context->queue_family_index = vk->GetGraphicsQueueFamilyIndex();
|
||||
context->presentation_queue = context->queue;
|
||||
context->presentation_queue_family_index = context->queue_family_index;
|
||||
#ifdef _DEBUG
|
||||
fflush(stdout);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroy_device(void)
|
||||
{
|
||||
if (!vk)
|
||||
return;
|
||||
|
||||
PSP_CoreParameter().graphicsContext->Shutdown();
|
||||
}
|
||||
|
||||
void LibretroVulkanContext::ContextDestroy()
|
||||
{
|
||||
LibretroHWRenderContext::ContextDestroy();
|
||||
|
||||
// temporary workaround, destroy_device is currently being called too late/never
|
||||
destroy_device();
|
||||
}
|
||||
|
||||
static const VkApplicationInfo *GetApplicationInfo(void)
|
||||
{
|
||||
static VkApplicationInfo app_info{ VK_STRUCTURE_TYPE_APPLICATION_INFO };
|
||||
app_info.pApplicationName = "PPSSPP";
|
||||
app_info.applicationVersion = Version(PPSSPP_GIT_VERSION).ToInteger();
|
||||
app_info.pEngineName = "PPSSPP";
|
||||
app_info.engineVersion = 2;
|
||||
app_info.apiVersion = VK_API_VERSION_1_0;
|
||||
return &app_info;
|
||||
}
|
||||
|
||||
bool LibretroVulkanContext::Init()
|
||||
{
|
||||
if (!LibretroHWRenderContext::Init())
|
||||
return false;
|
||||
|
||||
static const struct retro_hw_render_context_negotiation_interface_vulkan iface = { RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN, RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION, GetApplicationInfo, create_device, nullptr };
|
||||
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE, (void *)&iface);
|
||||
|
||||
g_Config.iGPUBackend = (int)GPUBackend::VULKAN;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibretroVulkanContext::Shutdown()
|
||||
{
|
||||
LibretroHWRenderContext::Shutdown();
|
||||
|
||||
vk->WaitUntilQueueIdle();
|
||||
|
||||
vk->DestroyObjects();
|
||||
vk->DestroyDevice();
|
||||
vk->DestroyInstance();
|
||||
delete vk;
|
||||
vk = nullptr;
|
||||
|
||||
#if 0
|
||||
NativeShutdownGraphics();
|
||||
#endif
|
||||
finalize_glslang();
|
||||
vk_libretro_shutdown();
|
||||
}
|
||||
|
||||
void *LibretroVulkanContext::GetAPIContext() { return vk; }
|
||||
|
||||
void LibretroVulkanContext::CreateDrawContext()
|
||||
{
|
||||
retro_hw_render_interface *vulkan;
|
||||
if (!Libretro::environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void **)&vulkan) || !vulkan)
|
||||
{
|
||||
ERROR_LOG(G3D, "Failed to get HW rendering interface!\n");
|
||||
return;
|
||||
}
|
||||
if (vulkan->interface_version != RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION)
|
||||
{
|
||||
ERROR_LOG(G3D, "HW render interface mismatch, expected %u, got %u!\n", RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION, vulkan->interface_version);
|
||||
return;
|
||||
}
|
||||
vk_libretro_set_hwrender_interface(vulkan);
|
||||
|
||||
vk->ReinitSurface(PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
|
||||
|
||||
if (!vk->InitSwapchain())
|
||||
return;
|
||||
|
||||
draw_ = Draw::T3DCreateVulkanContext(vk, false);
|
||||
}
|
24
libretro/LibretroVulkanContext.h
Normal file
24
libretro/LibretroVulkanContext.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/Vulkan/VulkanLoader.h"
|
||||
#include "libretro/LibretroGraphicsContext.h"
|
||||
|
||||
class LibretroVulkanContext : public LibretroHWRenderContext {
|
||||
public:
|
||||
LibretroVulkanContext() : LibretroHWRenderContext(RETRO_HW_CONTEXT_VULKAN, VK_MAKE_VERSION(1, 0, 18))
|
||||
{
|
||||
#if 0
|
||||
hw_render_.cache_context = true;
|
||||
#endif
|
||||
}
|
||||
~LibretroVulkanContext() override {}
|
||||
bool Init() override;
|
||||
void Shutdown() override;
|
||||
void SwapBuffers() override;
|
||||
void ContextDestroy() override;
|
||||
|
||||
void *GetAPIContext() override;
|
||||
void CreateDrawContext() override;
|
||||
GPUCore GetGPUCore() override { return GPUCORE_VULKAN; }
|
||||
const char *Ident() override { return "Vulkan"; }
|
||||
};
|
450
libretro/Makefile
Normal file
450
libretro/Makefile
Normal file
@ -0,0 +1,450 @@
|
||||
DEBUG=0
|
||||
WITH_DYNAREC := 1
|
||||
|
||||
DYNAFLAGS :=
|
||||
INCFLAGS := -I.
|
||||
COREFLAGS :=
|
||||
CPUFLAGS :=
|
||||
|
||||
UNAME=$(shell uname -a)
|
||||
|
||||
# Cross compile ?
|
||||
|
||||
ifeq (,$(TARGET_ARCH))
|
||||
TARGET_ARCH = $(shell uname -m)
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring 64,$(TARGET_ARCH)))
|
||||
override TARGET_ARCH := x86_64
|
||||
else ifneq (,$(findstring 86,$(TARGET_ARCH)))
|
||||
override TARGET_ARCH := x86
|
||||
endif
|
||||
|
||||
ifeq ($(platform),)
|
||||
platform = unix
|
||||
ifeq ($(UNAME),)
|
||||
platform = win
|
||||
else ifneq ($(findstring MINGW,$(UNAME)),)
|
||||
platform = win
|
||||
else ifneq ($(findstring Darwin,$(UNAME)),)
|
||||
platform = osx
|
||||
LDFLAGS += -lm
|
||||
else ifneq ($(findstring win,$(UNAME)),)
|
||||
platform = win
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(platform), unix)
|
||||
LDFLAGS += -lm
|
||||
endif
|
||||
|
||||
# Dirs
|
||||
CORE_DIR = ..
|
||||
FFMPEGDIR = $(CORE_DIR)/ffmpeg
|
||||
LIBRETRODIR = $(CORE_DIR)/libretro
|
||||
COREDIR = $(CORE_DIR)/Core
|
||||
COMMONDIR = $(CORE_DIR)/Common
|
||||
GPUCOMMONDIR = $(CORE_DIR)/GPU/Common
|
||||
GPUDIR = $(CORE_DIR)/GPU
|
||||
NATIVEDIR = $(CORE_DIR)/native
|
||||
EXTDIR = $(CORE_DIR)/ext
|
||||
|
||||
TARGET_NAME := ppsspp
|
||||
|
||||
FFMPEGINCFLAGS :=
|
||||
FFMPEGLIBDIR :=
|
||||
FFMPEGLIBS :=
|
||||
|
||||
# Unix
|
||||
ifneq (,$(findstring unix,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
LDFLAGS += -shared -Wl,--version-script=link.T -Wl,--no-undefined
|
||||
|
||||
FFMPEGINCFLAGS += -I$(FFMPEGDIR)/linux/$(TARGET_ARCH)/include
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/linux/$(TARGET_ARCH)/lib
|
||||
FFMPEGLDFLAGS += -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale
|
||||
fpic = -fPIC
|
||||
ifneq (,$(findstring gles,$(platform)))
|
||||
GLES = 1
|
||||
GL_LIB := -lGLESv2
|
||||
else
|
||||
GL_LIB := -lGL
|
||||
endif
|
||||
PLATFORM_EXT := unix
|
||||
LDFLAGS += -lrt -ldl
|
||||
|
||||
# Raspberry Pi
|
||||
else ifneq (,$(findstring rpi,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
LDFLAGS += -shared -Wl,--version-script=link.T
|
||||
fpic = -fPIC
|
||||
GLES = 1
|
||||
GL_LIB := -lGLESv2
|
||||
INCFLAGS += -I/opt/vc/include
|
||||
CPUFLAGS += -DARMv5_ONLY
|
||||
PLATFORM_EXT := unix
|
||||
TARGET_ARCH = arm
|
||||
LDFLAGS += -lrt -ldl
|
||||
|
||||
# i.MX6
|
||||
else ifneq (,$(findstring imx6,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
LDFLAGS += -shared -Wl,--version-script=link.T
|
||||
fpic = -fPIC
|
||||
GLES = 1
|
||||
GL_LIB := -lGLESv2
|
||||
CPUFLAGS +=
|
||||
PLATFORM_EXT := unix
|
||||
TARGET_ARCH = arm
|
||||
HAVE_NEON=1
|
||||
FFMPEGINCFLAGS += -I$(FFMPEGDIR)/linux/$(TARGET_ARCH)/include
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/linux/$(TARGET_ARCH)/lib
|
||||
FFMPEGLDFLAGS += -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale
|
||||
LDFLAGS += -lrt -ldl
|
||||
|
||||
# OS X
|
||||
else ifneq (,$(findstring osx,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro.dylib
|
||||
LDFLAGS += -dynamiclib
|
||||
OSXVER = `sw_vers -productVersion | cut -d. -f 2`
|
||||
OSX_LT_MAVERICKS = `(( $(OSXVER) <= 9 )) && echo "YES"`
|
||||
ifeq ($(OSX_LT_MAVERICKS),"YES")
|
||||
LDFLAGS += -mmacosx-version-min=10.5
|
||||
endif
|
||||
LDFLAGS += -stdlib=libc++
|
||||
fpic = -fPIC
|
||||
|
||||
FFMPEGINCFLAGS += -I$(FFMPEGDIR)/macosx/$(TARGET_ARCH)/include
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/macosx/$(TARGET_ARCH)/lib
|
||||
FFMPEGLDFLAGS += -liconv -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale
|
||||
PLATCFLAGS += -D__MACOSX__
|
||||
GL_LIB := -framework OpenGL
|
||||
PLATFORM_EXT := darwin
|
||||
|
||||
# iOS
|
||||
else ifneq (,$(findstring ios,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro_ios.dylib
|
||||
PLATCFLAGS += -DIOS
|
||||
LDFLAGS += -dynamiclib -marm
|
||||
fpic = -fPIC
|
||||
GLES = 1
|
||||
GL_LIB := -framework OpenGLES
|
||||
HAVE_NEON = 1
|
||||
|
||||
FFMPEGINCFLAGS += -I$(FFMPEGDIR)/ios/universal/include
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/ios/universal/lib
|
||||
FFMPEGLDFLAGS += -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale
|
||||
|
||||
ifeq ($(IOSSDK),)
|
||||
IOSSDK := $(shell xcodebuild -version -sdk iphoneos Path)
|
||||
endif
|
||||
|
||||
CC = clang -arch armv7 -isysroot $(IOSSDK)
|
||||
CXX = clang++ -arch armv7 -isysroot $(IOSSDK)
|
||||
OSXVER = `sw_vers -productVersion | cut -c 4`
|
||||
ifneq ($(OSXVER),9)
|
||||
CC += -miphoneos-version-min=5.0
|
||||
AS += -miphoneos-version-min=5.0
|
||||
CXX += -miphoneos-version-min=5.0
|
||||
PLATCFLAGS += -miphoneos-version-min=5.0
|
||||
endif
|
||||
PLATCFLAGS += -DIOS -DHAVE_POSIX_MEMALIGN
|
||||
CPUFLAGS += -DARMv5_ONLY -DARM
|
||||
PLATFORM_EXT := unix
|
||||
TARGET_ARCH = arm
|
||||
|
||||
# Android
|
||||
else ifneq (,$(findstring android,$(platform)))
|
||||
fpic = -fPIC
|
||||
TARGET := $(TARGET_NAME)_libretro_android.so
|
||||
LDFLAGS += -shared -Wl,--version-script=link.T -Wl,--no-undefined -Wl,--warn-common
|
||||
GL_LIB := -lGLESv2
|
||||
|
||||
CC = arm-linux-androideabi-gcc
|
||||
CXX = arm-linux-androideabi-g++
|
||||
TARGET_ARCH = arm
|
||||
GLES = 1
|
||||
PLATCFLAGS += -DANDROID
|
||||
CPUCFLAGS +=
|
||||
HAVE_NEON = 1
|
||||
CPUFLAGS += -marm -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -D__arm__ -DARM_ASM -D__NEON_OPT
|
||||
CFLAGS += -DANDROID
|
||||
LDFLAGS += -llog -lGLESv2 -lEGL
|
||||
|
||||
FFMPEGINCFLAGS += -I$(FFMPEGDIR)/android/armv7/include
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/android/armv7/lib
|
||||
FFMPEGLDFLAGS += -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale
|
||||
|
||||
PLATFORM_EXT := android
|
||||
|
||||
# QNX
|
||||
else ifeq ($(platform), qnx)
|
||||
fpic = -fPIC
|
||||
TARGET := $(TARGET_NAME)_libretro_qnx.so
|
||||
LDFLAGS += -shared -Wl,--version-script=link.T -Wl,--no-undefined -Wl,--warn-common
|
||||
GL_LIB := -lGLESv2
|
||||
|
||||
CC = qcc -Vgcc_ntoarmv7le
|
||||
AS = qcc -Vgcc_ntoarmv7le
|
||||
CXX = QCC -Vgcc_ntoarmv7le
|
||||
AR = QCC -Vgcc_ntoarmv7le
|
||||
TARGET_ARCH = arm
|
||||
GLES = 1
|
||||
PLATCFLAGS += -D__BLACKBERRY_QNX__
|
||||
HAVE_NEON = 1
|
||||
CPUFLAGS += -marm -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp -D__arm__ -DARM_ASM -D__NEON_OPT
|
||||
CFLAGS += -D__QNX__
|
||||
|
||||
PLATFORM_EXT := unix
|
||||
|
||||
# ARM
|
||||
else ifneq (,$(findstring armv,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
fpic := -fPIC
|
||||
LDFLAGS += -shared -Wl,--version-script=link.T -Wl,--no-undefined
|
||||
FFMPEGINCFLAGS += -I$(FFMPEGDIR)/linux/$(TARGET_ARCH)/include
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/linux/$(TARGET_ARCH)/lib
|
||||
FFMPEGLDFLAGS += -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale
|
||||
INCFLAGS += -I.
|
||||
TARGET_ARCH = arm
|
||||
ifneq (,$(findstring gles,$(platform)))
|
||||
GLES := 1
|
||||
GL_LIB := -lGLESv2 -lEGL
|
||||
LDFLAGS += -lGLESv2 -lEGL
|
||||
else
|
||||
GL_LIB := -lGL
|
||||
endif
|
||||
ifneq (,$(findstring cortexa8,$(platform)))
|
||||
CPUFLAGS += -marm -mcpu=cortex-a8
|
||||
else ifneq (,$(findstring cortexa9,$(platform)))
|
||||
CPUFLAGS += -marm -mcpu=cortex-a9
|
||||
endif
|
||||
CPUFLAGS += -marm
|
||||
ifneq (,$(findstring neon,$(platform)))
|
||||
CPUFLAGS += -mfpu=neon -D__NEON_OPT
|
||||
HAVE_NEON = 1
|
||||
endif
|
||||
ifneq (,$(findstring softfloat,$(platform)))
|
||||
CPUFLAGS += -mfloat-abi=softfp
|
||||
else ifneq (,$(findstring hardfloat,$(platform)))
|
||||
CPUFLAGS += -mfloat-abi=hard
|
||||
endif
|
||||
CPUFLAGS += -D__arm__ -DARM_ASM
|
||||
PLATCFLAGS += -DARM
|
||||
LDFLAGS += -lrt -ldl
|
||||
|
||||
# emscripten
|
||||
else ifeq ($(platform), emscripten)
|
||||
TARGET := $(TARGET_NAME)_libretro_emscripten.bc
|
||||
GLES := 1
|
||||
CPUFLAGS +=
|
||||
PLATCFLAGS += -DCC_resampler=mupen_CC_resampler -Dsinc_resampler=mupen_sinc_resampler \
|
||||
-Drglgen_symbol_map=mupen_rglgen_symbol_map -Dmain_exit=mupen_main_exit \
|
||||
-Dadler32=mupen_adler32 -Drarch_resampler_realloc=mupen_rarch_resampler_realloc \
|
||||
-Daudio_convert_s16_to_float_C=mupen_audio_convert_s16_to_float_C -Daudio_convert_float_to_s16_C=mupen_audio_convert_float_to_s16_C \
|
||||
-Daudio_convert_init_simd=mupen_audio_convert_init_simd -Drglgen_resolve_symbols_custom=mupen_rglgen_resolve_symbols_custom \
|
||||
-Drglgen_resolve_symbols=mupen_rglgen_resolve_symbols
|
||||
PLATFORM_EXT := unix
|
||||
|
||||
# Windows MSVC 2017 all architectures
|
||||
else ifneq (,$(findstring windows_msvc2017,$(platform)))
|
||||
|
||||
PlatformSuffix = $(subst windows_msvc2017_,,$(platform))
|
||||
ifneq (,$(findstring desktop,$(PlatformSuffix)))
|
||||
WinPartition = desktop
|
||||
MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -D_UNICODE -DUNICODE -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
|
||||
LDFLAGS += -MANIFEST -NXCOMPAT -DYNAMICBASE -DEBUG -OPT:REF -INCREMENTAL:NO -SUBSYSTEM:WINDOWS -MANIFESTUAC:"level='asInvoker' uiAccess='false'" -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1
|
||||
LIBS += kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib
|
||||
else ifneq (,$(findstring uwp,$(PlatformSuffix)))
|
||||
WinPartition = uwp
|
||||
MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_APP -DWINDLL -D_UNICODE -DUNICODE -DWRL_NO_DEFAULT_LIB
|
||||
LDFLAGS += -APPCONTAINER -NXCOMPAT -DYNAMICBASE -MANIFEST:NO -OPT:REF -SUBSYSTEM:CONSOLE -MANIFESTUAC:NO -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1 -DEBUG:FULL -WINMD:NO
|
||||
LIBS += WindowsApp.lib
|
||||
endif
|
||||
|
||||
CFLAGS += $(MSVC2017CompileFlags) -nologo
|
||||
CXXFLAGS += $(MSVC2017CompileFlags) -nologo -EHsc
|
||||
|
||||
TargetArchMoniker = $(subst $(WinPartition)_,,$(PlatformSuffix))
|
||||
|
||||
CC = cl.exe
|
||||
CXX = cl.exe
|
||||
|
||||
SPACE :=
|
||||
SPACE := $(SPACE) $(SPACE)
|
||||
BACKSLASH :=
|
||||
BACKSLASH := \$(BACKSLASH)
|
||||
filter_out1 = $(filter-out $(firstword $1),$1)
|
||||
filter_out2 = $(call filter_out1,$(call filter_out1,$1))
|
||||
|
||||
reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>nul)))
|
||||
fix_path = $(subst $(SPACE),\ ,$(subst \,/,$1))
|
||||
|
||||
ProgramFiles86w := $(shell cmd /c "echo %PROGRAMFILES(x86)%")
|
||||
ProgramFiles86 := $(shell cygpath "$(ProgramFiles86w)")
|
||||
|
||||
WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0)
|
||||
WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0)
|
||||
WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0)
|
||||
WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0)
|
||||
WindowsSdkDir := $(WindowsSdkDir)
|
||||
|
||||
WindowsSDKVersion ?= $(firstword $(foreach folder,$(subst $(subst \,/,$(WindowsSdkDir)Include/),,$(wildcard $(call fix_path,$(WindowsSdkDir)Include\*))),$(if $(wildcard $(call fix_path,$(WindowsSdkDir)Include/$(folder)/um/Windows.h)),$(folder),)))$(BACKSLASH)
|
||||
WindowsSDKVersion := $(WindowsSDKVersion)
|
||||
|
||||
VsInstallBuildTools = $(ProgramFiles86)/Microsoft Visual Studio/2017/BuildTools
|
||||
VsInstallEnterprise = $(ProgramFiles86)/Microsoft Visual Studio/2017/Enterprise
|
||||
VsInstallProfessional = $(ProgramFiles86)/Microsoft Visual Studio/2017/Professional
|
||||
VsInstallCommunity = $(ProgramFiles86)/Microsoft Visual Studio/2017/Community
|
||||
|
||||
VsInstallRoot ?= $(shell if [ -d "$(VsInstallBuildTools)" ]; then echo "$(VsInstallBuildTools)"; fi)
|
||||
ifeq ($(VsInstallRoot), )
|
||||
VsInstallRoot = $(shell if [ -d "$(VsInstallEnterprise)" ]; then echo "$(VsInstallEnterprise)"; fi)
|
||||
endif
|
||||
ifeq ($(VsInstallRoot), )
|
||||
VsInstallRoot = $(shell if [ -d "$(VsInstallProfessional)" ]; then echo "$(VsInstallProfessional)"; fi)
|
||||
endif
|
||||
ifeq ($(VsInstallRoot), )
|
||||
VsInstallRoot = $(shell if [ -d "$(VsInstallCommunity)" ]; then echo "$(VsInstallCommunity)"; fi)
|
||||
endif
|
||||
VsInstallRoot := $(VsInstallRoot)
|
||||
|
||||
VcCompilerToolsVer := $(shell cat "$(VsInstallRoot)/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt" | grep -o '[0-9\.]*')
|
||||
VcCompilerToolsDir := $(VsInstallRoot)/VC/Tools/MSVC/$(VcCompilerToolsVer)
|
||||
|
||||
WindowsSDKSharedIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\shared")
|
||||
WindowsSDKUCRTIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\ucrt")
|
||||
WindowsSDKUMIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\um")
|
||||
WindowsSDKUCRTLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\ucrt\$(TargetArchMoniker)")
|
||||
WindowsSDKUMLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\um\$(TargetArchMoniker)")
|
||||
|
||||
LIB := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/lib/$(TargetArchMoniker)")
|
||||
INCLUDE := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/include")
|
||||
|
||||
# For some reason the HostX86 compiler doesn't like compiling for x64
|
||||
# ("no such file" opening a shared library), and vice-versa.
|
||||
# Work around it for now by using the strictly x86 compiler for x86, and x64 for x64.
|
||||
# NOTE: What about ARM?
|
||||
ifneq (,$(findstring x64,$(TargetArchMoniker)))
|
||||
TARGET_ARCH = x86_64
|
||||
VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX64
|
||||
LIB := $(LIB);$(CORE_DIR)/dx9sdk/Lib/x64
|
||||
else
|
||||
TARGET_ARCH = x86
|
||||
VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX86
|
||||
LIB := $(LIB);$(CORE_DIR)/dx9sdk/Lib/x86
|
||||
endif
|
||||
|
||||
PATH := $(shell IFS=$$'\n'; cygpath "$(VCCompilerToolsBinDir)/$(TargetArchMoniker)"):$(PATH)
|
||||
PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VsInstallRoot)/Common7/IDE")
|
||||
|
||||
export INCLUDE := $(INCLUDE);$(WindowsSDKSharedIncludeDir);$(WindowsSDKUCRTIncludeDir);$(WindowsSDKUMIncludeDir)
|
||||
export LIB := $(LIB);$(WindowsSDKUCRTLibDir);$(WindowsSDKUMLibDir);$(FFMPEGDIR)/Windows/$(TARGET_ARCH)/lib
|
||||
TARGET := $(TARGET_NAME)_libretro.dll
|
||||
PSS_STYLE :=2
|
||||
LDFLAGS += -DLL
|
||||
PLATFORM_EXT := win32
|
||||
FFMPEGINCFLAGS += -I$(FFMPEGDIR)/Windows/$(TARGET_ARCH)/include
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/Windows/$(TARGET_ARCH)/lib
|
||||
FFMPEGLDFLAGS += -LIBPATH:$(FFMPEGLIBDIR)
|
||||
GL_LIB := opengl32.lib
|
||||
LDFLAGS += ws2_32.lib user32.lib shell32.lib avcodec.lib avutil.lib swresample.lib swscale.lib avformat.lib advapi32.lib winmm.lib gdi32.lib d3d9.lib d3dx9.lib
|
||||
|
||||
# Windows
|
||||
else ifneq (,$(findstring win,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro.dll
|
||||
CFLAGS += -D_UNICODE -DUNICODE
|
||||
CXXFLAGS += -fpermissive -Wno-multichar -D_UNICODE -DUNICODE
|
||||
LDFLAGS += -shared -Wl,--no-undefined -static-libgcc -static-libstdc++ -Wl,--version-script=link.T -lwinmm -lgdi32 -lwsock32 -lws2_32 -ld3d9 -ld3dx9
|
||||
GL_LIB := -lopengl32
|
||||
PLATFORM_EXT := win32
|
||||
FFMPEGINCFLAGS += -I$(FFMPEGDIR)/Windows/$(TARGET_ARCH)/include
|
||||
FFMPEGLDFLAGS += -lavformat -lavcodec -lavutil -lswresample -lswscale
|
||||
INCFLAGS += -include $(CORE_DIR)/Windows/mingw_defines.h
|
||||
ifneq (,$(findstring 64,$(TARGET_ARCH)))
|
||||
LDFLAGS += -L$(CORE_DIR)/dx9sdk/Lib/x64
|
||||
else
|
||||
LDFLAGS += -L$(CORE_DIR)/dx9sdk/Lib/x86
|
||||
endif
|
||||
fpic = -fPIC
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
endif
|
||||
|
||||
include Makefile.common
|
||||
|
||||
ifeq ($(GLES), 1)
|
||||
GLFLAGS += -DGLES -DUSING_GLES2
|
||||
else
|
||||
GLFLAGS += -DHAVE_OPENGL
|
||||
endif
|
||||
|
||||
COREFLAGS += -D__LIBRETRO__ -DPPSSPP -DUSE_FFMPEG -DGLEW_STATIC -DGLEW_NO_GLU
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
ifneq (,$(findstring msvc,$(platform)))
|
||||
CPUOPTS += -Od -MDd -Zi -FS
|
||||
LDFLAGS += -DEBUG
|
||||
else
|
||||
CPUOPTS += -O0 -g
|
||||
endif
|
||||
CPUOPTS += -D_DEBUG
|
||||
else
|
||||
CPUOPTS += -O2 -D_NDEBUG
|
||||
endif
|
||||
|
||||
ifeq (,$(findstring msvc,$(platform)))
|
||||
CXXFLAGS += -std=c++11
|
||||
endif
|
||||
|
||||
### Finalize ###
|
||||
OBJECTS += $(SOURCES_CXX:.cpp=.o) $(SOURCES_C:.c=.o) $(ASMFILES:.S=.o)
|
||||
CXXFLAGS += $(CPUOPTS) $(COREFLAGS) $(FFMPEGINCFLAGS) $(INCFLAGS) $(INCFLAGS_PLATFORM) $(PLATCFLAGS) $(fpic) $(PLATCFLAGS) $(CPUFLAGS) $(GLFLAGS) $(DYNAFLAGS)
|
||||
CFLAGS += $(CPUOPTS) $(COREFLAGS) $(FFMPEGINCFLAGS) $(INCFLAGS) $(INCFLAGS_PLATFORM) $(PLATCFLAGS) $(fpic) $(PLATCFLAGS) $(CPUFLAGS) $(GLFLAGS) $(DYNAFLAGS)
|
||||
|
||||
LDFLAGS += $(FFMPEGLDFLAGS) $(fpic)
|
||||
ifeq (,$(findstring android,$(platform)))
|
||||
ifeq (,$(findstring msvc,$(platform)))
|
||||
LDFLAGS += -lpthread
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJOUT = -o
|
||||
LINKOUT = -o
|
||||
|
||||
ifneq (,$(findstring msvc,$(platform)))
|
||||
OBJOUT = -Fo
|
||||
LINKOUT = -out:
|
||||
LD = link.exe
|
||||
else
|
||||
LD = $(CXX)
|
||||
CFLAGS += -ffunction-sections -fdata-sections
|
||||
CXXFLAGS += -ffunction-sections -fdata-sections
|
||||
LDFLAGS += -Wl,--gc-sections
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
%.o: %.S
|
||||
$(AS) $(CFLAGS) -c $(OBJOUT)$@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $(OBJOUT)$@ $<
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) -c $(OBJOUT)$@ $<
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
ifeq ($(STATIC_LINKING), 1)
|
||||
$(AR) rcs $A $(OBJECTS)
|
||||
else
|
||||
$(LD) $(LINKOUT)$@ $(OBJECTS) $(LDFLAGS) $(GL_LIB)
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -f $(OBJECTS) $(TARGET)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
print-%:
|
||||
@echo '$*=$($*)'
|
690
libretro/Makefile.common
Normal file
690
libretro/Makefile.common
Normal file
@ -0,0 +1,690 @@
|
||||
FFMPEGDIR = $(CORE_DIR)/ffmpeg
|
||||
LIBRETRODIR = $(CORE_DIR)/libretro
|
||||
COREDIR = $(CORE_DIR)/Core
|
||||
COMMONDIR = $(CORE_DIR)/Common
|
||||
GPUCOMMONDIR = $(CORE_DIR)/GPU/Common
|
||||
GPUDIR = $(CORE_DIR)/GPU
|
||||
EXTDIR = $(CORE_DIR)/ext
|
||||
NATIVEDIR = $(EXTDIR)/native
|
||||
|
||||
INCFLAGS += \
|
||||
-I$(CORE_DIR) \
|
||||
-I$(COMMONDIR) \
|
||||
-I$(CORE_DIR)/libretro \
|
||||
-I$(EXTDIR)/native \
|
||||
-I$(EXTDIR)/zlib \
|
||||
-I$(EXTDIR)/snappy \
|
||||
-I$(FFMPEGDIR) \
|
||||
$(FFMPEGINCFLAGS) \
|
||||
-I$(EXTDIR)/cityhash \
|
||||
-I$(EXTDIR)/armips \
|
||||
-I$(NATIVEDIR)/ext \
|
||||
-I$(NATIVEDIR) \
|
||||
-I$(EXTDIR)/libkirk \
|
||||
-I$(EXTDIR)/xbrz \
|
||||
-I$(EXTDIR)/xxhash \
|
||||
-I$(EXTDIR)/glew
|
||||
|
||||
ifeq ($(PLATFORM_EXT), android)
|
||||
INCFLAGS += -I$(NATIVEDIR)/ext/libzip
|
||||
SOURCES_C += \
|
||||
$(NATIVEDIR)/ext/libzip/mkstemp.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_add.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_add_dir.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_close.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_delete.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_dirent.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_entry_free.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_entry_new.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_err_str.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_error.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_error_clear.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_error_get.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_error_get_sys_type.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_error_strerror.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_error_to_str.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_fclose.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_file_error_clear.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_file_error_get.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_file_get_offset.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_file_strerror.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_filerange_crc.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_fopen.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_fopen_index.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_fread.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_free.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_get_archive_comment.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_get_archive_flag.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_get_file_comment.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_get_name.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_get_num_files.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_memdup.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_name_locate.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_new.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_open.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_rename.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_replace.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_set_archive_comment.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_set_archive_flag.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_set_file_comment.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_set_name.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_source_buffer.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_source_file.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_source_filep.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_source_free.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_source_function.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_source_zip.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_stat.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_stat_index.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_stat_init.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_strerror.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_unchange.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_unchange_all.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_unchange_archive.c \
|
||||
$(NATIVEDIR)/ext/libzip/zip_unchange_data.c
|
||||
|
||||
SOURCES_C +=\
|
||||
$(NATIVEDIR)/gfx_es2/gl3stub.c \
|
||||
$(NATIVEDIR)/math/fast/fast_math.c \
|
||||
$(NATIVEDIR)/math/fast/fast_matrix.c
|
||||
endif
|
||||
|
||||
SOURCES_C += $(EXTDIR)/glew/glew.c
|
||||
|
||||
SOURCES_C += \
|
||||
$(EXTDIR)/libkirk/AES.c \
|
||||
$(EXTDIR)/libkirk/amctrl.c \
|
||||
$(EXTDIR)/libkirk/SHA1.c \
|
||||
$(EXTDIR)/libkirk/bn.c \
|
||||
$(EXTDIR)/libkirk/ec.c \
|
||||
$(EXTDIR)/libkirk/kirk_engine.c
|
||||
|
||||
SOURCES_C += \
|
||||
$(NATIVEDIR)/ext/libpng17/png.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngerror.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngget.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngmem.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngread.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngrio.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngrtran.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngrutil.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngset.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngtrans.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngwio.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngwrite.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngwtran.c \
|
||||
$(NATIVEDIR)/ext/libpng17/pngwutil.c
|
||||
|
||||
SOURCES_C += $(EXTDIR)/sfmt19937/SFMT.c
|
||||
SOURCES_C += $(EXTDIR)/xxhash.c
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(EXTDIR)/snappy/snappy-c.cpp \
|
||||
$(EXTDIR)/snappy/snappy.cpp
|
||||
|
||||
SOURCES_CXX += $(EXTDIR)/xbrz/xbrz.cpp
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(NATIVEDIR)/ext/vjson/json.cpp \
|
||||
$(NATIVEDIR)/ext/vjson/block_allocator.cpp
|
||||
|
||||
SOURCES_CXX += $(NATIVEDIR)/ext/cityhash/city.cpp
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(COMMONDIR)/Crypto/md5.cpp \
|
||||
$(COMMONDIR)/Crypto/sha1.cpp \
|
||||
$(COMMONDIR)/Crypto/sha256.cpp
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(COMMONDIR)/ChunkFile.cpp \
|
||||
$(COMMONDIR)/ConsoleListener.cpp \
|
||||
$(COMMONDIR)/FileUtil.cpp \
|
||||
$(COMMONDIR)/KeyMap.cpp \
|
||||
$(COMMONDIR)/LogManager.cpp \
|
||||
$(COMMONDIR)/OSVersion.cpp \
|
||||
$(COMMONDIR)/MemoryUtil.cpp \
|
||||
$(COMMONDIR)/Misc.cpp \
|
||||
$(COMMONDIR)/MsgHandler.cpp \
|
||||
$(COMMONDIR)/StringUtils.cpp \
|
||||
$(COMMONDIR)/Timer.cpp \
|
||||
$(COMMONDIR)/ThreadPools.cpp
|
||||
|
||||
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(GPUCOMMONDIR)/VertexDecoderCommon.cpp \
|
||||
$(GPUCOMMONDIR)/GPUStateUtils.cpp \
|
||||
$(GPUCOMMONDIR)/DrawEngineCommon.cpp \
|
||||
$(GPUCOMMONDIR)/SplineCommon.cpp \
|
||||
$(GPUCOMMONDIR)/FramebufferCommon.cpp \
|
||||
$(GPUCOMMONDIR)/ShaderId.cpp \
|
||||
$(GPUCOMMONDIR)/ShaderCommon.cpp \
|
||||
$(GPUCOMMONDIR)/ShaderUniforms.cpp \
|
||||
$(GPUCOMMONDIR)/ShaderTranslation.cpp \
|
||||
$(GPUCOMMONDIR)/GPUDebugInterface.cpp \
|
||||
$(GPUCOMMONDIR)/DepalettizeShaderCommon.cpp \
|
||||
$(GPUCOMMONDIR)/TransformCommon.cpp \
|
||||
$(GPUCOMMONDIR)/IndexGenerator.cpp \
|
||||
$(GPUCOMMONDIR)/TextureDecoder.cpp \
|
||||
$(GPUCOMMONDIR)/PostShader.cpp \
|
||||
$(COMMONDIR)/ColorConv.cpp \
|
||||
$(GPUDIR)/Debugger/Breakpoints.cpp \
|
||||
$(GPUDIR)/Debugger/Stepping.cpp \
|
||||
$(GPUDIR)/Debugger/Record.cpp \
|
||||
$(GPUDIR)/Common/TextureCacheCommon.cpp \
|
||||
$(GPUDIR)/Common/TextureScalerCommon.cpp \
|
||||
$(GPUDIR)/Common/SoftwareTransformCommon.cpp \
|
||||
$(GPUDIR)/Common/StencilCommon.cpp \
|
||||
$(GPUDIR)/Software/TransformUnit.cpp \
|
||||
$(GPUDIR)/Software/SoftGpu.cpp \
|
||||
$(GPUDIR)/Software/Sampler.cpp \
|
||||
$(GPUDIR)/GeDisasm.cpp \
|
||||
$(GPUDIR)/GPUCommon.cpp \
|
||||
$(GPUDIR)/GPU.cpp \
|
||||
$(GPUDIR)/GPUState.cpp \
|
||||
$(GPUDIR)/Math3D.cpp \
|
||||
$(GPUDIR)/Null/NullGpu.cpp \
|
||||
$(GPUDIR)/Software/Clipper.cpp \
|
||||
$(GPUDIR)/Software/Lighting.cpp \
|
||||
$(GPUDIR)/Software/Rasterizer.cpp \
|
||||
$(GPUDIR)/GLES/DepalettizeShaderGLES.cpp \
|
||||
$(GPUDIR)/GLES/VertexShaderGeneratorGLES.cpp \
|
||||
$(GPUDIR)/GLES/DrawEngineGLES.cpp \
|
||||
$(GPUDIR)/GLES/GPU_GLES.cpp \
|
||||
$(GPUDIR)/GLES/FragmentShaderGeneratorGLES.cpp \
|
||||
$(GPUDIR)/GLES/FragmentTestCacheGLES.cpp \
|
||||
$(GPUDIR)/GLES/FramebufferManagerGLES.cpp \
|
||||
$(GPUDIR)/GLES/TextureCacheGLES.cpp \
|
||||
$(GPUDIR)/GLES/TextureScalerGLES.cpp \
|
||||
$(GPUDIR)/GLES/ShaderManagerGLES.cpp \
|
||||
$(GPUDIR)/GLES/StateMappingGLES.cpp \
|
||||
$(GPUDIR)/GLES/StencilBufferGLES.cpp \
|
||||
$(EXTDIR)/native/base/backtrace.cpp \
|
||||
$(EXTDIR)/native/base/buffer.cpp \
|
||||
$(EXTDIR)/native/base/colorutil.cpp \
|
||||
$(EXTDIR)/native/base/display.cpp \
|
||||
$(EXTDIR)/native/base/logging.cpp \
|
||||
$(EXTDIR)/native/base/stringutil.cpp \
|
||||
$(EXTDIR)/native/base/timeutil.cpp \
|
||||
$(EXTDIR)/native/data/compression.cpp \
|
||||
$(EXTDIR)/glslang/OGLCompilersDLL/InitializeDll.cpp \
|
||||
$(EXTDIR)/glslang/glslang/GenericCodeGen/CodeGen.cpp \
|
||||
$(EXTDIR)/glslang/glslang/GenericCodeGen/Link.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/Constant.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/InfoSink.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/Initialize.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/Intermediate.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/IntermTraverse.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/ParseContextBase.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/ParseHelper.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/PoolAlloc.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/RemoveTree.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/Scan.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/ShaderLang.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/SymbolTable.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/Versions.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/glslang_tab.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/intermOut.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/iomapper.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/limits.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/linkValidate.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/parseConst.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/propagateNoContraction.cpp \
|
||||
$(EXTDIR)/glslang/glslang/MachineIndependent/reflection.cpp \
|
||||
$(EXTDIR)/glslang/SPIRV/InReadableOrder.cpp \
|
||||
$(EXTDIR)/glslang/SPIRV/GlslangToSpv.cpp \
|
||||
$(EXTDIR)/glslang/SPIRV/Logger.cpp \
|
||||
$(EXTDIR)/glslang/SPIRV/SpvBuilder.cpp \
|
||||
$(EXTDIR)/SPIRV-Cross/spirv_cfg.cpp \
|
||||
$(EXTDIR)/SPIRV-Cross/spirv_cross.cpp \
|
||||
$(EXTDIR)/SPIRV-Cross/spirv_glsl.cpp \
|
||||
$(EXTDIR)/SPIRV-Cross/spirv_hlsl.cpp
|
||||
|
||||
ifeq ($(PLATFORM_EXT), win32)
|
||||
SOURCES_CXX += $(COMMONDIR)/MemArenaWin32.cpp \
|
||||
$(EXTDIR)/glslang/glslang/OSDependent/Windows/ossource.cpp \
|
||||
$(NATIVEDIR)/gfx_es2/draw_text_win.cpp
|
||||
else ifeq ($(PLATFORM_EXT), darwin)
|
||||
SOURCES_CXX += $(COMMONDIR)/MemArenaDarwin.cpp
|
||||
else ifeq ($(PLATFORM_EXT), android)
|
||||
SOURCES_CXX += $(COMMONDIR)/MemArenaAndroid.cpp \
|
||||
$(EXTDIR)/glslang/glslang/OSDependent/Unix/ossource.cpp \
|
||||
$(NATIVEDIR)/gfx_es2/draw_text_android.cpp
|
||||
else
|
||||
SOURCES_CXX += $(COMMONDIR)/MemArenaPosix.cpp \
|
||||
$(EXTDIR)/glslang/glslang/OSDependent/Unix/ossource.cpp
|
||||
endif
|
||||
|
||||
SOURCES_CXX += $(NATIVEDIR)/math/dataconv.cpp \
|
||||
$(NATIVEDIR)/file/fd_util.cpp \
|
||||
$(NATIVEDIR)/file/file_util.cpp \
|
||||
$(NATIVEDIR)/file/ini_file.cpp \
|
||||
$(NATIVEDIR)/file/free.cpp \
|
||||
$(NATIVEDIR)/file/zip_read.cpp \
|
||||
$(NATIVEDIR)/gfx/gl_debug_log.cpp \
|
||||
$(NATIVEDIR)/gfx/texture_atlas.cpp \
|
||||
$(NATIVEDIR)/gfx/d3d9_shader.cpp \
|
||||
$(NATIVEDIR)/gfx/d3d9_state.cpp \
|
||||
$(NATIVEDIR)/gfx_es2/draw_buffer.cpp \
|
||||
$(NATIVEDIR)/gfx_es2/draw_text.cpp \
|
||||
$(NATIVEDIR)/gfx_es2/gpu_features.cpp \
|
||||
$(NATIVEDIR)/gfx_es2/glsl_program.cpp \
|
||||
$(NATIVEDIR)/i18n/i18n.cpp \
|
||||
$(NATIVEDIR)/image/zim_load.cpp \
|
||||
$(NATIVEDIR)/image/png_load.cpp \
|
||||
$(NATIVEDIR)/input/gesture_detector.cpp \
|
||||
$(NATIVEDIR)/input/input_state.cpp \
|
||||
$(NATIVEDIR)/math/curves.cpp \
|
||||
$(NATIVEDIR)/math/expression_parser.cpp \
|
||||
$(NATIVEDIR)/math/lin/matrix4x4.cpp \
|
||||
$(NATIVEDIR)/math/lin/plane.cpp \
|
||||
$(NATIVEDIR)/math/lin/quat.cpp \
|
||||
$(NATIVEDIR)/math/lin/vec3.cpp \
|
||||
$(NATIVEDIR)/net/http_client.cpp \
|
||||
$(NATIVEDIR)/net/resolve.cpp \
|
||||
$(NATIVEDIR)/net/url.cpp \
|
||||
$(NATIVEDIR)/thin3d/thin3d.cpp \
|
||||
$(NATIVEDIR)/thin3d/thin3d_gl.cpp \
|
||||
$(NATIVEDIR)/thin3d/GLRenderManager.cpp \
|
||||
$(NATIVEDIR)/thin3d/GLQueueRunner.cpp \
|
||||
$(NATIVEDIR)/thin3d/DataFormatGL.cpp \
|
||||
$(NATIVEDIR)/thread/threadutil.cpp \
|
||||
$(NATIVEDIR)/thread/threadpool.cpp \
|
||||
$(NATIVEDIR)/ui/screen.cpp \
|
||||
$(NATIVEDIR)/ui/ui.cpp \
|
||||
$(NATIVEDIR)/ui/ui_context.cpp \
|
||||
$(NATIVEDIR)/ui/ui_screen.cpp \
|
||||
$(NATIVEDIR)/ui/ui_tween.cpp \
|
||||
$(NATIVEDIR)/ui/view.cpp \
|
||||
$(NATIVEDIR)/ui/viewgroup.cpp \
|
||||
$(NATIVEDIR)/util/hash/hash.cpp \
|
||||
$(NATIVEDIR)/util/text/utf8.cpp \
|
||||
$(NATIVEDIR)/util/text/parsers.cpp \
|
||||
$(NATIVEDIR)/util/text/wrap_text.cpp \
|
||||
$(NATIVEDIR)/ext/jpge/jpgd.cpp \
|
||||
$(NATIVEDIR)/ext/jpge/jpge.cpp \
|
||||
$(COREDIR)/AVIDump.cpp \
|
||||
$(COREDIR)/Config.cpp \
|
||||
$(COREDIR)/TextureReplacer.cpp \
|
||||
$(COREDIR)/Core.cpp \
|
||||
$(COREDIR)/WaveFile.cpp \
|
||||
$(COREDIR)/FileLoaders/HTTPFileLoader.cpp \
|
||||
$(COREDIR)/FileLoaders/CachingFileLoader.cpp \
|
||||
$(COREDIR)/FileLoaders/DiskCachingFileLoader.cpp \
|
||||
$(COREDIR)/FileLoaders/RetryingFileLoader.cpp \
|
||||
$(COREDIR)/FileLoaders/RamCachingFileLoader.cpp \
|
||||
$(COREDIR)/FileLoaders/LocalFileLoader.cpp \
|
||||
$(COREDIR)/CoreTiming.cpp \
|
||||
$(COREDIR)/CwCheat.cpp \
|
||||
$(COREDIR)/HDRemaster.cpp \
|
||||
$(COREDIR)/Debugger/Breakpoints.cpp \
|
||||
$(COREDIR)/Debugger/SymbolMap.cpp \
|
||||
$(COREDIR)/Dialog/PSPDialog.cpp \
|
||||
$(COREDIR)/Dialog/PSPGamedataInstallDialog.cpp \
|
||||
$(COREDIR)/Dialog/PSPMsgDialog.cpp \
|
||||
$(COREDIR)/Dialog/PSPNetconfDialog.cpp \
|
||||
$(COREDIR)/Dialog/PSPOskDialog.cpp \
|
||||
$(COREDIR)/Dialog/PSPSaveDialog.cpp \
|
||||
$(COREDIR)/Dialog/PSPScreenshotDialog.cpp \
|
||||
$(COREDIR)/Dialog/SavedataParam.cpp \
|
||||
$(COREDIR)/ELF/ElfReader.cpp \
|
||||
$(COREDIR)/ELF/PBPReader.cpp \
|
||||
$(COREDIR)/ELF/PrxDecrypter.cpp \
|
||||
$(COREDIR)/ELF/ParamSFO.cpp \
|
||||
$(COREDIR)/FileSystems/tlzrc.cpp \
|
||||
$(COREDIR)/FileSystems/BlockDevices.cpp \
|
||||
$(COREDIR)/FileSystems/BlobFileSystem.cpp \
|
||||
$(COREDIR)/FileSystems/DirectoryFileSystem.cpp \
|
||||
$(COREDIR)/FileSystems/FileSystem.cpp \
|
||||
$(COREDIR)/FileSystems/ISOFileSystem.cpp \
|
||||
$(COREDIR)/FileSystems/MetaFileSystem.cpp \
|
||||
$(COREDIR)/FileSystems/VirtualDiscFileSystem.cpp \
|
||||
$(COREDIR)/Font/PGF.cpp \
|
||||
$(COREDIR)/HLE/HLE.cpp \
|
||||
$(COREDIR)/HLE/KUBridge.cpp \
|
||||
$(COREDIR)/HLE/sceSha256.cpp \
|
||||
$(COREDIR)/HLE/sceG729.cpp \
|
||||
$(COREDIR)/HLE/sceSfmt19937.cpp \
|
||||
$(COREDIR)/HLE/ReplaceTables.cpp \
|
||||
$(COREDIR)/HLE/HLEHelperThread.cpp \
|
||||
$(COREDIR)/HLE/HLETables.cpp \
|
||||
$(COREDIR)/HLE/sceAdler.cpp \
|
||||
$(COREDIR)/HLE/sceAtrac.cpp \
|
||||
$(COREDIR)/HLE/sceAudio.cpp \
|
||||
$(COREDIR)/HLE/sceAudiocodec.cpp \
|
||||
$(COREDIR)/HLE/sceAudioRouting.cpp \
|
||||
$(COREDIR)/HLE/sceCcc.cpp \
|
||||
$(COREDIR)/HLE/sceChnnlsv.cpp \
|
||||
$(COREDIR)/HLE/sceCtrl.cpp \
|
||||
$(COREDIR)/HLE/sceDeflt.cpp \
|
||||
$(COREDIR)/HLE/sceDisplay.cpp \
|
||||
$(COREDIR)/HLE/sceDmac.cpp \
|
||||
$(COREDIR)/HLE/sceGameUpdate.cpp \
|
||||
$(COREDIR)/HLE/sceGe.cpp \
|
||||
$(COREDIR)/HLE/sceFont.cpp \
|
||||
$(COREDIR)/HLE/sceHeap.cpp \
|
||||
$(COREDIR)/HLE/sceHprm.cpp \
|
||||
$(COREDIR)/HLE/sceHttp.cpp \
|
||||
$(COREDIR)/HLE/sceImpose.cpp \
|
||||
$(COREDIR)/HLE/sceIo.cpp \
|
||||
$(COREDIR)/HLE/sceJpeg.cpp \
|
||||
$(COREDIR)/HLE/sceKernel.cpp \
|
||||
$(COREDIR)/HLE/sceKernelAlarm.cpp \
|
||||
$(COREDIR)/HLE/sceKernelEventFlag.cpp \
|
||||
$(COREDIR)/HLE/sceKernelInterrupt.cpp \
|
||||
$(COREDIR)/HLE/sceKernelMbx.cpp \
|
||||
$(COREDIR)/HLE/sceKernelMemory.cpp \
|
||||
$(COREDIR)/HLE/sceKernelModule.cpp \
|
||||
$(COREDIR)/HLE/sceKernelMsgPipe.cpp \
|
||||
$(COREDIR)/HLE/sceKernelMutex.cpp \
|
||||
$(COREDIR)/HLE/sceKernelSemaphore.cpp \
|
||||
$(COREDIR)/HLE/sceKernelThread.cpp \
|
||||
$(COREDIR)/HLE/sceKernelTime.cpp \
|
||||
$(COREDIR)/HLE/sceKernelVTimer.cpp \
|
||||
$(COREDIR)/HLE/sceMpeg.cpp \
|
||||
$(COREDIR)/HLE/sceNet.cpp \
|
||||
$(COREDIR)/HLE/sceNetAdhoc.cpp \
|
||||
$(COREDIR)/HLE/proAdhocServer.cpp \
|
||||
$(COREDIR)/HLE/proAdhoc.cpp \
|
||||
$(COREDIR)/HLE/sceOpenPSID.cpp \
|
||||
$(COREDIR)/HLE/sceP3da.cpp \
|
||||
$(COREDIR)/HLE/sceMt19937.cpp \
|
||||
$(COREDIR)/HLE/sceMd5.cpp \
|
||||
$(COREDIR)/HLE/sceMp4.cpp \
|
||||
$(COREDIR)/HLE/sceMp3.cpp \
|
||||
$(COREDIR)/HLE/sceParseHttp.cpp \
|
||||
$(COREDIR)/HLE/sceParseUri.cpp \
|
||||
$(COREDIR)/HLE/scePower.cpp \
|
||||
$(COREDIR)/HLE/scePsmf.cpp \
|
||||
$(COREDIR)/HLE/sceRtc.cpp \
|
||||
$(COREDIR)/HLE/sceSas.cpp \
|
||||
$(COREDIR)/HLE/sceSsl.cpp \
|
||||
$(COREDIR)/HLE/sceUmd.cpp \
|
||||
$(COREDIR)/HLE/sceUsb.cpp \
|
||||
$(COREDIR)/HLE/sceUsbCam.cpp \
|
||||
$(COREDIR)/HLE/sceUtility.cpp \
|
||||
$(COREDIR)/HLE/sceVaudio.cpp \
|
||||
$(COREDIR)/HLE/scePspNpDrm_user.cpp \
|
||||
$(COREDIR)/HLE/sceNp.cpp \
|
||||
$(COREDIR)/HLE/scePauth.cpp \
|
||||
$(COREDIR)/HLE/sceUsbGps.cpp \
|
||||
$(COREDIR)/HW/SimpleAudioDec.cpp \
|
||||
$(COREDIR)/HW/AsyncIOManager.cpp \
|
||||
$(COREDIR)/HW/MediaEngine.cpp \
|
||||
$(COREDIR)/HW/MpegDemux.cpp \
|
||||
$(COREDIR)/HW/MemoryStick.cpp \
|
||||
$(COREDIR)/HW/SasAudio.cpp \
|
||||
$(COREDIR)/HW/SasReverb.cpp \
|
||||
$(COREDIR)/HW/StereoResampler.cpp \
|
||||
$(COREDIR)/Compatibility.cpp \
|
||||
$(COREDIR)/Host.cpp \
|
||||
$(COREDIR)/Loaders.cpp \
|
||||
$(COREDIR)/MIPS/JitCommon/JitCommon.cpp \
|
||||
$(COREDIR)/MIPS/JitCommon/JitState.cpp \
|
||||
$(COREDIR)/MIPS/JitCommon/JitBlockCache.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRCompALU.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRCompBranch.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRCompFPU.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRCompLoadStore.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRCompVFPU.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRInterpreter.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRJit.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRInst.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRPassSimplify.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRRegCache.cpp \
|
||||
$(COREDIR)/MIPS/IR/IRFrontend.cpp \
|
||||
$(COREDIR)/MIPS/MIPS.cpp \
|
||||
$(COREDIR)/MIPS/MIPSAnalyst.cpp \
|
||||
$(COREDIR)/MIPS/MIPSCodeUtils.cpp \
|
||||
$(COREDIR)/MIPS/MIPSDebugInterface.cpp \
|
||||
$(COREDIR)/MIPS/MIPSDis.cpp \
|
||||
$(COREDIR)/MIPS/MIPSDisVFPU.cpp \
|
||||
$(COREDIR)/MIPS/MIPSInt.cpp \
|
||||
$(COREDIR)/MIPS/MIPSIntVFPU.cpp \
|
||||
$(COREDIR)/MIPS/MIPSTables.cpp \
|
||||
$(COREDIR)/MIPS/MIPSVFPUUtils.cpp \
|
||||
$(COREDIR)/MemMap.cpp \
|
||||
$(COREDIR)/MemMapFunctions.cpp \
|
||||
$(COREDIR)/PSPLoaders.cpp \
|
||||
$(COREDIR)/Reporting.cpp \
|
||||
$(COREDIR)/SaveState.cpp \
|
||||
$(COREDIR)/Screenshot.cpp \
|
||||
$(COREDIR)/System.cpp \
|
||||
$(COREDIR)/Util/BlockAllocator.cpp \
|
||||
$(COREDIR)/Util/PPGeDraw.cpp \
|
||||
$(COREDIR)/Util/ppge_atlas.cpp \
|
||||
$(COREDIR)/Util/AudioFormat.cpp \
|
||||
$(EXTDIR)/disarm.cpp \
|
||||
$(CORE_DIR)/UI/TextureUtil.cpp
|
||||
|
||||
SOURCES_CXX += $(COREDIR)/HLE/__sceAudio.cpp
|
||||
|
||||
### DYNAREC ###
|
||||
ifeq ($(WITH_DYNAREC),1)
|
||||
DYNAFLAGS += -DDYNAREC
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
DYNAFLAGS += -D_ARCH_32
|
||||
SOURCES_CXX += $(COMMONDIR)/ArmEmitter.cpp \
|
||||
$(COMMONDIR)/ArmCPUDetect.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmAsm.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompALU.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompBranch.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompFPU.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompLoadStore.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompVFPU.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompReplace.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmJit.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmRegCache.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmRegCacheFPU.cpp \
|
||||
$(GPUCOMMONDIR)/VertexDecoderArm.cpp
|
||||
|
||||
ifeq ($(HAVE_NEON),1)
|
||||
SOURCES_CXX += \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompVFPUNEON.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompVFPUNEONUtil.cpp \
|
||||
$(COREDIR)/Util/AudioFormatNEON.cpp \
|
||||
$(COMMONDIR)/ColorConvNEON.cpp \
|
||||
$(GPUDIR)/Common/TextureDecoderNEON.cpp
|
||||
|
||||
SOURCES_C += $(NATIVEDIR)/ext/libpng17/arm/arm_init.c \
|
||||
$(NATIVEDIR)/ext/libpng17/arm/filter_neon_intrinsics.c
|
||||
ASMFILES += $(NATIVEDIR)/math/fast/fast_matrix_neon.S \
|
||||
$(NATIVEDIR)/ext/libpng17/arm/filter_neon.S
|
||||
endif
|
||||
else ifeq ($(TARGET_ARCH),arm64)
|
||||
DYNAFLAGS += -D_ARCH_64
|
||||
SOURCES_CXX += $(COMMONDIR)/Arm64Emitter.cpp \
|
||||
$(COMMONDIR)/ArmCPUDetect.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64Asm.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64CompALU.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64CompBranch.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64CompFPU.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64CompLoadStore.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64CompVFPU.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64CompReplace.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64Jit.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64RegCache.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64RegCacheFPU.cpp \
|
||||
$(COREDIR)/Util/DisArm64.cpp \
|
||||
$(GPUCOMMONDIR)/VertexDecoderArm64.cpp
|
||||
|
||||
ifeq ($(HAVE_NEON),1)
|
||||
SOURCES_CXX += \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompVFPUNEON.cpp \
|
||||
$(COREDIR)/MIPS/ARM/ArmCompVFPUNEONUtil.cpp \
|
||||
$(COMMONDIR)/ColorConvNEON.cpp \
|
||||
$(GPUDIR)/Common/TextureDecoderNEON.cpp
|
||||
|
||||
SOURCES_C += $(NATIVEDIR)/ext/libpng17/arm/arm_init.c \
|
||||
$(NATIVEDIR)/ext/libpng17/arm/filter_neon_intrinsics.c
|
||||
ASMFILES += $(NATIVEDIR)/math/fast/fast_matrix_neon.S \
|
||||
$(NATIVEDIR)/ext/libpng17/arm/filter_neon.S
|
||||
endif
|
||||
else
|
||||
ifneq (,$(findstring msvc,$(platform)))
|
||||
ifeq (,$(findstring x64,$(platform)))
|
||||
CPUFLAGS += /arch:SSE2
|
||||
endif
|
||||
CPUFLAGS += -D_M_IX86_FP
|
||||
else
|
||||
CPUFLAGS += -msse -msse2
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),x86_64)
|
||||
CPUFLAGS += -D_M_X64 -D_ARCH_64
|
||||
else
|
||||
CPUFLAGS += -D_M_IX86 -D_ARCH_32
|
||||
ifeq (,$(findstring msvc,$(platform)))
|
||||
CPUFLAGS += -m32
|
||||
endif
|
||||
endif
|
||||
SOURCES_CXX += $(GPUDIR)/Software/SamplerX86.cpp
|
||||
SOURCES_CXX += $(COMMONDIR)/x64Emitter.cpp \
|
||||
$(COMMONDIR)/ABI.cpp \
|
||||
$(COMMONDIR)/Thunk.cpp \
|
||||
$(COMMONDIR)/CPUDetect.cpp \
|
||||
$(COREDIR)/MIPS/x86/CompReplace.cpp \
|
||||
$(COREDIR)/MIPS/x86/CompBranch.cpp \
|
||||
$(COREDIR)/MIPS/x86/Asm.cpp \
|
||||
$(COREDIR)/MIPS/x86/CompALU.cpp \
|
||||
$(COREDIR)/MIPS/x86/CompVFPU.cpp \
|
||||
$(COREDIR)/MIPS/x86/CompLoadStore.cpp \
|
||||
$(COREDIR)/MIPS/x86/CompFPU.cpp \
|
||||
$(COREDIR)/MIPS/x86/Jit.cpp \
|
||||
$(COREDIR)/MIPS/x86/JitSafeMem.cpp \
|
||||
$(COREDIR)/MIPS/x86/RegCache.cpp \
|
||||
$(COREDIR)/MIPS/x86/RegCacheFPU.cpp \
|
||||
$(GPUDIR)/Common/VertexDecoderX86.cpp
|
||||
SOURCES_C += $(NATIVEDIR)/math/fast/fast_matrix_sse.c
|
||||
endif
|
||||
endif
|
||||
SOURCES_CXX += \
|
||||
$(COMMONDIR)/ArmEmitter.cpp \
|
||||
$(COMMONDIR)/Arm64Emitter.cpp \
|
||||
$(COREDIR)/Util/DisArm64.cpp
|
||||
|
||||
#UDIS86
|
||||
# Compiled and linked even on ARM for now
|
||||
|
||||
SOURCES_C += $(EXTDIR)/udis86/decode.c \
|
||||
$(EXTDIR)/udis86/itab.c \
|
||||
$(EXTDIR)/udis86/syn-intel.c \
|
||||
$(EXTDIR)/udis86/syn.c \
|
||||
$(EXTDIR)/udis86/udis86.c
|
||||
|
||||
ifeq ($(PLATFORM_EXT), darwin)
|
||||
COREFLAGS += -DNO_VULKAN
|
||||
else
|
||||
SOURCES_CXX += \
|
||||
$(COMMONDIR)/Vulkan/SPIRVDisasm.cpp \
|
||||
$(COMMONDIR)/Vulkan/VulkanContext.cpp \
|
||||
$(COMMONDIR)/Vulkan/VulkanDebug.cpp \
|
||||
$(COMMONDIR)/Vulkan/VulkanImage.cpp \
|
||||
$(COMMONDIR)/Vulkan/VulkanLoader.cpp \
|
||||
$(COMMONDIR)/Vulkan/VulkanMemory.cpp \
|
||||
$(NATIVEDIR)/thin3d/thin3d_vulkan.cpp \
|
||||
$(NATIVEDIR)/thin3d/VulkanRenderManager.cpp \
|
||||
$(NATIVEDIR)/thin3d/VulkanQueueRunner.cpp \
|
||||
$(GPUDIR)/Vulkan/DepalettizeShaderVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/DrawEngineVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/FragmentShaderGeneratorVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/FramebufferVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/GPU_Vulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/PipelineManagerVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/ShaderManagerVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/StateMappingVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/StencilBufferVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/TextureCacheVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/TextureScalerVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/VertexShaderGeneratorVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/VulkanUtil.cpp \
|
||||
$(LIBRETRODIR)/LibretroVulkanContext.cpp \
|
||||
$(LIBRETRODIR)/libretro_vulkan.cpp
|
||||
|
||||
ifeq ($(PLATFORM_EXT), unix)
|
||||
COREFLAGS += -DVK_USE_PLATFORM_XLIB_KHR
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM_EXT), win32)
|
||||
SOURCES_CXX += \
|
||||
$(GPUDIR)/Directx9/DepalettizeShaderDX9.cpp \
|
||||
$(GPUDIR)/Directx9/DrawEngineDX9.cpp \
|
||||
$(GPUDIR)/Directx9/PixelShaderGeneratorDX9.cpp \
|
||||
$(GPUDIR)/Directx9/FramebufferDX9.cpp \
|
||||
$(GPUDIR)/Directx9/GPU_DX9.cpp \
|
||||
$(GPUDIR)/Directx9/ShaderManagerDX9.cpp \
|
||||
$(GPUDIR)/Directx9/StateMappingDX9.cpp \
|
||||
$(GPUDIR)/Directx9/StencilBufferDX9.cpp \
|
||||
$(GPUDIR)/Directx9/TextureCacheDX9.cpp \
|
||||
$(GPUDIR)/Directx9/TextureScalerDX9.cpp \
|
||||
$(GPUDIR)/Directx9/VertexShaderGeneratorDX9.cpp
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(GPUDIR)/D3D11/DepalettizeShaderD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/DrawEngineD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/FragmentShaderGeneratorD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/FramebufferManagerD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/GPU_D3D11.cpp \
|
||||
$(GPUDIR)/D3D11/D3D11Util.cpp \
|
||||
$(GPUDIR)/D3D11/ShaderManagerD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/StateMappingD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/StencilBufferD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/TextureCacheD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/TextureScalerD3D11.cpp \
|
||||
$(GPUDIR)/D3D11/VertexShaderGeneratorD3D11.cpp
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(NATIVEDIR)/thin3d/thin3d_d3d9.cpp \
|
||||
$(NATIVEDIR)/thin3d/thin3d_d3d11.cpp \
|
||||
$(NATIVEDIR)/thin3d/d3dx9_loader.cpp \
|
||||
$(NATIVEDIR)/thin3d/d3d11_loader.cpp
|
||||
|
||||
INCFLAGS += -I$(CORE_DIR)/dx9sdk/Include -I$(CORE_DIR)/dx9sdk/Include/DX11
|
||||
|
||||
endif
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(LIBRETRODIR)/libretro.cpp \
|
||||
$(LIBRETRODIR)/LibretroGraphicsContext.cpp \
|
||||
$(LIBRETRODIR)/LibretroGLContext.cpp
|
||||
|
||||
ifneq ($(STATIC_LINKING), 1)
|
||||
SOURCES_C += \
|
||||
$(EXTDIR)/zlib/adler32.c \
|
||||
$(EXTDIR)/zlib/compress.c \
|
||||
$(EXTDIR)/zlib/crc32.c \
|
||||
$(EXTDIR)/zlib/deflate.c \
|
||||
$(EXTDIR)/zlib/gzclose.c \
|
||||
$(EXTDIR)/zlib/gzlib.c \
|
||||
$(EXTDIR)/zlib/gzread.c \
|
||||
$(EXTDIR)/zlib/gzwrite.c \
|
||||
$(EXTDIR)/zlib/inffast.c \
|
||||
$(EXTDIR)/zlib/inflate.c \
|
||||
$(EXTDIR)/zlib/inftrees.c \
|
||||
$(EXTDIR)/zlib/trees.c \
|
||||
$(EXTDIR)/zlib/uncompr.c \
|
||||
$(EXTDIR)/zlib/zutil.c
|
||||
endif
|
||||
|
||||
GIT_VERSION_SRC = $(CORE_DIR)/git-version.cpp
|
||||
GIT_VERSION := $(shell git describe --always || echo v1.4.2-git)
|
||||
GIT_VERSION_NO_UPDATE = $(findstring 1,$(shell grep PPSSPP_GIT_VERSION_NO_UPDATE $(GIT_VERSION_SRC)))
|
||||
ifneq (,$(findstring $(GIT_VERSION),$(shell grep char $(GIT_VERSION_SRC))))
|
||||
GIT_VERSION_NO_UPDATE = 1
|
||||
endif
|
||||
|
||||
ifneq ($(GIT_VERSION_NO_UPDATE),1)
|
||||
$(shell echo '// This is a generated file.' > $(GIT_VERSION_SRC))
|
||||
$(shell echo >> $(GIT_VERSION_SRC))
|
||||
$(shell echo 'const char *PPSSPP_GIT_VERSION = "${GIT_VERSION}";' >> $(GIT_VERSION_SRC))
|
||||
$(shell echo >> $(GIT_VERSION_SRC))
|
||||
$(shell echo "// If you don't want this file to update/recompile, change to 1." >> $(GIT_VERSION_SRC))
|
||||
$(shell echo '#define PPSSPP_GIT_VERSION_NO_UPDATE 0' >> $(GIT_VERSION_SRC))
|
||||
endif
|
||||
|
||||
SOURCES_CXX += $(GIT_VERSION_SRC)
|
82
libretro/jni/Android.mk
Normal file
82
libretro/jni/Android.mk
Normal file
@ -0,0 +1,82 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
GIT_VERSION := " $(shell git rev-parse --short HEAD || echo unknown)"
|
||||
ifneq ($(GIT_VERSION)," unknown")
|
||||
COREFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
|
||||
endif
|
||||
|
||||
COREFLAGS :=
|
||||
CORE_DIR := ../..
|
||||
FFMPEGDIR := $(CORE_DIR)/ffmpeg
|
||||
FFMPEGLIBS += libavformat libavcodec libavutil libswresample libswscale
|
||||
|
||||
ifeq ($(TARGET_ARCH),arm64)
|
||||
COREFLAGS += -DARM64 -D_ARCH_64
|
||||
HAVE_NEON := 1
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/android/arm64/lib
|
||||
FFMPEGINCFLAGS := -I$(FFMPEGDIR)/android/arm64/include
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
COREFLAGS += -DARM -DARMEABI_V7A -D__arm__ -DARM_ASM -D_ARCH_32 -mfpu=neon
|
||||
HAVE_NEON := 1
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/android/armv7/lib
|
||||
FFMPEGINCFLAGS := -I$(FFMPEGDIR)/android/armv7/include
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH),x86)
|
||||
COREFLAGS += -D_ARCH_32 -D_M_IX86 -fomit-frame-pointer -mtune=atom -mfpmath=sse -mssse3 -mstackrealign
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/android/x86/lib
|
||||
FFMPEGINCFLAGS := -I$(FFMPEGDIR)/android/x86/include
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH),x86_64)
|
||||
COREFLAGS += -D_ARCH_64 -D_M_X64 -fomit-frame-pointer -mtune=atom -mfpmath=sse -mssse3 -mstackrealign
|
||||
FFMPEGLIBDIR := $(FFMPEGDIR)/android/x86_64/lib
|
||||
FFMPEGINCFLAGS := -I$(FFMPEGDIR)/android/x86_64/include
|
||||
endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libavformat
|
||||
LOCAL_SRC_FILES := $(FFMPEGLIBDIR)/libavformat.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libavcodec
|
||||
LOCAL_SRC_FILES := $(FFMPEGLIBDIR)/libavcodec.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libavutil
|
||||
LOCAL_SRC_FILES := $(FFMPEGLIBDIR)/libavutil.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libswresample
|
||||
LOCAL_SRC_FILES := $(FFMPEGLIBDIR)/libswresample.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libswscale
|
||||
LOCAL_SRC_FILES := $(FFMPEGLIBDIR)/libswscale.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
PLATFORM_EXT := android
|
||||
platform := android
|
||||
GLES = 1
|
||||
|
||||
LOCAL_MODULE := retro
|
||||
|
||||
include $(CORE_DIR)/libretro/Makefile.common
|
||||
|
||||
COREFLAGS += -DINLINE="inline" -DPPSSPP -DUSE_FFMPEG -DMOBILE_DEVICE -DBAKE_IN_GIT -DDYNAREC -D__LIBRETRO__ -DUSING_GLES2 -D__STDC_CONSTANT_MACROS -DGLEW_NO_GLU -DNO_VULKAN $(INCFLAGS)
|
||||
LOCAL_SRC_FILES = $(SOURCES_CXX) $(SOURCES_C) $(ASMFILES)
|
||||
LOCAL_CPPFLAGS := -Wall -std=gnu++11 $(COREFLAGS) -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
|
||||
LOCAL_CFLAGS := -O2 -ffast-math -DANDROID $(COREFLAGS)
|
||||
LOCAL_LDLIBS += -lz -llog -lGLESv2 -lEGL -latomic
|
||||
LOCAL_STATIC_LIBRARIES += $(FFMPEGLIBS)
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
5
libretro/jni/Application.mk
Normal file
5
libretro/jni/Application.mk
Normal file
@ -0,0 +1,5 @@
|
||||
NDK_TOOLCHAIN_VERSION ?= 4.8
|
||||
APP_ABI := armeabi-v7a,arm64-v8a,x86
|
||||
APP_STL := c++_static
|
||||
APP_GNUSTL_CPP_FEATURES := exceptions
|
||||
APP_PLATFORM := android-9
|
792
libretro/libretro.cpp
Normal file
792
libretro/libretro.cpp
Normal file
@ -0,0 +1,792 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include "base/timeutil.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HLE/sceCtrl.h"
|
||||
#include "Core/HLE/sceUtility.h"
|
||||
#include "Core/HLE/__sceAudio.h"
|
||||
#include "Core/HW/MemoryStick.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/System.h"
|
||||
#include "Log.h"
|
||||
#include "LogManager.h"
|
||||
#include "ConsoleListener.h"
|
||||
#include "file/vfs.h"
|
||||
#include "file/zip_read.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Common/TextureScalerCommon.h"
|
||||
#include "input/input_state.h"
|
||||
#include "base/NativeApp.h"
|
||||
#include "thread/threadutil.h"
|
||||
|
||||
#include "libretro/libretro.h"
|
||||
#include "libretro/LibretroGraphicsContext.h"
|
||||
|
||||
#define DIR_SEP "/"
|
||||
#ifdef _WIN32
|
||||
#define DIR_SEP_CHRS "/\\"
|
||||
#else
|
||||
#define DIR_SEP_CHRS "/"
|
||||
#endif
|
||||
|
||||
#define SAMPLERATE 44100
|
||||
|
||||
namespace Libretro {
|
||||
LibretroGraphicsContext *ctx;
|
||||
retro_environment_t environ_cb;
|
||||
static retro_audio_sample_batch_t audio_batch_cb;
|
||||
static retro_input_poll_t input_poll_cb;
|
||||
static retro_input_state_t input_state_cb;
|
||||
} // namespace Libretro
|
||||
|
||||
using namespace Libretro;
|
||||
|
||||
class LibretroHost : public Host {
|
||||
public:
|
||||
LibretroHost() {}
|
||||
bool InitGraphics(std::string *error_message, GraphicsContext **ctx) override { return true; }
|
||||
void ShutdownGraphics() override {}
|
||||
void InitSound() override {}
|
||||
void UpdateSound() override
|
||||
{
|
||||
extern int hostAttemptBlockSize;
|
||||
const int blockSizeMax = 512;
|
||||
static int16_t audio[blockSizeMax * 2];
|
||||
assert(hostAttemptBlockSize <= blockSizeMax);
|
||||
|
||||
int samples = __AudioMix(audio, hostAttemptBlockSize, SAMPLERATE);
|
||||
audio_batch_cb(audio, samples);
|
||||
}
|
||||
void ShutdownSound() override {}
|
||||
bool IsDebuggingEnabled() override { return false; }
|
||||
bool AttemptLoadSymbolMap() override { return false; }
|
||||
};
|
||||
|
||||
class PrintfLogger : public LogListener {
|
||||
public:
|
||||
PrintfLogger(retro_log_callback log) : log_(log.log) {}
|
||||
void Log(const LogMessage &message)
|
||||
{
|
||||
switch (message.level)
|
||||
{
|
||||
case LogTypes::LVERBOSE:
|
||||
case LogTypes::LDEBUG:
|
||||
log_(RETRO_LOG_DEBUG, "[%s] %s", message.log, message.msg.c_str());
|
||||
return;
|
||||
|
||||
case LogTypes::LERROR:
|
||||
log_(RETRO_LOG_ERROR, "[%s] %s", message.log, message.msg.c_str());
|
||||
return;
|
||||
|
||||
case LogTypes::LNOTICE:
|
||||
case LogTypes::LWARNING:
|
||||
log_(RETRO_LOG_WARN, "[%s] %s", message.log, message.msg.c_str());
|
||||
return;
|
||||
|
||||
case LogTypes::LINFO:
|
||||
default:
|
||||
log_(RETRO_LOG_INFO, "[%s] %s", message.log, message.msg.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
retro_log_printf_t log_;
|
||||
};
|
||||
static PrintfLogger *printfLogger;
|
||||
|
||||
template <typename T> class RetroOption {
|
||||
public:
|
||||
RetroOption(const char *id, const char *name, std::initializer_list<std::pair<const char *, T>> list) : id_(id), name_(name), list_(list.begin(), list.end()) {}
|
||||
RetroOption(const char *id, const char *name, std::initializer_list<const char *> list) : id_(id), name_(name)
|
||||
{
|
||||
for (auto option : list)
|
||||
list_.push_back({ option, (T)list_.size() });
|
||||
}
|
||||
RetroOption(const char *id, const char *name, T first, std::initializer_list<const char *> list) : id_(id), name_(name)
|
||||
{
|
||||
for (auto option : list)
|
||||
list_.push_back({ option, first + (int)list_.size() });
|
||||
}
|
||||
RetroOption(const char *id, const char *name, T first, int count, int step = 1) : id_(id), name_(name)
|
||||
{
|
||||
for (T i = first; i < first + count; i += step)
|
||||
list_.push_back({ std::to_string(i), i });
|
||||
}
|
||||
RetroOption(const char *id, const char *name, bool initial) : id_(id), name_(name)
|
||||
{
|
||||
list_.push_back({ initial ? "enabled" : "disabled", initial });
|
||||
list_.push_back({ !initial ? "enabled" : "disabled", !initial });
|
||||
}
|
||||
retro_variable GetOptions()
|
||||
{
|
||||
if (options_.empty())
|
||||
{
|
||||
options_ = name_;
|
||||
options_.push_back(';');
|
||||
for (auto &option : list_)
|
||||
{
|
||||
if (option.first == list_.begin()->first)
|
||||
options_ += std::string(" ") + option.first;
|
||||
else
|
||||
options_ += std::string("|") + option.first;
|
||||
}
|
||||
}
|
||||
return { id_, options_.c_str() };
|
||||
}
|
||||
bool Update(T *dest)
|
||||
{
|
||||
retro_variable var{ id_ };
|
||||
T val = list_.front().second;
|
||||
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||
{
|
||||
for (auto option : list_)
|
||||
{
|
||||
if (option.first == var.value)
|
||||
{
|
||||
val = option.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*dest != val)
|
||||
{
|
||||
*dest = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *id_;
|
||||
const char *name_;
|
||||
std::string options_;
|
||||
std::vector<std::pair<std::string, T>> list_;
|
||||
};
|
||||
|
||||
static RetroOption<CPUCore> ppsspp_cpu_core("ppsspp_cpu_core", "CPU Core", { { "jit", CPUCore::JIT }, { "IR jit", CPUCore::IR_JIT }, { "interpreter", CPUCore::INTERPRETER } });
|
||||
static RetroOption<int> ppsspp_locked_cpu_speed("ppsspp_locked_cpu_speed", "Locked CPU Speed", { { "off", 0 }, { "222MHz", 222 }, { "266MHz", 266 }, { "333MHz", 333 } });
|
||||
static RetroOption<int> ppsspp_language("ppsspp_language", "Language", { { "automatic", -1 }, { "english", PSP_SYSTEMPARAM_LANGUAGE_ENGLISH }, { "japanese", PSP_SYSTEMPARAM_LANGUAGE_JAPANESE }, { "french", PSP_SYSTEMPARAM_LANGUAGE_FRENCH }, { "spanish", PSP_SYSTEMPARAM_LANGUAGE_SPANISH }, { "german", PSP_SYSTEMPARAM_LANGUAGE_GERMAN }, { "italian", PSP_SYSTEMPARAM_LANGUAGE_ITALIAN }, { "dutch", PSP_SYSTEMPARAM_LANGUAGE_DUTCH }, { "portuguese", PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE }, { "russian", PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN }, { "korean", PSP_SYSTEMPARAM_LANGUAGE_KOREAN }, { "chinese_traditional", PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL }, { "chinese_simplified", PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED } });
|
||||
static RetroOption<int> ppsspp_rendering_mode("ppsspp_rendering_mode", "Rendering Mode", { { "buffered", FB_BUFFERED_MODE }, { "nonbuffered", FB_NON_BUFFERED_MODE } });
|
||||
static RetroOption<bool> ppsspp_true_color("ppsspp_true_color", "True Color Depth", true);
|
||||
static RetroOption<bool> ppsspp_auto_frameskip("ppsspp_auto_frameskip", "Auto Frameskip", false);
|
||||
static RetroOption<int> ppsspp_frameskip("ppsspp_frameskip", "Frameskip", 0, 10);
|
||||
static RetroOption<int> ppsspp_force_max_fps("ppsspp_force_max_fps", "Force Max FPS", { { "disabled", 0 }, { "enabled", 60 } });
|
||||
static RetroOption<int> ppsspp_audio_latency("ppsspp_audio_latency", "Audio latency", { "low", "medium", "high" });
|
||||
static RetroOption<int> ppsspp_internal_resolution("ppsspp_internal_resolution", "Internal Resolution", 1, { "480x272", "960x544", "1440x816", "1920x1088", "2400x1360", "2880x1632", "3360x1904", "3840x2176", "4320x2448", "4800x2720" });
|
||||
static RetroOption<int> ppsspp_button_preference("ppsspp_button_preference", "Confirmation Button", { { "cross", PSP_SYSTEMPARAM_BUTTON_CROSS }, { "circle", PSP_SYSTEMPARAM_BUTTON_CIRCLE } });
|
||||
static RetroOption<bool> ppsspp_fast_memory("ppsspp_fast_memory", "Fast Memory (Speedhack)", true);
|
||||
static RetroOption<bool> ppsspp_block_transfer_gpu("ppsspp_block_transfer_gpu", "Block Transfer GPU", true);
|
||||
static RetroOption<int> ppsspp_texture_scaling_level("ppsspp_texture_scaling_level", "Texture Scaling Level", { { "1", 1 }, { "2", 2 }, { "3", 3 }, { "4", 4 }, { "5", 5 }, { "0", 0 } });
|
||||
static RetroOption<int> ppsspp_texture_scaling_type("ppsspp_texture_scaling_type", "Texture Scaling Type", { { "xbrz", TextureScalerCommon::XBRZ }, { "hybrid", TextureScalerCommon::HYBRID }, { "bicubic", TextureScalerCommon::BICUBIC }, { "hybrid_bicubic", TextureScalerCommon::HYBRID_BICUBIC } });
|
||||
static RetroOption<int> ppsspp_texture_anisotropic_filtering("ppsspp_texture_anisotropic_filtering", "Anisotropic Filtering", { "off", "1x", "2x", "4x", "8x", "16x" });
|
||||
static RetroOption<bool> ppsspp_texture_deposterize("ppsspp_texture_deposterize", "Texture Deposterize", false);
|
||||
static RetroOption<bool> ppsspp_gpu_hardware_transform("ppsspp_gpu_hardware_transform", "GPU Hardware T&L", true);
|
||||
static RetroOption<bool> ppsspp_vertex_cache("ppsspp_vertex_cache", "Vertex Cache (Speedhack)", true);
|
||||
static RetroOption<bool> ppsspp_separate_io_thread("ppsspp_separate_io_thread", "IO Threading", false);
|
||||
static RetroOption<bool> ppsspp_unsafe_func_replacements("ppsspp_unsafe_func_replacements", "Unsafe FuncReplacements", true);
|
||||
static RetroOption<bool> ppsspp_sound_speedhack("ppsspp_sound_speedhack", "Sound Speedhack", false);
|
||||
static RetroOption<bool> ppsspp_cheats("ppsspp_cheats", "Internal Cheats Support", false);
|
||||
|
||||
void retro_set_environment(retro_environment_t cb)
|
||||
{
|
||||
std::vector<retro_variable> vars;
|
||||
vars.push_back(ppsspp_cpu_core.GetOptions());
|
||||
vars.push_back(ppsspp_locked_cpu_speed.GetOptions());
|
||||
vars.push_back(ppsspp_language.GetOptions());
|
||||
vars.push_back(ppsspp_rendering_mode.GetOptions());
|
||||
vars.push_back(ppsspp_true_color.GetOptions());
|
||||
vars.push_back(ppsspp_auto_frameskip.GetOptions());
|
||||
vars.push_back(ppsspp_frameskip.GetOptions());
|
||||
vars.push_back(ppsspp_force_max_fps.GetOptions());
|
||||
vars.push_back(ppsspp_audio_latency.GetOptions());
|
||||
vars.push_back(ppsspp_internal_resolution.GetOptions());
|
||||
vars.push_back(ppsspp_button_preference.GetOptions());
|
||||
vars.push_back(ppsspp_fast_memory.GetOptions());
|
||||
vars.push_back(ppsspp_block_transfer_gpu.GetOptions());
|
||||
vars.push_back(ppsspp_texture_scaling_level.GetOptions());
|
||||
vars.push_back(ppsspp_texture_scaling_type.GetOptions());
|
||||
vars.push_back(ppsspp_texture_anisotropic_filtering.GetOptions());
|
||||
vars.push_back(ppsspp_texture_deposterize.GetOptions());
|
||||
vars.push_back(ppsspp_gpu_hardware_transform.GetOptions());
|
||||
vars.push_back(ppsspp_vertex_cache.GetOptions());
|
||||
vars.push_back(ppsspp_separate_io_thread.GetOptions());
|
||||
vars.push_back(ppsspp_unsafe_func_replacements.GetOptions());
|
||||
vars.push_back(ppsspp_sound_speedhack.GetOptions());
|
||||
vars.push_back(ppsspp_cheats.GetOptions());
|
||||
vars.push_back({});
|
||||
|
||||
environ_cb = cb;
|
||||
|
||||
cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars.data());
|
||||
}
|
||||
|
||||
static int get_language_auto(void)
|
||||
{
|
||||
retro_language val = RETRO_LANGUAGE_ENGLISH;
|
||||
environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &val);
|
||||
|
||||
switch (val)
|
||||
{
|
||||
default:
|
||||
case RETRO_LANGUAGE_ENGLISH:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
||||
case RETRO_LANGUAGE_JAPANESE:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;
|
||||
case RETRO_LANGUAGE_FRENCH:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_FRENCH;
|
||||
case RETRO_LANGUAGE_GERMAN:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_GERMAN;
|
||||
case RETRO_LANGUAGE_SPANISH:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_SPANISH;
|
||||
case RETRO_LANGUAGE_ITALIAN:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;
|
||||
case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:
|
||||
case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;
|
||||
case RETRO_LANGUAGE_RUSSIAN:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;
|
||||
case RETRO_LANGUAGE_DUTCH:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_DUTCH;
|
||||
case RETRO_LANGUAGE_KOREAN:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_KOREAN;
|
||||
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;
|
||||
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
static void check_variables(CoreParameter &coreParam)
|
||||
{
|
||||
bool updated = false;
|
||||
|
||||
if (coreState != CORE_POWERUP && environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && !updated)
|
||||
return;
|
||||
|
||||
ppsspp_button_preference.Update(&g_Config.iButtonPreference);
|
||||
ppsspp_fast_memory.Update(&g_Config.bFastMemory);
|
||||
ppsspp_vertex_cache.Update(&g_Config.bVertexCache);
|
||||
ppsspp_gpu_hardware_transform.Update(&g_Config.bHardwareTransform);
|
||||
ppsspp_frameskip.Update(&g_Config.iFrameSkip);
|
||||
ppsspp_audio_latency.Update(&g_Config.iAudioLatency);
|
||||
ppsspp_true_color.Update(&g_Config.bTrueColor);
|
||||
ppsspp_auto_frameskip.Update(&g_Config.bAutoFrameSkip);
|
||||
ppsspp_block_transfer_gpu.Update(&g_Config.bBlockTransferGPU);
|
||||
ppsspp_texture_anisotropic_filtering.Update(&g_Config.iAnisotropyLevel);
|
||||
ppsspp_texture_deposterize.Update(&g_Config.bTexDeposterize);
|
||||
ppsspp_separate_io_thread.Update(&g_Config.bSeparateIOThread);
|
||||
ppsspp_unsafe_func_replacements.Update(&g_Config.bFuncReplacements);
|
||||
ppsspp_sound_speedhack.Update(&g_Config.bSoundSpeedHack);
|
||||
ppsspp_cheats.Update(&g_Config.bEnableCheats);
|
||||
ppsspp_locked_cpu_speed.Update(&g_Config.iLockedCPUSpeed);
|
||||
ppsspp_rendering_mode.Update(&g_Config.iRenderingMode);
|
||||
ppsspp_force_max_fps.Update(&g_Config.iForceMaxEmulatedFPS);
|
||||
ppsspp_cpu_core.Update((CPUCore *)&g_Config.iCpuCore);
|
||||
|
||||
ppsspp_language.Update(&g_Config.iLanguage);
|
||||
if (g_Config.iLanguage < 0)
|
||||
g_Config.iLanguage = get_language_auto();
|
||||
|
||||
if (ppsspp_internal_resolution.Update(&g_Config.iInternalResolution))
|
||||
{
|
||||
coreParam.pixelWidth = coreParam.renderWidth = g_Config.iInternalResolution * 480;
|
||||
coreParam.pixelHeight = coreParam.renderHeight = g_Config.iInternalResolution * 272;
|
||||
if (gpu)
|
||||
{
|
||||
retro_system_av_info av_info;
|
||||
retro_get_system_av_info(&av_info);
|
||||
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &av_info);
|
||||
gpu->Resized();
|
||||
}
|
||||
}
|
||||
|
||||
if (ppsspp_texture_scaling_type.Update(&g_Config.iTexScalingType) && gpu)
|
||||
gpu->ClearCacheNextFrame();
|
||||
|
||||
if (ppsspp_texture_scaling_level.Update(&g_Config.iTexScalingLevel) && gpu)
|
||||
gpu->ClearCacheNextFrame();
|
||||
}
|
||||
|
||||
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
|
||||
void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
|
||||
void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
|
||||
void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
|
||||
|
||||
void retro_init(void)
|
||||
{
|
||||
#if 0
|
||||
g_Config.Load("");
|
||||
#endif
|
||||
|
||||
g_Config.bEnableLogging = true;
|
||||
g_Config.bFrameSkipUnthrottle = false;
|
||||
g_Config.bMemStickInserted = PSP_MEMORYSTICK_STATE_INSERTED;
|
||||
g_Config.iGlobalVolume = VOLUME_MAX - 1;
|
||||
g_Config.bEnableSound = true;
|
||||
g_Config.bAudioResampler = false;
|
||||
g_Config.iCwCheatRefreshRate = 60;
|
||||
|
||||
LogManager::Init();
|
||||
|
||||
host = new LibretroHost;
|
||||
|
||||
struct retro_log_callback log;
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
|
||||
{
|
||||
printfLogger = new PrintfLogger(log);
|
||||
LogManager *logman = LogManager::GetInstance();
|
||||
logman->RemoveListener(logman->GetConsoleListener());
|
||||
logman->RemoveListener(logman->GetDebuggerListener());
|
||||
logman->ChangeFileLog(nullptr);
|
||||
logman->AddListener(printfLogger);
|
||||
#if 1
|
||||
logman->SetAllLogLevels(LogTypes::LINFO);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void retro_deinit(void)
|
||||
{
|
||||
LogManager::Shutdown();
|
||||
|
||||
delete printfLogger;
|
||||
printfLogger = nullptr;
|
||||
|
||||
delete host;
|
||||
host = nullptr;
|
||||
}
|
||||
|
||||
void retro_set_controller_port_device(unsigned port, unsigned device)
|
||||
{
|
||||
(void)port;
|
||||
(void)device;
|
||||
}
|
||||
|
||||
void retro_get_system_info(struct retro_system_info *info)
|
||||
{
|
||||
*info = {};
|
||||
info->library_name = "PPSSPP";
|
||||
info->library_version = PPSSPP_GIT_VERSION;
|
||||
info->need_fullpath = true;
|
||||
info->valid_extensions = "elf|iso|cso|prx|pbp";
|
||||
}
|
||||
|
||||
void retro_get_system_av_info(struct retro_system_av_info *info)
|
||||
{
|
||||
*info = {};
|
||||
info->timing.fps = 60.0f / 1.001f;
|
||||
info->timing.sample_rate = SAMPLERATE;
|
||||
|
||||
info->geometry.base_width = g_Config.iInternalResolution * 480;
|
||||
info->geometry.base_height = g_Config.iInternalResolution * 272;
|
||||
info->geometry.max_width = g_Config.iInternalResolution * 480;
|
||||
info->geometry.max_height = g_Config.iInternalResolution * 272;
|
||||
info->geometry.aspect_ratio = 16.0 / 9.0;
|
||||
}
|
||||
|
||||
unsigned retro_api_version(void) { return RETRO_API_VERSION; }
|
||||
namespace Libretro {
|
||||
|
||||
bool useEmuThread = false;
|
||||
std::atomic<EmuThreadState> emuThreadState(EmuThreadState::DISABLED);
|
||||
|
||||
static std::thread emuThread;
|
||||
static void EmuFrame()
|
||||
{
|
||||
ctx->SetRenderTarget();
|
||||
if (ctx->GetDrawContext())
|
||||
ctx->GetDrawContext()->BeginFrame();
|
||||
|
||||
gpu->BeginHostFrame();
|
||||
|
||||
coreState = CORE_RUNNING;
|
||||
PSP_RunLoopUntil(UINT64_MAX);
|
||||
|
||||
gpu->EndHostFrame();
|
||||
|
||||
if (ctx->GetDrawContext())
|
||||
ctx->GetDrawContext()->EndFrame();
|
||||
}
|
||||
|
||||
static void EmuThreadFunc()
|
||||
{
|
||||
setCurrentThreadName("Emu");
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch ((EmuThreadState)emuThreadState)
|
||||
{
|
||||
case EmuThreadState::START_REQUESTED:
|
||||
emuThreadState = EmuThreadState::RUNNING;
|
||||
/* fallthrough */
|
||||
case EmuThreadState::RUNNING:
|
||||
EmuFrame();
|
||||
break;
|
||||
case EmuThreadState::PAUSE_REQUESTED:
|
||||
emuThreadState = EmuThreadState::PAUSED;
|
||||
/* fallthrough */
|
||||
case EmuThreadState::PAUSED:
|
||||
sleep_ms(1);
|
||||
break;
|
||||
default:
|
||||
case EmuThreadState::QUIT_REQUESTED:
|
||||
emuThreadState = EmuThreadState::STOPPED;
|
||||
ctx->StopThread();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmuThreadStart()
|
||||
{
|
||||
bool wasPaused = emuThreadState == EmuThreadState::PAUSED;
|
||||
emuThreadState = EmuThreadState::START_REQUESTED;
|
||||
|
||||
if (!wasPaused)
|
||||
{
|
||||
ctx->ThreadStart();
|
||||
emuThread = std::thread(&EmuThreadFunc);
|
||||
}
|
||||
}
|
||||
|
||||
void EmuThreadStop()
|
||||
{
|
||||
if (emuThreadState != EmuThreadState::RUNNING)
|
||||
return;
|
||||
|
||||
emuThreadState = EmuThreadState::QUIT_REQUESTED;
|
||||
|
||||
while (ctx->ThreadFrame())
|
||||
{
|
||||
// Need to keep eating frames to allow the EmuThread to exit correctly.
|
||||
continue;
|
||||
}
|
||||
emuThread.join();
|
||||
emuThread = std::thread();
|
||||
ctx->ThreadEnd();
|
||||
}
|
||||
|
||||
void EmuThreadPause()
|
||||
{
|
||||
if (emuThreadState != EmuThreadState::RUNNING)
|
||||
return;
|
||||
emuThreadState = EmuThreadState::PAUSE_REQUESTED;
|
||||
ctx->ThreadFrame();
|
||||
while (emuThreadState != EmuThreadState::PAUSED)
|
||||
sleep_ms(1);
|
||||
}
|
||||
|
||||
} // namespace Libretro
|
||||
|
||||
bool retro_load_game(const struct retro_game_info *game)
|
||||
{
|
||||
struct retro_input_descriptor desc[] = {
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle" },
|
||||
{ 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_SELECT, "Select" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
||||
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Analog X" },
|
||||
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Analog Y" },
|
||||
{ 0 },
|
||||
};
|
||||
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
|
||||
|
||||
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
|
||||
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
|
||||
{
|
||||
ERROR_LOG(SYSTEM, "XRGB8888 is not supported.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *nickname = NULL;
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_USERNAME, &nickname) && nickname)
|
||||
g_Config.sNickName = std::string(nickname);
|
||||
|
||||
const char *dir_ptr = NULL;
|
||||
static std::string retro_base_dir;
|
||||
static std::string retro_save_dir;
|
||||
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir_ptr) && dir_ptr)
|
||||
{
|
||||
retro_base_dir = dir_ptr;
|
||||
// Make sure that we don't have any lingering slashes, etc, as they break Windows.
|
||||
size_t last = retro_base_dir.find_last_not_of(DIR_SEP_CHRS);
|
||||
if (last != std::string::npos)
|
||||
last++;
|
||||
|
||||
retro_base_dir = retro_base_dir.substr(0, last) + DIR_SEP;
|
||||
}
|
||||
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir_ptr) && dir_ptr)
|
||||
{
|
||||
retro_save_dir = dir_ptr;
|
||||
// Make sure that we don't have any lingering slashes, etc, as they break Windows.
|
||||
size_t last = retro_save_dir.find_last_not_of(DIR_SEP_CHRS);
|
||||
if (last != std::string::npos)
|
||||
last++;
|
||||
|
||||
retro_save_dir = retro_save_dir.substr(0, last) + DIR_SEP;
|
||||
}
|
||||
|
||||
if (retro_base_dir.empty())
|
||||
retro_base_dir = File::GetDir(game->path);
|
||||
|
||||
retro_base_dir += "PPSSPP" DIR_SEP;
|
||||
|
||||
if (retro_save_dir.empty())
|
||||
retro_save_dir = File::GetDir(game->path);
|
||||
|
||||
g_Config.currentDirectory = retro_base_dir;
|
||||
g_Config.externalDirectory = retro_base_dir;
|
||||
g_Config.memStickDirectory = retro_save_dir;
|
||||
g_Config.flash0Directory = retro_base_dir + "flash0" DIR_SEP;
|
||||
g_Config.internalDataDirectory = retro_base_dir;
|
||||
|
||||
VFSRegister("", new DirectoryAssetReader(retro_base_dir.c_str()));
|
||||
|
||||
coreState = CORE_POWERUP;
|
||||
ctx = LibretroGraphicsContext::CreateGraphicsContext();
|
||||
INFO_LOG(SYSTEM, "Using %s backend", ctx->Ident());
|
||||
|
||||
Core_SetGraphicsContext(ctx);
|
||||
SetGPUBackend((GPUBackend)g_Config.iGPUBackend);
|
||||
|
||||
useEmuThread = ctx->GetGPUCore() == GPUCORE_GLES;
|
||||
|
||||
CoreParameter coreParam = {};
|
||||
coreParam.enableSound = true;
|
||||
coreParam.fileToStart = std::string(game->path);
|
||||
coreParam.mountIso = "";
|
||||
coreParam.startPaused = false;
|
||||
coreParam.printfEmuLog = true;
|
||||
coreParam.headLess = true;
|
||||
coreParam.unthrottle = true;
|
||||
coreParam.graphicsContext = ctx;
|
||||
coreParam.gpuCore = ctx->GetGPUCore();
|
||||
coreParam.cpuCore = CPUCore::JIT;
|
||||
check_variables(coreParam);
|
||||
|
||||
#if 0
|
||||
g_Config.bVertexDecoderJit = (coreParam.cpuCore == CPU_JIT) ? true : false;
|
||||
#endif
|
||||
|
||||
std::string error_string;
|
||||
if (!PSP_InitStart(coreParam, &error_string))
|
||||
{
|
||||
ERROR_LOG(BOOT, "%s", error_string.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void retro_unload_game(void)
|
||||
{
|
||||
if (Libretro::useEmuThread)
|
||||
Libretro::EmuThreadStop();
|
||||
|
||||
PSP_Shutdown();
|
||||
VFSShutdown();
|
||||
|
||||
delete ctx;
|
||||
ctx = nullptr;
|
||||
PSP_CoreParameter().graphicsContext = nullptr;
|
||||
}
|
||||
|
||||
void retro_reset(void)
|
||||
{
|
||||
std::string error_string;
|
||||
|
||||
PSP_Shutdown();
|
||||
|
||||
#if 0
|
||||
coreState = CORE_POWERUP;
|
||||
if(!PSP_InitStart(PSP_CoreParameter(), &error_string))
|
||||
{
|
||||
ERROR_LOG(BOOT, "%s", error_string.c_str());
|
||||
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
|
||||
}
|
||||
#else
|
||||
if (!PSP_Init(PSP_CoreParameter(), &error_string))
|
||||
{
|
||||
ERROR_LOG(BOOT, "%s", error_string.c_str());
|
||||
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void retro_input(void)
|
||||
{
|
||||
// clang-format off
|
||||
static struct
|
||||
{
|
||||
u32 retro;
|
||||
u32 sceCtrl;
|
||||
} map[] = {
|
||||
{ RETRO_DEVICE_ID_JOYPAD_UP, CTRL_UP },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_DOWN, CTRL_DOWN },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_LEFT, CTRL_LEFT },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_RIGHT, CTRL_RIGHT },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_X, CTRL_TRIANGLE },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_A, CTRL_CIRCLE },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_B, CTRL_CROSS },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_Y, CTRL_SQUARE },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_L, CTRL_LTRIGGER },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_R, CTRL_RTRIGGER },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_START, CTRL_START },
|
||||
{ RETRO_DEVICE_ID_JOYPAD_SELECT, CTRL_SELECT },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
input_poll_cb();
|
||||
|
||||
for (int i = 0; i < sizeof(map) / sizeof(*map); i++)
|
||||
{
|
||||
if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, map[i].retro))
|
||||
__CtrlButtonDown(map[i].sceCtrl);
|
||||
else
|
||||
__CtrlButtonUp(map[i].sceCtrl);
|
||||
}
|
||||
|
||||
__CtrlSetAnalogX(input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 32768.0f);
|
||||
__CtrlSetAnalogY(input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / -32768.0f);
|
||||
}
|
||||
|
||||
void retro_run(void)
|
||||
{
|
||||
if (PSP_IsIniting())
|
||||
{
|
||||
std::string error_string;
|
||||
#if 0
|
||||
if(!PSP_InitUpdate(&error_string))
|
||||
{
|
||||
graphics_context->SwapBuffers();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
while (!PSP_InitUpdate(&error_string))
|
||||
sleep_ms(4);
|
||||
#endif
|
||||
|
||||
if (!PSP_IsInited())
|
||||
{
|
||||
ERROR_LOG(BOOT, "%s", error_string.c_str());
|
||||
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
check_variables(PSP_CoreParameter());
|
||||
|
||||
retro_input();
|
||||
|
||||
if (useEmuThread)
|
||||
{
|
||||
if (emuThreadState != EmuThreadState::RUNNING)
|
||||
EmuThreadStart();
|
||||
|
||||
if (!ctx->ThreadFrame())
|
||||
return;
|
||||
}
|
||||
else
|
||||
EmuFrame();
|
||||
|
||||
ctx->SwapBuffers();
|
||||
}
|
||||
|
||||
unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
|
||||
|
||||
bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num)
|
||||
{
|
||||
(void)type;
|
||||
(void)info;
|
||||
(void)num;
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace SaveState {
|
||||
struct SaveStart
|
||||
{
|
||||
void DoState(PointerWrap &p);
|
||||
};
|
||||
} // namespace SaveState
|
||||
|
||||
size_t retro_serialize_size(void)
|
||||
{
|
||||
SaveState::SaveStart state;
|
||||
return (CChunkFileReader::MeasurePtr(state) + 0x800000) & ~0x7FFFFF;
|
||||
}
|
||||
|
||||
bool retro_serialize(void *data, size_t size)
|
||||
{
|
||||
SaveState::SaveStart state;
|
||||
assert(CChunkFileReader::MeasurePtr(state) <= size);
|
||||
return CChunkFileReader::SavePtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE;
|
||||
}
|
||||
|
||||
bool retro_unserialize(const void *data, size_t size)
|
||||
{
|
||||
SaveState::SaveStart state;
|
||||
return CChunkFileReader::LoadPtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE;
|
||||
}
|
||||
|
||||
void *retro_get_memory_data(unsigned id)
|
||||
{
|
||||
(void)id;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t retro_get_memory_size(unsigned id)
|
||||
{
|
||||
(void)id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void retro_cheat_reset(void) {}
|
||||
|
||||
void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
||||
{
|
||||
(void)index;
|
||||
(void)enabled;
|
||||
(void)code;
|
||||
}
|
||||
|
||||
int System_GetPropertyInt(SystemProperty prop)
|
||||
{
|
||||
switch (prop)
|
||||
{
|
||||
case SYSPROP_AUDIO_SAMPLE_RATE:
|
||||
return SAMPLERATE;
|
||||
case SYSPROP_DISPLAY_REFRESH_RATE:
|
||||
return 60000;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string System_GetProperty(SystemProperty prop) { return ""; }
|
||||
void System_SendMessage(const char *command, const char *parameter) {}
|
||||
void NativeUpdate() {}
|
||||
void NativeRender(GraphicsContext *graphicsContext) {}
|
||||
void NativeResized() {}
|
||||
bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultvalue, std::wstring &outvalue) { return false; }
|
27
libretro/libretro.def
Normal file
27
libretro/libretro.def
Normal file
@ -0,0 +1,27 @@
|
||||
LIBRARY "libretro"
|
||||
EXPORTS
|
||||
retro_set_environment
|
||||
retro_set_video_refresh
|
||||
retro_set_audio_sample
|
||||
retro_set_audio_sample_batch
|
||||
retro_set_input_poll
|
||||
retro_set_input_state
|
||||
retro_init
|
||||
retro_deinit
|
||||
retro_api_version
|
||||
retro_get_system_info
|
||||
retro_get_system_av_info
|
||||
retro_set_controller_port_device
|
||||
retro_reset
|
||||
retro_run
|
||||
retro_serialize_size
|
||||
retro_serialize
|
||||
retro_unserialize
|
||||
retro_cheat_reset
|
||||
retro_cheat_set
|
||||
retro_load_game
|
||||
retro_load_game_special
|
||||
retro_unload_game
|
||||
retro_get_region
|
||||
retro_get_memory_data
|
||||
retro_get_memory_size
|
2182
libretro/libretro.h
Normal file
2182
libretro/libretro.h
Normal file
File diff suppressed because it is too large
Load Diff
450
libretro/libretro_vulkan.cpp
Normal file
450
libretro/libretro_vulkan.cpp
Normal file
@ -0,0 +1,450 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#define VK_NO_PROTOTYPES
|
||||
#include "libretro/libretro_vulkan.h"
|
||||
|
||||
static retro_hw_render_interface_vulkan *vulkan;
|
||||
|
||||
static struct
|
||||
{
|
||||
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;
|
||||
} vk_init_info;
|
||||
static bool DEDICATED_ALLOCATION;
|
||||
|
||||
extern PFN_vkCreateInstance vkCreateInstance;
|
||||
extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
|
||||
extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
||||
extern PFN_vkAllocateMemory vkAllocateMemory;
|
||||
extern PFN_vkBindImageMemory vkBindImageMemory;
|
||||
extern PFN_vkCreateImage vkCreateImage;
|
||||
extern PFN_vkDestroyImage vkDestroyImage;
|
||||
extern PFN_vkCreateImageView vkCreateImageView;
|
||||
extern PFN_vkDestroyImageView vkDestroyImageView;
|
||||
extern PFN_vkFreeMemory vkFreeMemory;
|
||||
|
||||
#define VULKAN_MAX_SWAPCHAIN_IMAGES 8
|
||||
struct VkSwapchainKHR_T
|
||||
{
|
||||
uint32_t count;
|
||||
struct
|
||||
{
|
||||
VkImage handle;
|
||||
VkDeviceMemory memory;
|
||||
retro_vulkan_image retro_image;
|
||||
} images[VULKAN_MAX_SWAPCHAIN_IMAGES];
|
||||
std::mutex mutex;
|
||||
std::condition_variable condVar;
|
||||
int current_index;
|
||||
};
|
||||
static VkSwapchainKHR_T chain;
|
||||
|
||||
#define LIBRETRO_VK_WARP_LIST() \
|
||||
LIBRETRO_VK_WARP_FUNC(vkDestroyInstance); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCreateDevice); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkDestroyDevice); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkDestroySurfaceKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCreateSwapchainKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkGetSwapchainImagesKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkAcquireNextImageKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkQueuePresentKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkDestroySwapchainKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkQueueSubmit); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkQueueWaitIdle); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCmdPipelineBarrier); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCreateRenderPass)
|
||||
|
||||
#define LIBRETRO_VK_WARP_FUNC(x) \
|
||||
extern PFN_##x x; \
|
||||
PFN_##x x##_org
|
||||
|
||||
LIBRETRO_VK_WARP_FUNC(vkGetInstanceProcAddr);
|
||||
LIBRETRO_VK_WARP_FUNC(vkGetDeviceProcAddr);
|
||||
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 (int i = 0; i < vk_init_info.num_required_device_layers; i++)
|
||||
add_name_unique(EnabledLayerNames, vk_init_info.required_device_layers[i]);
|
||||
|
||||
for (int 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 (int i = 0; i < sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); i++)
|
||||
{
|
||||
if (((VkBool32 *)vk_init_info.required_features)[i])
|
||||
((VkBool32 *)&EnabledFeatures)[i] = VK_TRUE;
|
||||
}
|
||||
|
||||
static bool DEDICATED_ALLOCATION;
|
||||
for (auto extension_name : EnabledExtensionNames)
|
||||
{
|
||||
if (!strcmp(extension_name, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME))
|
||||
DEDICATED_ALLOCATION = 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 VkResult VKAPI_CALL vkCreateLibretroSurfaceKHR(VkInstance instance, const void *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface)
|
||||
{
|
||||
*pSurface = vk_init_info.surface;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR_libretro(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities)
|
||||
{
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR_org(physicalDevice, surface, pSurfaceCapabilities);
|
||||
if (res == VK_SUCCESS)
|
||||
{
|
||||
pSurfaceCapabilities->currentExtent.width = -1;
|
||||
pSurfaceCapabilities->currentExtent.height = -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex)
|
||||
{
|
||||
VkPhysicalDeviceMemoryProperties memory_properties;
|
||||
vkGetPhysicalDeviceMemoryProperties(vulkan->gpu, &memory_properties);
|
||||
// Search memtypes to find first index with those properties
|
||||
for (uint32_t i = 0; i < 32; i++)
|
||||
{
|
||||
if ((typeBits & 1) == 1)
|
||||
{
|
||||
// Type is available, does it match user properties?
|
||||
if ((memory_properties.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask)
|
||||
{
|
||||
*typeIndex = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
typeBits >>= 1;
|
||||
}
|
||||
// No memory types matched, return failure
|
||||
return false;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR_libretro(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain)
|
||||
{
|
||||
uint32_t swapchain_mask = vulkan->get_sync_index_mask(vulkan->handle);
|
||||
|
||||
chain.count = 0;
|
||||
while (swapchain_mask)
|
||||
{
|
||||
chain.count++;
|
||||
swapchain_mask >>= 1;
|
||||
}
|
||||
assert(chain.count <= VULKAN_MAX_SWAPCHAIN_IMAGES);
|
||||
|
||||
for (uint32_t i = 0; i < chain.count; i++)
|
||||
{
|
||||
{
|
||||
VkImageCreateInfo info{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
info.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
info.imageType = VK_IMAGE_TYPE_2D;
|
||||
info.format = pCreateInfo->imageFormat;
|
||||
info.extent.width = pCreateInfo->imageExtent.width;
|
||||
info.extent.height = pCreateInfo->imageExtent.height;
|
||||
info.extent.depth = 1;
|
||||
info.mipLevels = 1;
|
||||
info.arrayLayers = 1;
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
vkCreateImage(device, &info, pAllocator, &chain.images[i].handle);
|
||||
}
|
||||
|
||||
VkMemoryRequirements memreq;
|
||||
vkGetImageMemoryRequirements(device, chain.images[i].handle, &memreq);
|
||||
|
||||
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
alloc.allocationSize = memreq.size;
|
||||
|
||||
VkMemoryDedicatedAllocateInfoKHR dedicated{ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
|
||||
if (DEDICATED_ALLOCATION)
|
||||
{
|
||||
alloc.pNext = &dedicated;
|
||||
dedicated.image = chain.images[i].handle;
|
||||
}
|
||||
|
||||
MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex);
|
||||
VkResult res = vkAllocateMemory(device, &alloc, pAllocator, &chain.images[i].memory);
|
||||
assert(res == VK_SUCCESS);
|
||||
res = vkBindImageMemory(device, chain.images[i].handle, chain.images[i].memory, 0);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
chain.images[i].retro_image.create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
chain.images[i].retro_image.create_info.image = chain.images[i].handle;
|
||||
chain.images[i].retro_image.create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
chain.images[i].retro_image.create_info.format = pCreateInfo->imageFormat;
|
||||
chain.images[i].retro_image.create_info.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
|
||||
chain.images[i].retro_image.create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
chain.images[i].retro_image.create_info.subresourceRange.layerCount = 1;
|
||||
chain.images[i].retro_image.create_info.subresourceRange.levelCount = 1;
|
||||
res = vkCreateImageView(device, &chain.images[i].retro_image.create_info, pAllocator, &chain.images[i].retro_image.image_view);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
chain.images[i].retro_image.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
}
|
||||
|
||||
chain.current_index = -1;
|
||||
*pSwapchain = (VkSwapchainKHR)&chain;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainImagesKHR_libretro(VkDevice device, VkSwapchainKHR swapchain_, uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages)
|
||||
{
|
||||
VkSwapchainKHR_T *swapchain = (VkSwapchainKHR_T *)swapchain_;
|
||||
if (pSwapchainImages)
|
||||
{
|
||||
assert(*pSwapchainImageCount <= swapchain->count);
|
||||
for (int i = 0; i < *pSwapchainImageCount; i++)
|
||||
pSwapchainImages[i] = swapchain->images[i].handle;
|
||||
}
|
||||
else
|
||||
*pSwapchainImageCount = swapchain->count;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImageKHR_libretro(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex)
|
||||
{
|
||||
vulkan->wait_sync_index(vulkan->handle);
|
||||
*pImageIndex = vulkan->get_sync_index(vulkan->handle);
|
||||
#if 0
|
||||
vulkan->set_signal_semaphore(vulkan->handle, semaphore);
|
||||
#endif
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR_libretro(VkQueue queue, const VkPresentInfoKHR *pPresentInfo)
|
||||
{
|
||||
VkSwapchainKHR_T *swapchain = (VkSwapchainKHR_T *)pPresentInfo->pSwapchains[0];
|
||||
std::unique_lock<std::mutex> lock(swapchain->mutex);
|
||||
#if 0
|
||||
if(chain.current_index >= 0)
|
||||
chain.condVar.wait(lock);
|
||||
#endif
|
||||
|
||||
chain.current_index = pPresentInfo->pImageIndices[0];
|
||||
#if 0
|
||||
vulkan->set_image(vulkan->handle, &swapchain->images[pPresentInfo->pImageIndices[0]].retro_image, pPresentInfo->waitSemaphoreCount, pPresentInfo->pWaitSemaphores, vulkan->queue_index);
|
||||
#else
|
||||
vulkan->set_image(vulkan->handle, &swapchain->images[pPresentInfo->pImageIndices[0]].retro_image, 0, nullptr, vulkan->queue_index);
|
||||
#endif
|
||||
swapchain->condVar.notify_all();
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
void vk_libretro_wait_for_presentation()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(chain.mutex);
|
||||
if (chain.current_index < 0)
|
||||
chain.condVar.wait(lock);
|
||||
#if 0
|
||||
chain.current_index = -1;
|
||||
chain.condVar.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
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) {}
|
||||
static VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR_libretro(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks *pAllocator) {}
|
||||
static VKAPI_ATTR void VKAPI_CALL vkDestroySwapchainKHR_libretro(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks *pAllocator)
|
||||
{
|
||||
for (int i = 0; i < chain.count; i++)
|
||||
{
|
||||
vkDestroyImage(device, chain.images[i].handle, pAllocator);
|
||||
vkDestroyImageView(device, chain.images[i].retro_image.image_view, pAllocator);
|
||||
vkFreeMemory(device, chain.images[i].memory, pAllocator);
|
||||
}
|
||||
|
||||
memset(&chain.images, 0x00, sizeof(chain.images));
|
||||
chain.count = 0;
|
||||
chain.current_index = -1;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit_libretro(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence)
|
||||
{
|
||||
VkResult res = VK_SUCCESS;
|
||||
|
||||
#if 0
|
||||
for(int i = 0; i < submitCount; i++)
|
||||
vulkan->set_command_buffers(vulkan->handle, pSubmits[i].commandBufferCount, pSubmits[i].pCommandBuffers);
|
||||
#else
|
||||
#if 1
|
||||
for (int i = 0; i < submitCount; i++)
|
||||
{
|
||||
((VkSubmitInfo *)pSubmits)[i].waitSemaphoreCount = 0;
|
||||
((VkSubmitInfo *)pSubmits)[i].pWaitSemaphores = nullptr;
|
||||
((VkSubmitInfo *)pSubmits)[i].signalSemaphoreCount = 0;
|
||||
((VkSubmitInfo *)pSubmits)[i].pSignalSemaphores = nullptr;
|
||||
}
|
||||
#endif
|
||||
vulkan->lock_queue(vulkan->handle);
|
||||
res = vkQueueSubmit_org(queue, submitCount, pSubmits, fence);
|
||||
vulkan->unlock_queue(vulkan->handle);
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier_libretro(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers)
|
||||
{
|
||||
VkImageMemoryBarrier *barriers = (VkImageMemoryBarrier *)pImageMemoryBarriers;
|
||||
for (int i = 0; i < imageMemoryBarrierCount; i++)
|
||||
{
|
||||
if (pImageMemoryBarriers[i].oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
|
||||
{
|
||||
barriers[i].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barriers[i].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
}
|
||||
if (pImageMemoryBarriers[i].newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
|
||||
{
|
||||
barriers[i].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barriers[i].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
}
|
||||
}
|
||||
return vkCmdPipelineBarrier_org(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, barriers);
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass_libretro(VkDevice device, const VkRenderPassCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass)
|
||||
{
|
||||
if (pCreateInfo->pAttachments[0].finalLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
|
||||
((VkAttachmentDescription *)pCreateInfo->pAttachments)[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
return vkCreateRenderPass_org(device, pCreateInfo, pAllocator, pRenderPass);
|
||||
}
|
||||
|
||||
#undef LIBRETRO_VK_WARP_FUNC
|
||||
#define LIBRETRO_VK_WARP_FUNC(x) \
|
||||
do \
|
||||
{ \
|
||||
if (!strcmp(pName, #x)) \
|
||||
{ \
|
||||
x##_org = (PFN_##x)fptr; \
|
||||
return (PFN_vkVoidFunction)x##_libretro; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr_libretro(VkInstance instance, const char *pName)
|
||||
{
|
||||
if (false
|
||||
#ifdef _WIN32
|
||||
|| !strcmp(pName, "vkCreateWin32SurfaceKHR")
|
||||
#endif
|
||||
#ifdef __ANDROID__
|
||||
|| !strcmp(pName, "vkCreateAndroidSurfaceKHR")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||
|| !strcmp(pName, "vkCreateXlibSurfaceKHR")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||
|| !strcmp(pName, "vkCreateXcbSurfaceKHR")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||
|| !strcmp(pName, "vkCreateWaylandSurfaceKHR")
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return (PFN_vkVoidFunction)vkCreateLibretroSurfaceKHR;
|
||||
}
|
||||
|
||||
PFN_vkVoidFunction fptr = vkGetInstanceProcAddr_org(instance, pName);
|
||||
if (!fptr)
|
||||
return fptr;
|
||||
|
||||
LIBRETRO_VK_WARP_LIST();
|
||||
|
||||
return fptr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void vk_libretro_init(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)
|
||||
{
|
||||
assert(surface);
|
||||
|
||||
vk_init_info.instance = instance;
|
||||
vk_init_info.gpu = gpu;
|
||||
vk_init_info.surface = surface;
|
||||
vk_init_info.get_instance_proc_addr = get_instance_proc_addr;
|
||||
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;
|
||||
|
||||
vkGetInstanceProcAddr_org = vkGetInstanceProcAddr;
|
||||
vkGetInstanceProcAddr = vkGetInstanceProcAddr_libretro;
|
||||
vkGetDeviceProcAddr_org = vkGetDeviceProcAddr;
|
||||
vkGetDeviceProcAddr = vkGetDeviceProcAddr_libretro;
|
||||
vkCreateInstance = vkCreateInstance_libretro;
|
||||
}
|
||||
|
||||
void vk_libretro_set_hwrender_interface(retro_hw_render_interface *hw_render_interface) { vulkan = (retro_hw_render_interface_vulkan *)hw_render_interface; }
|
||||
void vk_libretro_shutdown()
|
||||
{
|
||||
vulkan = NULL;
|
||||
DEDICATED_ALLOCATION = false;
|
||||
}
|
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 "ext/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
|
5
libretro/link.T
Normal file
5
libretro/link.T
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
global: retro_*;
|
||||
local: *;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user