Permalink
Browse files

Merge pull request #10731 from hrydgard/improve-vulkan-detection

Improve Vulkan detection
  • Loading branch information...
hrydgard committed Mar 17, 2018
2 parents 31de74c + 08c3e13 commit 940d8b4589f4aad0c937f183069f44017dac2146
@@ -579,6 +579,7 @@ VkResult VulkanContext::CreateDevice() {
VulkanLoadDeviceFunctions(device_);
}
ILOG("Device created.\n");
VulkanSetAvailable(true);
return res;
}
@@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/Vulkan/VulkanLoader.h"
#include <vector>
#include "base/logging.h"
#include "base/basictypes.h"
@@ -203,37 +204,139 @@ static HINSTANCE vulkanLibrary;
static void *vulkanLibrary;
#endif
bool g_vulkanAvailabilityChecked = false;
bool g_vulkanMayBeAvailable = false;
#define LOAD_INSTANCE_FUNC(instance, x) x = (PFN_ ## x)vkGetInstanceProcAddr(instance, #x); if (!x) {ILOG("Missing (instance): %s", #x);}
#define LOAD_DEVICE_FUNC(instance, x) x = (PFN_ ## x)vkGetDeviceProcAddr(instance, #x); if (!x) {ILOG("Missing (device): %s", #x);}
#define LOAD_GLOBAL_FUNC(x) x = (PFN_ ## x)dlsym(vulkanLibrary, #x); if (!x) {ILOG("Missing (global): %s", #x);}
#define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x);
static const char *so_names[] = {
"libvulkan.so",
"libvulkan.so.1",
};
void VulkanSetAvailable(bool available) {
g_vulkanAvailabilityChecked = true;
g_vulkanMayBeAvailable = available;
}
bool VulkanMayBeAvailable() {
if (vulkanLibrary)
return true;
if (g_vulkanAvailabilityChecked)
return g_vulkanMayBeAvailable;
bool available = false;
#ifndef _WIN32
void *lib = nullptr;
for (int i = 0; i < ARRAY_SIZE(so_names); i++) {
void *lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
if (lib) {
available = true;
dlclose(lib);
break;
}
}
#else
// LoadLibrary etc
HINSTANCE lib = LoadLibrary(L"vulkan-1.dll");
available = lib != 0;
#endif
// Do a hyper minimal initialization and teardown to figure out if there's any chance
// that any sort of Vulkan will be usable.
PFN_vkCreateInstance localCreateInstance = LOAD_GLOBAL_FUNC_LOCAL(lib, vkCreateInstance);
PFN_vkEnumeratePhysicalDevices localEnumerate = LOAD_GLOBAL_FUNC_LOCAL(lib, vkEnumeratePhysicalDevices);
PFN_vkDestroyInstance localDestroyInstance = LOAD_GLOBAL_FUNC_LOCAL(lib, vkDestroyInstance);
PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties = LOAD_GLOBAL_FUNC_LOCAL(lib, vkGetPhysicalDeviceProperties);
// Need to predeclare all this because of the gotos...
VkInstanceCreateInfo ci{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
VkApplicationInfo info{ VK_STRUCTURE_TYPE_APPLICATION_INFO };
std::vector<VkPhysicalDevice> devices;
bool anyGood;
const char *instanceExtensions[1];
int extCount = 0;
VkInstance instance = VK_NULL_HANDLE;
VkResult res;
uint32_t physicalDeviceCount = 0;
if (!localCreateInstance || !localEnumerate || !localDestroyInstance || !localGetPhysicalDeviceProperties)
goto bail;
#ifdef _WIN32
instanceExtensions[0] = VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
ci.enabledExtensionCount = 1;
#elif defined(__ANDROID__)
instanceExtensions[0] = VK_KHR_ANDROID_SURFACE_EXTENSION_NAME;
ci.enabledExtensionCount = 1;
#endif
ci.ppEnabledExtensionNames = instanceExtensions;
ci.enabledLayerCount = 0;
info.apiVersion = VK_API_VERSION_1_0;
info.applicationVersion = 1;
info.engineVersion = 1;
info.pApplicationName = "VulkanChecker";
info.pEngineName = "VulkanChecker";
ci.pApplicationInfo = &info;
ci.flags = 0;
res = localCreateInstance(&ci, nullptr, &instance);
if (res != VK_SUCCESS) {
ELOG("Failed to create vulkan instance.");
goto bail;
}
ILOG("Vulkan test instance created successfully.");
res = localEnumerate(instance, &physicalDeviceCount, nullptr);
if (res != VK_SUCCESS) {
ELOG("Failed to count physical devices.");
goto bail;
}
if (physicalDeviceCount == 0) {
ELOG("No physical Vulkan devices.");
goto bail;
}
devices.resize(physicalDeviceCount);
res = localEnumerate(instance, &physicalDeviceCount, devices.data());
if (res != VK_SUCCESS) {
ELOG("Failed to enumerate physical devices.");
goto bail;
}
anyGood = false;
for (auto device : devices) {
VkPhysicalDeviceProperties props;
localGetPhysicalDeviceProperties(device, &props);
switch (props.deviceType) {
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
anyGood = true;
break;
}
// TODO: Should also check queuefamilyproperties for a GRAPHICS queue family? Oh well.
}
if (!anyGood) {
WLOG("Found Vulkan API, but no good Vulkan device!");
g_vulkanMayBeAvailable = false;
} else {
ILOG("Found working Vulkan API!");
g_vulkanMayBeAvailable = true;
}
bail:
g_vulkanAvailabilityChecked = true;
if (instance) {
localDestroyInstance(instance, nullptr);
}
if (lib) {
#ifndef _WIN32
dlclose(lib);
#else
FreeLibrary(lib);
}
#endif
return available;
} else {
ELOG("Vulkan with working device not detected.");
}
return g_vulkanMayBeAvailable;
}
bool VulkanLoad() {
@@ -201,6 +201,7 @@ extern PFN_vkDestroyDebugReportCallbackEXT dyn_vkDestroyDebugReportCallbackEXT;
// Way to do a quick check before even attempting to load.
bool VulkanMayBeAvailable();
void VulkanSetAvailable(bool available);
bool VulkanLoad();
void VulkanLoadInstanceFunctions(VkInstance instance);
@@ -63,8 +63,8 @@ std::string D3D11FragmentShader::GetShaderString(DebugShaderStringType type) con
}
}
D3D11VertexShader::D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform, bool usesLighting)
: device_(device), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(nullptr), usesLighting_(usesLighting) {
D3D11VertexShader::D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform)
: device_(device), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(nullptr) {
source_ = code;
module_ = CreateVertexShaderD3D11(device, code, strlen(code), &bytecode_, featureLevel);
@@ -196,9 +196,8 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **
D3D11VertexShader *vs;
if (vsIter == vsCache_.end()) {
// Vertex shader not in cache. Let's compile it.
bool usesLighting;
GenerateVertexShaderD3D11(VSID, codeBuffer_, &usesLighting, featureLevel_ <= D3D_FEATURE_LEVEL_9_3 ? HLSL_D3D11_LEVEL9 : HLSL_D3D11);
vs = new D3D11VertexShader(device_, featureLevel_, VSID, codeBuffer_, vertType, useHWTransform, usesLighting);
GenerateVertexShaderD3D11(VSID, codeBuffer_, featureLevel_ <= D3D_FEATURE_LEVEL_9_3 ? HLSL_D3D11_LEVEL9 : HLSL_D3D11);
vs = new D3D11VertexShader(device_, featureLevel_, VSID, codeBuffer_, vertType, useHWTransform);
vsCache_[VSID] = vs;
} else {
vs = vsIter->second;
@@ -54,16 +54,13 @@ class D3D11FragmentShader {
class D3D11VertexShader {
public:
D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform, bool usesLighting);
D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform);
~D3D11VertexShader();
const std::string &source() const { return source_; }
const std::vector<uint8_t> &bytecode() const { return bytecode_; }
bool Failed() const { return failed_; }
bool UseHWTransform() const { return useHWTransform_; }
bool HasLights() const {
return usesLighting_;
}
std::string GetShaderString(DebugShaderStringType type) const;
ID3D11VertexShader *GetShader() const { return module_; }
@@ -77,7 +74,6 @@ class D3D11VertexShader {
bool failed_;
bool useHWTransform_;
bool usesLighting_;
VShaderID id_;
};
@@ -19,7 +19,6 @@
#include "GPU/D3D11/VertexShaderGeneratorD3D11.h"
#include "GPU/Directx9/VertexShaderGeneratorDX9.h"
void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, bool *usesLighting, ShaderLanguage lang) {
*usesLighting = true;
void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, ShaderLanguage lang) {
DX9::GenerateVertexShaderHLSL(id, buffer, lang);
}
@@ -19,4 +19,4 @@
#include "GPU/Common/ShaderId.h"
void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, bool *usesLighting, ShaderLanguage lang);
void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, ShaderLanguage lang);
@@ -281,16 +281,11 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
vulkanPipeline->pipeline = pipeline;
vulkanPipeline->flags = PIPELINE_FLAG_USES_BASE_UB;
vulkanPipeline->flags = 0;
if (useBlendConstant)
vulkanPipeline->flags |= PIPELINE_FLAG_USES_BLEND_CONSTANT;
if (key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP)
vulkanPipeline->flags |= PIPELINE_FLAG_USES_LINES;
if (useHwTransform) {
if (vs->HasLights()) {
vulkanPipeline->flags |= PIPELINE_FLAG_USES_LIGHT_UB;
}
}
return vulkanPipeline;
}
@@ -63,8 +63,6 @@ struct StoredVulkanPipelineKey {
};
enum PipelineFlags {
PIPELINE_FLAG_USES_BASE_UB = (1 << 0),
PIPELINE_FLAG_USES_LIGHT_UB = (1 << 1),
PIPELINE_FLAG_USES_LINES = (1 << 2),
PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3),
};
@@ -98,8 +98,8 @@ std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) co
}
}
VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform, bool usesLighting)
: vulkan_(vulkan), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(VK_NULL_HANDLE), usesLighting_(usesLighting) {
VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform)
: vulkan_(vulkan), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(VK_NULL_HANDLE) {
PROFILE_THIS_SCOPE("shadercomp");
source_ = code;
std::string errorMessage;
@@ -252,9 +252,8 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader
VulkanVertexShader *vs = vsCache_.Get(VSID);
if (!vs) {
// Vertex shader not in cache. Let's compile it.
bool usesLighting;
GenerateVulkanGLSLVertexShader(VSID, codeBuffer_, &usesLighting);
vs = new VulkanVertexShader(vulkan_, VSID, codeBuffer_, useHWTransform, usesLighting);
GenerateVulkanGLSLVertexShader(VSID, codeBuffer_);
vs = new VulkanVertexShader(vulkan_, VSID, codeBuffer_, useHWTransform);
vsCache_.Insert(VSID, vs);
}
lastVSID_ = VSID;
@@ -375,9 +374,8 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) {
VShaderID id;
fread(&id, sizeof(id), 1, f);
bool useHWTransform = id.Bit(VS_BIT_USE_HW_TRANSFORM);
bool usesLighting;
GenerateVulkanGLSLVertexShader(id, codeBuffer_, &usesLighting);
VulkanVertexShader *vs = new VulkanVertexShader(vulkan_, id, codeBuffer_, useHWTransform, usesLighting);
GenerateVulkanGLSLVertexShader(id, codeBuffer_);
VulkanVertexShader *vs = new VulkanVertexShader(vulkan_, id, codeBuffer_, useHWTransform);
vsCache_.Insert(id, vs);
}
for (int i = 0; i < header.numFragmentShaders; i++) {
@@ -57,16 +57,13 @@ class VulkanFragmentShader {
class VulkanVertexShader {
public:
VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform, bool usesLighting);
VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform);
~VulkanVertexShader();
const std::string &source() const { return source_; }
bool Failed() const { return failed_; }
bool UseHWTransform() const { return useHWTransform_; }
bool HasLights() const {
return usesLighting_;
}
std::string GetShaderString(DebugShaderStringType type) const;
VkShaderModule GetModule() const { return module_; }
@@ -79,7 +76,6 @@ class VulkanVertexShader {
std::string source_;
bool failed_;
bool useHWTransform_;
bool usesLighting_;
VShaderID id_;
};
@@ -85,7 +85,7 @@ enum DoLightComputation {
// TODO: Skip all this if we can actually get a 16-bit depth buffer along with stencil, which
// is a bit of a rare configuration, although quite common on mobile.
bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer, bool *usesLighting) {
bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) {
char *p = buffer;
WRITE(p, "%s", vulkan_glsl_preamble);
@@ -123,10 +123,6 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer, bool *use
bool hasTexcoordTess = id.Bit(VS_BIT_HAS_TEXCOORD_TESS);
bool flipNormalTess = id.Bit(VS_BIT_NORM_REVERSE_TESS);
// The uniforms are passed in as three "clumps" that may or may not be present.
// We will memcpy the parts into place in a big buffer so we can be quite dynamic about what parts
// are present and what parts aren't, but we will not be ultra detailed about it.
*usesLighting = enableLighting || doShadeMapping;
WRITE(p, "\n");
WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseVars {\n%s} base;\n", ub_baseStr);
if (enableLighting || doShadeMapping)
@@ -2,4 +2,4 @@
#include "GPU/Common/ShaderId.h"
bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer, bool *usesLighting);
bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer);
@@ -98,9 +98,11 @@ bool AndroidVulkanContext::InitAPI() {
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)) {
VkResult res = g_Vulkan->CreateInstance(info);
if (res != VK_SUCCESS) {
ELOG("Failed to create vulkan context: %s", g_Vulkan->InitError().c_str());
System_SendMessage("toast", "No Vulkan compatible device found. Using OpenGL instead.");
VulkanSetAvailable(false);
delete g_Vulkan;
g_Vulkan = nullptr;
return false;
@@ -431,10 +431,10 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
NativeInit(2, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str());
}
retry:
// Now that we've loaded config, set javaGL.
javaGL = NativeQueryConfig("androidJavaGL") == "true";
switch (g_Config.iGPUBackend) {
case (int)GPUBackend::OPENGL:
useCPUThread = true;

0 comments on commit 940d8b4

Please sign in to comment.