Libretro port.

This commit is contained in:
aliaspider 2018-03-24 12:44:53 +01:00
parent 686717eb71
commit c4f7790618
22 changed files with 5674 additions and 0 deletions

View File

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

View File

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

View File

@ -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
View File

@ -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
View File

@ -0,0 +1,4 @@
*.bc
*.so
*.dll
*.dylib

21
libretro/CMakeLists.txt Normal file
View 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})

View 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;
}

View 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;
};

View 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();
}

View 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

View 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);
}

View 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
View 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
View 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
View 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)

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View 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
View File

@ -0,0 +1,403 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------------
* The following license statement only applies to this libretro API header (libretro_vulkan.h)
* ---------------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the
* "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRETRO_VULKAN_H__
#define LIBRETRO_VULKAN_H__
#include "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
View File

@ -0,0 +1,5 @@
{
global: retro_*;
local: *;
};