Split Android graphics context implementations into their own files.

This commit is contained in:
Henrik Rydgård 2018-01-20 21:47:16 +01:00
parent d2e739092d
commit 133bef575a
14 changed files with 468 additions and 339 deletions

View File

@ -660,6 +660,13 @@ if(ANDROID)
set(nativeExtra ${nativeExtra}
ext/native/base/NativeApp.h
android/jni/app-android.cpp
android/jni/AndroidEGLContext.cpp
android/jni/AndroidEGLContext.h
android/jni/AndroidJavaGLContext.cpp
android/jni/AndroidJavaGLContext.h
android/jni/AndroidVulkanContext.cpp
android/jni/AndroidVulkanContext.h
android/jni/AndroidGraphicsContext.h
android/jni/native_audio.cpp
android/jni/native_audio.h
android/jni/native-audio-so.cpp

View File

@ -6,9 +6,11 @@
#include <iostream>
#include "base/basictypes.h"
#include "base/display.h"
#include "VulkanContext.h"
#include "GPU/Common/ShaderCommon.h"
#include "Common/StringUtils.h"
#include "Core/Config.h"
// Change this to 1, 2, and 3 to fake failures in a few places, so that
// we can test our fallback-to-GL code.

View File

@ -722,8 +722,14 @@ void NativeShutdownGraphics() {
ui_draw2d.Shutdown();
ui_draw2d_front.Shutdown();
if (colorPipeline) {
colorPipeline->Release();
colorPipeline = nullptr;
}
if (texColorPipeline) {
texColorPipeline->Release();
texColorPipeline = nullptr;
}
ILOG("NativeShutdownGraphics done");
}

View File

@ -278,6 +278,24 @@
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\android\jni\AndroidEGLContext.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\android\jni\AndroidJavaGLContext.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\android\jni\AndroidVulkanContext.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\android\jni\app-android.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -435,6 +453,30 @@
<ClCompile Include="XinputDevice.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\android\jni\AndroidEGLContext.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\android\jni\AndroidGraphicsContext.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\android\jni\AndroidJavaGLContext.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\android\jni\AndroidVulkanContext.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\android\jni\app-android.h" />
<ClInclude Include="..\android\jni\ArmEmitterTest.h" />
<ClInclude Include="..\android\jni\Arm64EmitterTest.h" />

View File

@ -200,6 +200,15 @@
<ClCompile Include="..\android\jni\native-audio-so.cpp">
<Filter>Other Platforms</Filter>
</ClCompile>
<ClCompile Include="..\android\jni\AndroidEGLContext.cpp">
<Filter>Other Platforms</Filter>
</ClCompile>
<ClCompile Include="..\android\jni\AndroidJavaGLContext.cpp">
<Filter>Other Platforms</Filter>
</ClCompile>
<ClCompile Include="..\android\jni\AndroidVulkanContext.cpp">
<Filter>Other Platforms</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Debugger\CtrlDisAsmView.h">
@ -368,6 +377,18 @@
<ClInclude Include="..\android\jni\native-audio-so.h">
<Filter>Other Platforms</Filter>
</ClInclude>
<ClInclude Include="..\android\jni\AndroidEGLContext.h">
<Filter>Other Platforms</Filter>
</ClInclude>
<ClInclude Include="..\android\jni\AndroidGraphicsContext.h">
<Filter>Other Platforms</Filter>
</ClInclude>
<ClInclude Include="..\android\jni\AndroidJavaGLContext.h">
<Filter>Other Platforms</Filter>
</ClInclude>
<ClInclude Include="..\android\jni\AndroidVulkanContext.h">
<Filter>Other Platforms</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="icon1.ico">

View File

@ -406,6 +406,9 @@ LOCAL_STATIC_LIBRARIES += ppsspp_core
LOCAL_MODULE := ppsspp_jni
LOCAL_SRC_FILES := \
$(SRC)/android/jni/app-android.cpp \
$(SRC)/android/jni/AndroidEGLContext.cpp \
$(SRC)/android/jni/AndroidJavaGLContext.cpp \
$(SRC)/android/jni/AndroidVulkanContext.cpp \
$(SRC)/android/jni/native_audio.cpp \
$(SRC)/android/jni/native-audio-so.cpp \
$(SRC)/UI/BackgroundAudio.cpp \

View File

@ -0,0 +1,62 @@
#include "base/logging.h"
#include "base/NativeApp.h"
#include "gfx_es2/gpu_features.h"
#include "AndroidEGLContext.h"
#include "GL/GLInterface/EGLAndroid.h"
#include "Core/System.h"
bool AndroidEGLGraphicsContext::Init(ANativeWindow *wnd, int backbufferWidth, int backbufferHeight, int backbufferFormat, int androidVersion) {
wnd_ = wnd;
gl = HostGL_CreateGLInterface();
if (!gl) {
ELOG("ERROR: Failed to create GL interface");
return false;
}
ILOG("EGL interface created. Desired backbuffer size: %dx%d", backbufferWidth, backbufferHeight);
// Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect...
gl->SetBackBufferDimensions(backbufferWidth, backbufferHeight);
gl->SetMode(MODE_DETECT_ES);
bool use565 = false;
// This workaround seems only be needed on some really old devices.
if (androidVersion < ANDROID_VERSION_ICS) {
switch (backbufferFormat) {
case 4: // PixelFormat.RGB_565
use565 = true;
break;
default:
break;
}
}
if (!gl->Create(wnd, false, use565)) {
ELOG("EGL creation failed! (use565=%d)", (int)use565);
// TODO: What do we do now?
delete gl;
return false;
}
gl->MakeCurrent();
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
SetGPUBackend(GPUBackend::OPENGL);
bool success = draw_->CreatePresets(); // There will always be a GLSL compiler capable of compiling these.
assert(success);
return true;
}
void AndroidEGLGraphicsContext::Shutdown() {
delete draw_;
draw_ = nullptr;
NativeShutdownGraphics();
gl->ClearCurrent();
gl->Shutdown();
delete gl;
ANativeWindow_release(wnd_);
}
void AndroidEGLGraphicsContext::SwapBuffers() {
gl->Swap();
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "AndroidGraphicsContext.h"
#include "Common/GL/GLInterfaceBase.h"
class AndroidEGLGraphicsContext : public AndroidGraphicsContext {
public:
AndroidEGLGraphicsContext() : draw_(nullptr), wnd_(nullptr), gl(nullptr) {}
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
void Shutdown() override;
void SwapBuffers() override;
void SwapInterval(int interval) override {}
void Resize() override {}
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
private:
Draw::DrawContext *draw_;
ANativeWindow *wnd_;
cInterfaceBase *gl;
};

View File

@ -0,0 +1,23 @@
#pragma once
#include <android/native_window_jni.h>
#include "thin3d/thin3d.h"
#include "Common/GraphicsContext.h"
enum {
ANDROID_VERSION_GINGERBREAD = 9,
ANDROID_VERSION_ICS = 14,
ANDROID_VERSION_JELLYBEAN = 16,
ANDROID_VERSION_KITKAT = 19,
ANDROID_VERSION_LOLLIPOP = 21,
ANDROID_VERSION_MARSHMALLOW = 23,
ANDROID_VERSION_NOUGAT = 24,
ANDROID_VERSION_NOUGAT_1 = 25,
};
class AndroidGraphicsContext : public GraphicsContext {
public:
virtual bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0;
};

View File

@ -0,0 +1,19 @@
#include "AndroidJavaGLContext.h"
#include "base/NativeApp.h"
#include "gfx_es2/gpu_features.h"
#include "Core/System.h"
AndroidJavaEGLGraphicsContext::AndroidJavaEGLGraphicsContext() {
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
SetGPUBackend(GPUBackend::OPENGL);
bool success = draw_->CreatePresets();
assert(success);
}
void AndroidJavaEGLGraphicsContext::Shutdown() {
ILOG("AndroidJavaEGLGraphicsContext::Shutdown");
delete draw_;
draw_ = nullptr;
NativeShutdownGraphics();
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "AndroidGraphicsContext.h"
// Doesn't do much. Just to fit in.
class AndroidJavaEGLGraphicsContext : public GraphicsContext {
public:
AndroidJavaEGLGraphicsContext();
~AndroidJavaEGLGraphicsContext() {
delete draw_;
}
void Shutdown() override;
void SwapBuffers() override {}
void SwapInterval(int interval) override {}
void Resize() override {}
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
private:
Draw::DrawContext *draw_;
};

View File

@ -0,0 +1,198 @@
#include "AndroidVulkanContext.h"
#include "base/NativeApp.h"
#include "base/display.h"
#include "Common/Vulkan/VulkanLoader.h"
#include "Common/Vulkan/VulkanContext.h"
#include "thin3d/VulkanRenderManager.h"
#include "util/text/parsers.h"
#include "Core/Config.h"
#include "Core/System.h"
static VulkanLogOptions g_LogOptions;
const char *ObjTypeToString(VkDebugReportObjectTypeEXT type) {
switch (type) {
case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return "Instance";
case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return "PhysicalDevice";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return "Device";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return "Queue";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return "CommandBuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return "DeviceMemory";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return "Buffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return "BufferView";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return "Image";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return "ImageView";
case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return "ShaderModule";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return "Pipeline";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return "PipelineLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return "Sampler";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return "DescriptorSet";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return "DescriptorSetLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return "DescriptorPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return "Fence";
case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return "Semaphore";
case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return "Event";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return "QueryPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return "Framebuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return "RenderPass";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return "PipelineCache";
case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return "SurfaceKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return "SwapChainKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return "CommandPool";
default: return "Unknown";
}
}
static VKAPI_ATTR VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void *pUserData) {
const VulkanLogOptions *options = (const VulkanLogOptions *)pUserData;
int loglevel = ANDROID_LOG_INFO;
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
loglevel = ANDROID_LOG_ERROR;
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
}
__android_log_print(loglevel, APP_NAME, "[%s] %s Code %d : %s", pLayerPrefix, ObjTypeToString(objType), msgCode, pMsg);
// false indicates that layer should not bail-out of an
// API call that had validation failures. This may mean that the
// app dies inside the driver due to invalid parameter(s).
// That's what would happen without validation layers, so we'll
// keep that behavior here.
return false;
}
AndroidVulkanContext::~AndroidVulkanContext() {
delete g_Vulkan;
g_Vulkan = nullptr;
}
bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) {
ILOG("AndroidVulkanContext::Init");
init_glslang();
g_LogOptions.breakOnError = true;
g_LogOptions.breakOnWarning = true;
g_LogOptions.msgBoxOnError = false;
ILOG("Creating Vulkan context");
Version gitVer(PPSSPP_GIT_VERSION);
if (!g_Vulkan) {
g_Vulkan = new VulkanContext();
}
VulkanContext::CreateInfo info{};
info.app_name = "PPSSPP";
info.app_ver = gitVer.ToInteger();
info.flags = VULKAN_FLAG_PRESENT_MAILBOX | VULKAN_FLAG_PRESENT_FIFO_RELAXED;
if (VK_SUCCESS != g_Vulkan->CreateInstance(info)) {
ELOG("Failed to create vulkan context: %s", g_Vulkan->InitError().c_str());
System_SendMessage("toast", "No Vulkan compatible device found. Using OpenGL instead.");
delete g_Vulkan;
g_Vulkan = nullptr;
return false;
}
int physicalDevice = g_Vulkan->GetBestPhysicalDevice();
if (physicalDevice < 0) {
ELOG("No usable Vulkan device found.");
g_Vulkan->DestroyInstance();
delete g_Vulkan;
g_Vulkan = nullptr;
return false;
}
g_Vulkan->ChooseDevice(physicalDevice);
// Here we can enable device extensions if we like.
ILOG("Creating Vulkan device");
if (g_Vulkan->CreateDevice() != VK_SUCCESS) {
ILOG("Failed to create vulkan device: %s", g_Vulkan->InitError().c_str());
System_SendMessage("toast", "No Vulkan driver found. Using OpenGL instead.");
g_Vulkan->DestroyInstance();
delete g_Vulkan;
g_Vulkan = nullptr;
return false;
}
int width = desiredBackbufferSizeX;
int height = desiredBackbufferSizeY;
if (!width || !height) {
width = pixel_xres;
height = pixel_yres;
}
ILOG("InitSurfaceAndroid: width=%d height=%d", width, height);
g_Vulkan->InitSurface(WINDOWSYSTEM_ANDROID, (void *)wnd, nullptr, width, height);
if (g_validate_) {
int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions);
}
bool success = true;
if (g_Vulkan->InitObjects()) {
draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, g_Config.bGfxDebugSplitSubmit);
SetGPUBackend(GPUBackend::VULKAN);
success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler.
assert(success);
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
success = renderManager->HasBackbuffers();
} else {
success = false;
}
ILOG("AndroidVulkanContext::Init completed, %s", success ? "successfully" : "but failed");
if (!success) {
g_Vulkan->DestroyObjects();
g_Vulkan->DestroyDevice();
g_Vulkan->DestroyDebugMsgCallback();
g_Vulkan->DestroyInstance();
}
return success;
}
void AndroidVulkanContext::Shutdown() {
ILOG("AndroidVulkanContext::Shutdown");
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
ILOG("Calling NativeShutdownGraphics");
NativeShutdownGraphics();
delete draw_;
draw_ = nullptr;
g_Vulkan->WaitUntilQueueIdle();
g_Vulkan->DestroyObjects();
g_Vulkan->DestroyDevice();
g_Vulkan->DestroyDebugMsgCallback();
g_Vulkan->DestroyInstance();
// We keep the g_Vulkan context around to avoid invalidating a ton of pointers around the app.
finalize_glslang();
ILOG("AndroidVulkanContext::Shutdown completed");
}
void AndroidVulkanContext::SwapBuffers() {
}
void AndroidVulkanContext::Resize() {
ILOG("AndroidVulkanContext::Resize begin (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
g_Vulkan->DestroyObjects();
// backbufferResize updated these values. TODO: Notify another way?
g_Vulkan->ReinitSurface(pixel_xres, pixel_yres);
g_Vulkan->InitObjects();
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
ILOG("AndroidVulkanContext::Resize end (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
}
void AndroidVulkanContext::SwapInterval(int interval) {
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "AndroidGraphicsContext.h"
static const bool g_validate_ = true;
class VulkanContext;
class AndroidVulkanContext : public AndroidGraphicsContext {
public:
AndroidVulkanContext() : draw_(nullptr) {}
~AndroidVulkanContext();
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
void Shutdown() override;
void SwapInterval(int interval) override;
void SwapBuffers() override;
void Resize() override;
void *GetAPIContext() override { return g_Vulkan; }
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
private:
VulkanContext *g_Vulkan = nullptr;
Draw::DrawContext *draw_ = nullptr;
};
struct VulkanLogOptions {
bool breakOnWarning;
bool breakOnError;
bool msgBoxOnError;
};

View File

@ -4,7 +4,6 @@
// It calls a set of methods defined in NativeApp.h. These should be implemented
// by your game or app.
#include <assert.h>
#include <sstream>
#include <jni.h>
@ -18,6 +17,11 @@
#include <mutex>
#include <thread>
#include "AndroidGraphicsContext.h"
#include "AndroidVulkanContext.h"
#include "AndroidEGLContext.h"
#include "AndroidJavaGLContext.h"
#include "base/basictypes.h"
#include "base/stringutil.h"
#include "base/display.h"
@ -58,17 +62,6 @@ JavaVM *javaVM;
static AndroidAudioState *g_audioState;
enum {
ANDROID_VERSION_GINGERBREAD = 9,
ANDROID_VERSION_ICS = 14,
ANDROID_VERSION_JELLYBEAN = 16,
ANDROID_VERSION_KITKAT = 19,
ANDROID_VERSION_LOLLIPOP = 21,
ANDROID_VERSION_MARSHMALLOW = 23,
ANDROID_VERSION_NOUGAT = 24,
ANDROID_VERSION_NOUGAT_1 = 25,
};
struct FrameCommand {
FrameCommand() {}
FrameCommand(std::string cmd, std::string prm) : command(cmd), params(prm) {}
@ -77,331 +70,6 @@ struct FrameCommand {
std::string params;
};
class AndroidGraphicsContext : public GraphicsContext {
public:
virtual bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0;
};
class AndroidEGLGraphicsContext : public AndroidGraphicsContext {
public:
AndroidEGLGraphicsContext() : draw_(nullptr), wnd_(nullptr), gl(nullptr) {}
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
void Shutdown() override;
void SwapBuffers() override;
void SwapInterval(int interval) override {}
void Resize() override {}
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
private:
Draw::DrawContext *draw_;
ANativeWindow *wnd_;
cInterfaceBase *gl;
};
bool AndroidEGLGraphicsContext::Init(ANativeWindow *wnd, int backbufferWidth, int backbufferHeight, int backbufferFormat, int androidVersion) {
wnd_ = wnd;
gl = HostGL_CreateGLInterface();
if (!gl) {
ELOG("ERROR: Failed to create GL interface");
return false;
}
ILOG("EGL interface created. Desired backbuffer size: %dx%d", backbufferWidth, backbufferHeight);
// Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect...
gl->SetBackBufferDimensions(backbufferWidth, backbufferHeight);
gl->SetMode(MODE_DETECT_ES);
bool use565 = false;
// This workaround seems only be needed on some really old devices.
if (androidVersion < ANDROID_VERSION_ICS) {
switch (backbufferFormat) {
case 4: // PixelFormat.RGB_565
use565 = true;
break;
default:
break;
}
}
if (!gl->Create(wnd, false, use565)) {
ELOG("EGL creation failed! (use565=%d)", (int)use565);
// TODO: What do we do now?
delete gl;
return false;
}
gl->MakeCurrent();
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
SetGPUBackend(GPUBackend::OPENGL);
bool success = draw_->CreatePresets(); // There will always be a GLSL compiler capable of compiling these.
assert(success);
return true;
}
void AndroidEGLGraphicsContext::Shutdown() {
delete draw_;
draw_ = nullptr;
NativeShutdownGraphics();
gl->ClearCurrent();
gl->Shutdown();
delete gl;
ANativeWindow_release(wnd_);
finalize_glslang();
}
void AndroidEGLGraphicsContext::SwapBuffers() {
gl->Swap();
}
// Doesn't do much. Just to fit in.
class AndroidJavaEGLGraphicsContext : public GraphicsContext {
public:
AndroidJavaEGLGraphicsContext() {
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
SetGPUBackend(GPUBackend::OPENGL);
bool success = draw_->CreatePresets();
assert(success);
}
~AndroidJavaEGLGraphicsContext() {
delete draw_;
}
void Shutdown() override {
ILOG("AndroidJavaEGLGraphicsContext::Shutdown");
delete draw_;
draw_ = nullptr;
NativeShutdownGraphics();
finalize_glslang();
}
void SwapBuffers() override {}
void SwapInterval(int interval) override {}
void Resize() override {}
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
private:
Draw::DrawContext *draw_;
};
static const bool g_validate_ = true;
static VulkanContext *g_Vulkan;
class AndroidVulkanContext : public AndroidGraphicsContext {
public:
AndroidVulkanContext() : draw_(nullptr) {}
~AndroidVulkanContext() {
delete g_Vulkan;
g_Vulkan = nullptr;
}
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
void Shutdown() override;
void SwapInterval(int interval) override;
void SwapBuffers() override;
void Resize() override;
void *GetAPIContext() override { return g_Vulkan; }
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
private:
Draw::DrawContext *draw_;
};
struct VulkanLogOptions {
bool breakOnWarning;
bool breakOnError;
bool msgBoxOnError;
};
static VulkanLogOptions g_LogOptions;
const char *ObjTypeToString(VkDebugReportObjectTypeEXT type) {
switch (type) {
case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return "Instance";
case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return "PhysicalDevice";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return "Device";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return "Queue";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return "CommandBuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return "DeviceMemory";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return "Buffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return "BufferView";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return "Image";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return "ImageView";
case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return "ShaderModule";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return "Pipeline";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return "PipelineLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return "Sampler";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return "DescriptorSet";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return "DescriptorSetLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return "DescriptorPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return "Fence";
case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return "Semaphore";
case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return "Event";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return "QueryPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return "Framebuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return "RenderPass";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return "PipelineCache";
case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return "SurfaceKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return "SwapChainKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return "CommandPool";
default: return "Unknown";
}
}
static VKAPI_ATTR VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void *pUserData) {
const VulkanLogOptions *options = (const VulkanLogOptions *)pUserData;
int loglevel = ANDROID_LOG_INFO;
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
loglevel = ANDROID_LOG_ERROR;
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
}
__android_log_print(loglevel, APP_NAME, "[%s] %s Code %d : %s", pLayerPrefix, ObjTypeToString(objType), msgCode, pMsg);
// false indicates that layer should not bail-out of an
// API call that had validation failures. This may mean that the
// app dies inside the driver due to invalid parameter(s).
// That's what would happen without validation layers, so we'll
// keep that behavior here.
return false;
}
bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) {
ILOG("AndroidVulkanContext::Init");
init_glslang();
g_LogOptions.breakOnError = true;
g_LogOptions.breakOnWarning = true;
g_LogOptions.msgBoxOnError = false;
ILOG("Creating Vulkan context");
Version gitVer(PPSSPP_GIT_VERSION);
if (!g_Vulkan) {
g_Vulkan = new VulkanContext();
}
VulkanContext::CreateInfo info{};
info.app_name = "PPSSPP";
info.app_ver = gitVer.ToInteger();
info.flags = VULKAN_FLAG_PRESENT_MAILBOX | VULKAN_FLAG_PRESENT_FIFO_RELAXED;
if (VK_SUCCESS != g_Vulkan->CreateInstance(info)) {
ELOG("Failed to create vulkan context: %s", g_Vulkan->InitError().c_str());
System_SendMessage("toast", "No Vulkan compatible device found. Using OpenGL instead.");
delete g_Vulkan;
g_Vulkan = nullptr;
return false;
}
int physicalDevice = g_Vulkan->GetBestPhysicalDevice();
if (physicalDevice < 0) {
ELOG("No usable Vulkan device found.");
g_Vulkan->DestroyInstance();
delete g_Vulkan;
g_Vulkan = nullptr;
return false;
}
g_Vulkan->ChooseDevice(physicalDevice);
// Here we can enable device extensions if we like.
ILOG("Creating Vulkan device");
if (g_Vulkan->CreateDevice() != VK_SUCCESS) {
ILOG("Failed to create vulkan device: %s", g_Vulkan->InitError().c_str());
System_SendMessage("toast", "No Vulkan driver found. Using OpenGL instead.");
g_Vulkan->DestroyInstance();
delete g_Vulkan;
g_Vulkan = nullptr;
return false;
}
int width = desiredBackbufferSizeX;
int height = desiredBackbufferSizeY;
if (!width || !height) {
width = pixel_xres;
height = pixel_yres;
}
ILOG("InitSurfaceAndroid: width=%d height=%d", width, height);
g_Vulkan->InitSurface(WINDOWSYSTEM_ANDROID, (void *)wnd, nullptr, width, height);
if (g_validate_) {
int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions);
}
bool success = true;
if (g_Vulkan->InitObjects()) {
draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, g_Config.bGfxDebugSplitSubmit);
SetGPUBackend(GPUBackend::VULKAN);
success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler.
assert(success);
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
success = renderManager->HasBackbuffers();
} else {
success = false;
}
ILOG("AndroidVulkanContext::Init completed, %s", success ? "successfully" : "but failed");
if (!success) {
g_Vulkan->DestroyObjects();
g_Vulkan->DestroyDevice();
g_Vulkan->DestroyDebugMsgCallback();
g_Vulkan->DestroyInstance();
}
return success;
}
void AndroidVulkanContext::Shutdown() {
ILOG("AndroidVulkanContext::Shutdown");
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
ILOG("Calling NativeShutdownGraphics");
NativeShutdownGraphics();
delete draw_;
draw_ = nullptr;
g_Vulkan->WaitUntilQueueIdle();
g_Vulkan->DestroyObjects();
g_Vulkan->DestroyDevice();
g_Vulkan->DestroyDebugMsgCallback();
g_Vulkan->DestroyInstance();
// We keep the g_Vulkan context around to avoid invalidating a ton of pointers around the app.
finalize_glslang();
ILOG("AndroidVulkanContext::Shutdown completed");
}
void AndroidVulkanContext::SwapBuffers() {
}
void AndroidVulkanContext::Resize() {
ILOG("AndroidVulkanContext::Resize begin (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
g_Vulkan->DestroyObjects();
// backbufferResize updated these values. TODO: Notify another way?
g_Vulkan->ReinitSurface(pixel_xres, pixel_yres);
g_Vulkan->InitObjects();
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
ILOG("AndroidVulkanContext::Resize end (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
}
void AndroidVulkanContext::SwapInterval(int interval) {
}
static std::mutex frameCommandLock;
static std::queue<FrameCommand> frameCommands;