00cc9309cb
de6e324bdseparate emu thread10d3daf86Roms List improvements95d202f37Let's make the rom list process on a separate thread so the emulator doesnt take ages to load.fc306967fWow the ROM Header was just completely busted. Game list view works nowbad1691eefuck this shit2b59e5f46game list in progressd26417b83remappable inputs in progressac4af8106inpute72abc240update readme430139dc9Qt6 frontend3080d4d45Fix this small bug too08cd13b85Cop0 unused functions do not actually pose a threat (as per manual). They don't do anything, so shall we.61bb4fb44make idle loop detection a little more specific with where the load goesb037de4c3SAZDFsdff12e81e73eneed to figure out why n64-systemtest loops indefinitely at some address that appears to be valid (i think it's me not invalidating the cache properly)204f0e13bidle skipping seems to work!cb8bb634asdkfjlasdf58e5c89c1Fix compilation issue on my machine (no idea)24fb2898eattempting more serious idle skipping214719577Place rsp.Step inside cached interpreter. Gains about 3 more fpsbb97dcc23mmmmm920b77d38wjkhasdfjhkasdf430ccdab4it's a start...4f42a673aCached interpreter plays Mario 64. Start looking into RSP as wellc9a030787idle skipping works!5fbda03cenew idea366637abaIdle skipping... maybe?609fa2fb0Cache instructions implemented but broken lmao. Commented out for nowe140a6d12- Stop using inheritance for CPU, instead use composition. - Introduce KAIZEN_JIT_ENABLED optional define instead of relying on __aarch64__ and the like. - More cache work68e613057prep cache impl811b4d809fix clang formatfda755f7didkd5024ebbfsmall MI refactor in preparation of (eventually) implementing the RDRAM interface properly694b45341Merge commit '206dcdedf195fb320913584180edb12c7731e396' as 'external/SDL'206dcdedfSquashed 'external/SDL/' content from commit 4d17b99d0a4d16e1cb4need to update sdl848b19920Fix compilation errordb61b5299Merge commit 'e94a94559f28e49678fbcf72199a5258137b0fe9' as 'external/imgui'e94a94559Squashed 'external/imgui/' content from commit 02e9b8cac52edb3757need to update imguic1a705e86Emulate weird JALR behaviour4b4c32f4bFix exception for "unusable COP1" in 4 instructions i missed accidentally (again)df5828142Bug putting 0s in the log everywheref8b580048Make isviewer a sink to file8241e9735Fix exception for "unusable COP1" in 4 instructions i missed accidentallyb29715f20small changesd9a620bc1make use of my new small utility library0d1aa938eAdd 'external/ircolib/' from commit 'ce3cd726c8df8388d554abf8bb55d55020eb4450'e64eb40b3Fuck git git-subtree-dir: external/ircolib git-subtree-split:de6e324bde
1780 lines
58 KiB
C++
1780 lines
58 KiB
C++
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
#include "context.hpp"
|
|
#include "limits.hpp"
|
|
#include "small_vector.hpp"
|
|
#include "environment.hpp"
|
|
#include <vector>
|
|
#include <mutex>
|
|
#include <algorithm>
|
|
#include <string.h>
|
|
|
|
#ifndef _WIN32
|
|
#include <dlfcn.h>
|
|
#elif defined(_WIN32)
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#if defined(ANDROID) && defined(HAVE_SWAPPY)
|
|
#include "swappy/swappyVk.h"
|
|
#endif
|
|
|
|
//#undef VULKAN_DEBUG
|
|
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
#pragma GCC diagnostic ignored "-Wshadow"
|
|
#endif
|
|
|
|
// We need to make sure profiles implementation sees volk symbols, not loader.
|
|
#include "vulkan/vulkan_profiles.cpp"
|
|
// Ideally there would be a vpQueryProfile which returns a const pointer to static VpProfileProperties,
|
|
// so we wouldn't have to do this.
|
|
struct ProfileHolder
|
|
{
|
|
explicit ProfileHolder(const std::string &name)
|
|
{
|
|
if (name.empty())
|
|
return;
|
|
|
|
uint32_t count;
|
|
vpGetProfiles(&count, nullptr);
|
|
props.resize(count);
|
|
vpGetProfiles(&count, props.data());
|
|
|
|
for (auto &prop : props)
|
|
if (name == prop.profileName)
|
|
profile = ∝
|
|
}
|
|
Util::SmallVector<VpProfileProperties> props;
|
|
const VpProfileProperties *profile = nullptr;
|
|
};
|
|
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
#endif
|
|
|
|
#define NV_DRIVER_VERSION_MAJOR(v) (uint32_t(v) >> 22)
|
|
|
|
namespace Vulkan
|
|
{
|
|
static constexpr ContextCreationFlags video_context_flags =
|
|
CONTEXT_CREATION_ENABLE_VIDEO_DECODE_BIT |
|
|
CONTEXT_CREATION_ENABLE_VIDEO_ENCODE_BIT;
|
|
|
|
void Context::set_instance_factory(InstanceFactory *factory)
|
|
{
|
|
instance_factory = factory;
|
|
}
|
|
|
|
void Context::set_device_factory(DeviceFactory *factory)
|
|
{
|
|
device_factory = factory;
|
|
}
|
|
|
|
void Context::set_application_info(const VkApplicationInfo *app_info)
|
|
{
|
|
user_application_info.copy_assign(app_info);
|
|
VK_ASSERT(!app_info || app_info->apiVersion >= VK_API_VERSION_1_1);
|
|
}
|
|
|
|
CopiedApplicationInfo::CopiedApplicationInfo()
|
|
{
|
|
set_default_app();
|
|
}
|
|
|
|
void CopiedApplicationInfo::set_default_app()
|
|
{
|
|
engine.clear();
|
|
application.clear();
|
|
app = {
|
|
VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr,
|
|
"Granite", 0, "Granite", 0,
|
|
VK_API_VERSION_1_1,
|
|
};
|
|
}
|
|
|
|
void CopiedApplicationInfo::copy_assign(const VkApplicationInfo *info)
|
|
{
|
|
if (info)
|
|
{
|
|
app = *info;
|
|
|
|
if (info->pApplicationName)
|
|
{
|
|
application = info->pApplicationName;
|
|
app.pApplicationName = application.c_str();
|
|
}
|
|
else
|
|
application.clear();
|
|
|
|
if (info->pEngineName)
|
|
{
|
|
engine = info->pEngineName;
|
|
app.pEngineName = engine.c_str();
|
|
}
|
|
else
|
|
engine.clear();
|
|
}
|
|
else
|
|
{
|
|
set_default_app();
|
|
}
|
|
}
|
|
|
|
const VkApplicationInfo &CopiedApplicationInfo::get_application_info() const
|
|
{
|
|
return app;
|
|
}
|
|
|
|
bool Context::init_instance(const char * const *instance_ext, uint32_t instance_ext_count, ContextCreationFlags flags)
|
|
{
|
|
destroy_device();
|
|
destroy_instance();
|
|
|
|
owned_instance = true;
|
|
if (!create_instance(instance_ext, instance_ext_count, flags))
|
|
{
|
|
destroy_instance();
|
|
LOGE("Failed to create Vulkan instance.\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Context::init_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface_compat, const char *const *device_ext,
|
|
uint32_t device_ext_count, ContextCreationFlags flags)
|
|
{
|
|
owned_device = true;
|
|
VkPhysicalDeviceFeatures features = {};
|
|
if (!create_device(gpu_, surface_compat, device_ext, device_ext_count, &features, flags))
|
|
{
|
|
destroy_device();
|
|
LOGE("Failed to create Vulkan device.\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Context::init_instance_and_device(const char * const *instance_ext, uint32_t instance_ext_count,
|
|
const char * const *device_ext, uint32_t device_ext_count,
|
|
ContextCreationFlags flags)
|
|
{
|
|
if (!init_instance(instance_ext, instance_ext_count, flags))
|
|
return false;
|
|
if (!init_device(VK_NULL_HANDLE, VK_NULL_HANDLE, device_ext, device_ext_count, flags))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static std::mutex loader_init_lock;
|
|
static bool loader_init_once;
|
|
static PFN_vkGetInstanceProcAddr instance_proc_addr;
|
|
|
|
PFN_vkGetInstanceProcAddr Context::get_instance_proc_addr()
|
|
{
|
|
return instance_proc_addr;
|
|
}
|
|
|
|
bool Context::init_loader(PFN_vkGetInstanceProcAddr addr, bool force_reload)
|
|
{
|
|
std::lock_guard<std::mutex> holder(loader_init_lock);
|
|
if (loader_init_once && !force_reload && !addr)
|
|
return true;
|
|
|
|
if (!addr)
|
|
{
|
|
#ifndef _WIN32
|
|
static void *module;
|
|
if (!module)
|
|
{
|
|
auto vulkan_path = Util::get_environment_string("GRANITE_VULKAN_LIBRARY", "");
|
|
if (!vulkan_path.empty())
|
|
module = dlopen(vulkan_path.c_str(), RTLD_LOCAL | RTLD_LAZY);
|
|
#ifdef __APPLE__
|
|
if (!module)
|
|
module = dlopen("libvulkan.1.dylib", RTLD_LOCAL | RTLD_LAZY);
|
|
if (!module)
|
|
module = dlopen("libMoltenVK.dylib", RTLD_LOCAL | RTLD_LAZY);
|
|
#else
|
|
if (!module)
|
|
module = dlopen("libvulkan.so.1", RTLD_LOCAL | RTLD_LAZY);
|
|
if (!module)
|
|
module = dlopen("libvulkan.so", RTLD_LOCAL | RTLD_LAZY);
|
|
#endif
|
|
if (!module)
|
|
return false;
|
|
}
|
|
|
|
addr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(module, "vkGetInstanceProcAddr"));
|
|
if (!addr)
|
|
return false;
|
|
#else
|
|
static HMODULE module;
|
|
if (!module)
|
|
{
|
|
module = LoadLibraryA("vulkan-1.dll");
|
|
if (!module)
|
|
return false;
|
|
}
|
|
|
|
// Ugly pointer warning workaround.
|
|
auto ptr = GetProcAddress(module, "vkGetInstanceProcAddr");
|
|
static_assert(sizeof(ptr) == sizeof(addr), "Mismatch pointer type.");
|
|
memcpy(&addr, &ptr, sizeof(ptr));
|
|
|
|
if (!addr)
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
instance_proc_addr = addr;
|
|
volkInitializeCustom(addr);
|
|
loader_init_once = true;
|
|
return true;
|
|
}
|
|
|
|
bool Context::init_device_from_instance(VkInstance instance_, VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
|
const char **required_device_extensions, unsigned num_required_device_extensions,
|
|
const VkPhysicalDeviceFeatures *required_features,
|
|
ContextCreationFlags flags)
|
|
{
|
|
destroy_device();
|
|
destroy_instance();
|
|
|
|
instance = instance_;
|
|
owned_instance = false;
|
|
owned_device = true;
|
|
|
|
if (!create_instance(nullptr, 0, flags))
|
|
return false;
|
|
|
|
if (!create_device(gpu_, surface, required_device_extensions, num_required_device_extensions, required_features, flags))
|
|
{
|
|
destroy_device();
|
|
LOGE("Failed to create Vulkan device.\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Context::destroy_device()
|
|
{
|
|
if (device != VK_NULL_HANDLE)
|
|
device_table.vkDeviceWaitIdle(device);
|
|
|
|
#if defined(ANDROID) && defined(HAVE_SWAPPY)
|
|
if (device != VK_NULL_HANDLE)
|
|
SwappyVk_destroyDevice(device);
|
|
#endif
|
|
|
|
if (owned_device && device != VK_NULL_HANDLE)
|
|
{
|
|
device_table.vkDestroyDevice(device, nullptr);
|
|
device = VK_NULL_HANDLE;
|
|
owned_device = false;
|
|
}
|
|
}
|
|
|
|
void Context::destroy_instance()
|
|
{
|
|
#ifdef VULKAN_DEBUG
|
|
if (debug_messenger)
|
|
vkDestroyDebugUtilsMessengerEXT(instance, debug_messenger, nullptr);
|
|
debug_messenger = VK_NULL_HANDLE;
|
|
#endif
|
|
|
|
if (owned_instance && instance != VK_NULL_HANDLE)
|
|
{
|
|
vkDestroyInstance(instance, nullptr);
|
|
instance = VK_NULL_HANDLE;
|
|
owned_instance = false;
|
|
}
|
|
}
|
|
|
|
Context::Context()
|
|
{
|
|
}
|
|
|
|
Context::~Context()
|
|
{
|
|
destroy_device();
|
|
destroy_instance();
|
|
}
|
|
|
|
const VkApplicationInfo &Context::get_application_info() const
|
|
{
|
|
return user_application_info.get_application_info();
|
|
}
|
|
|
|
void Context::notify_validation_error(const char *msg)
|
|
{
|
|
if (message_callback)
|
|
message_callback(msg);
|
|
}
|
|
|
|
void Context::set_notification_callback(std::function<void(const char *)> func)
|
|
{
|
|
message_callback = std::move(func);
|
|
}
|
|
|
|
#ifdef VULKAN_DEBUG
|
|
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_messenger_cb(
|
|
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
void *pUserData)
|
|
{
|
|
auto *context = static_cast<Context *>(pUserData);
|
|
|
|
switch (messageSeverity)
|
|
{
|
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
|
|
if (messageType == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
|
|
{
|
|
LOGE("[Vulkan]: Validation Error: %s\n", pCallbackData->pMessage);
|
|
context->notify_validation_error(pCallbackData->pMessage);
|
|
}
|
|
else
|
|
LOGE("[Vulkan]: Other Error: %s\n", pCallbackData->pMessage);
|
|
break;
|
|
|
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
|
|
if (messageType == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
|
|
LOGW("[Vulkan]: Validation Warning: %s\n", pCallbackData->pMessage);
|
|
else
|
|
LOGW("[Vulkan]: Other Warning: %s\n", pCallbackData->pMessage);
|
|
break;
|
|
|
|
#if 0
|
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
|
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
|
|
if (messageType == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
|
|
LOGI("[Vulkan]: Validation Info: %s\n", pCallbackData->pMessage);
|
|
else
|
|
LOGI("[Vulkan]: Other Info: %s\n", pCallbackData->pMessage);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return VK_FALSE;
|
|
}
|
|
|
|
bool log_object_names = false;
|
|
for (uint32_t i = 0; i < pCallbackData->objectCount; i++)
|
|
{
|
|
auto *name = pCallbackData->pObjects[i].pObjectName;
|
|
if (name)
|
|
{
|
|
log_object_names = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (log_object_names)
|
|
{
|
|
for (uint32_t i = 0; i < pCallbackData->objectCount; i++)
|
|
{
|
|
auto *name = pCallbackData->pObjects[i].pObjectName;
|
|
LOGI(" Object #%u: %s\n", i, name ? name : "N/A");
|
|
}
|
|
}
|
|
|
|
return VK_FALSE;
|
|
}
|
|
#endif
|
|
|
|
void Context::set_required_profile(const char *profile, bool strict)
|
|
{
|
|
if (profile)
|
|
required_profile = profile;
|
|
else
|
|
required_profile.clear();
|
|
required_profile_strict = strict;
|
|
}
|
|
|
|
bool Context::init_profile()
|
|
{
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
if (required_profile.empty())
|
|
{
|
|
if (Util::get_environment("GRANITE_VULKAN_PROFILE", required_profile))
|
|
LOGI("Overriding profile: %s\n", required_profile.c_str());
|
|
|
|
required_profile_strict = Util::get_environment_bool("GRANITE_VULKAN_PROFILE_STRICT", false);
|
|
if (required_profile_strict)
|
|
LOGI("Using profile strictness.\n");
|
|
}
|
|
|
|
if (required_profile.empty())
|
|
return true;
|
|
|
|
ProfileHolder profile{required_profile};
|
|
|
|
if (!profile.profile)
|
|
{
|
|
LOGW("No profile matches %s.\n", required_profile.c_str());
|
|
return false;
|
|
}
|
|
|
|
VkBool32 supported = VK_FALSE;
|
|
if (vpGetInstanceProfileSupport(nullptr, profile.profile, &supported) != VK_SUCCESS || !supported)
|
|
{
|
|
LOGE("Profile %s is not supported.\n", required_profile.c_str());
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
VkResult Context::create_instance_from_profile(const VkInstanceCreateInfo &info, VkInstance *pInstance)
|
|
{
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
ProfileHolder holder{required_profile};
|
|
if (!holder.profile)
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
|
|
if (instance_factory)
|
|
{
|
|
// Can override vkGetInstanceProcAddr (macro define) and override vkCreateInstance
|
|
// to a TLS magic trampoline if we really have to.
|
|
LOGE("Instance factory currently not supported with profiles.\n");
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
}
|
|
|
|
VpInstanceCreateInfo vp_info = {};
|
|
vp_info.pCreateInfo = &info;
|
|
vp_info.pProfile = holder.profile;
|
|
// Any extra extensions we add for instances are essential, like WSI stuff.
|
|
vp_info.flags = VP_INSTANCE_CREATE_MERGE_EXTENSIONS_BIT;
|
|
|
|
VkResult result;
|
|
if ((result = vpCreateInstance(&vp_info, nullptr, pInstance)) != VK_SUCCESS)
|
|
LOGE("Failed to create instance from profile.\n");
|
|
|
|
return result;
|
|
#else
|
|
(void)info;
|
|
(void)pInstance;
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
#endif
|
|
}
|
|
|
|
VkResult Context::create_device_from_profile(const VkDeviceCreateInfo &info, VkDevice *pDevice)
|
|
{
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
ProfileHolder holder{required_profile};
|
|
if (!holder.profile)
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
|
|
if (device_factory)
|
|
{
|
|
// Need TLS hackery like instance.
|
|
LOGE("Device factory currently not supported with profiles.\n");
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
}
|
|
|
|
auto tmp_info = info;
|
|
|
|
VpDeviceCreateInfo vp_info = {};
|
|
vp_info.pProfile = holder.profile;
|
|
vp_info.pCreateInfo = &tmp_info;
|
|
vp_info.flags |= VP_DEVICE_CREATE_DISABLE_ROBUST_ACCESS;
|
|
|
|
if (required_profile_strict)
|
|
{
|
|
tmp_info.enabledExtensionCount = 0;
|
|
tmp_info.ppEnabledExtensionNames = nullptr;
|
|
tmp_info.pNext = nullptr;
|
|
tmp_info.pEnabledFeatures = nullptr;
|
|
}
|
|
else
|
|
{
|
|
vp_info.flags = VP_DEVICE_CREATE_MERGE_EXTENSIONS_BIT | VP_DEVICE_CREATE_OVERRIDE_FEATURES_BIT;
|
|
}
|
|
|
|
VkResult result;
|
|
if ((result = vpCreateDevice(gpu, &vp_info, nullptr, pDevice)) != VK_SUCCESS)
|
|
LOGE("Failed to create device from profile.\n");
|
|
return result;
|
|
#else
|
|
(void)info;
|
|
(void)pDevice;
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
#endif
|
|
}
|
|
|
|
VkApplicationInfo Context::get_promoted_application_info() const
|
|
{
|
|
auto app_info = get_application_info();
|
|
|
|
// Granite min-req is 1.1.
|
|
app_info.apiVersion = std::max(VK_API_VERSION_1_1, app_info.apiVersion);
|
|
|
|
// Target Vulkan 1.3 if available.
|
|
app_info.apiVersion = std::max(app_info.apiVersion, std::min(VK_API_VERSION_1_3, volkGetInstanceVersion()));
|
|
|
|
return app_info;
|
|
}
|
|
|
|
bool Context::create_instance(const char * const *instance_ext, uint32_t instance_ext_count, ContextCreationFlags flags)
|
|
{
|
|
VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
|
|
auto app_info = get_promoted_application_info();
|
|
|
|
if (volkGetInstanceVersion() < app_info.apiVersion)
|
|
{
|
|
LOGE("Vulkan loader does not support required Vulkan version.\n");
|
|
return false;
|
|
}
|
|
|
|
info.pApplicationInfo = &app_info;
|
|
|
|
std::vector<const char *> instance_exts;
|
|
std::vector<const char *> instance_layers;
|
|
for (uint32_t i = 0; i < instance_ext_count; i++)
|
|
instance_exts.push_back(instance_ext[i]);
|
|
|
|
uint32_t ext_count = 0;
|
|
vkEnumerateInstanceExtensionProperties(nullptr, &ext_count, nullptr);
|
|
std::vector<VkExtensionProperties> queried_extensions(ext_count);
|
|
if (ext_count)
|
|
vkEnumerateInstanceExtensionProperties(nullptr, &ext_count, queried_extensions.data());
|
|
|
|
uint32_t layer_count = 0;
|
|
vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
|
|
std::vector<VkLayerProperties> queried_layers(layer_count);
|
|
if (layer_count)
|
|
vkEnumerateInstanceLayerProperties(&layer_count, queried_layers.data());
|
|
|
|
LOGI("Layer count: %u\n", layer_count);
|
|
for (auto &layer : queried_layers)
|
|
LOGI("Found layer: %s.\n", layer.layerName);
|
|
|
|
const auto has_extension = [&](const char *name) -> bool {
|
|
auto itr = find_if(begin(queried_extensions), end(queried_extensions), [name](const VkExtensionProperties &e) -> bool {
|
|
return strcmp(e.extensionName, name) == 0;
|
|
});
|
|
return itr != end(queried_extensions);
|
|
};
|
|
|
|
for (uint32_t i = 0; i < instance_ext_count; i++)
|
|
if (!has_extension(instance_ext[i]))
|
|
return false;
|
|
|
|
if (has_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
|
|
{
|
|
instance_exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
ext.supports_debug_utils = true;
|
|
}
|
|
|
|
auto itr = std::find_if(instance_ext, instance_ext + instance_ext_count, [](const char *name) {
|
|
return strcmp(name, VK_KHR_SURFACE_EXTENSION_NAME) == 0;
|
|
});
|
|
bool has_surface_extension = itr != (instance_ext + instance_ext_count);
|
|
|
|
if (has_surface_extension && has_extension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME))
|
|
{
|
|
instance_exts.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);
|
|
ext.supports_surface_capabilities2 = true;
|
|
}
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT) != 0 &&
|
|
has_surface_extension &&
|
|
has_extension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME))
|
|
{
|
|
instance_exts.push_back(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
|
|
ext.supports_swapchain_colorspace = true;
|
|
}
|
|
|
|
#ifdef VULKAN_DEBUG
|
|
const auto has_layer = [&](const char *name) -> bool {
|
|
auto layer_itr = find_if(begin(queried_layers), end(queried_layers), [name](const VkLayerProperties &e) -> bool {
|
|
return strcmp(e.layerName, name) == 0;
|
|
});
|
|
return layer_itr != end(queried_layers);
|
|
};
|
|
|
|
force_no_validation = Util::get_environment_bool("GRANITE_VULKAN_NO_VALIDATION", false);
|
|
|
|
if (!force_no_validation && has_layer("VK_LAYER_KHRONOS_validation"))
|
|
{
|
|
instance_layers.push_back("VK_LAYER_KHRONOS_validation");
|
|
LOGI("Enabling VK_LAYER_KHRONOS_validation.\n");
|
|
|
|
uint32_t layer_ext_count = 0;
|
|
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &layer_ext_count, nullptr);
|
|
std::vector<VkExtensionProperties> layer_exts(layer_ext_count);
|
|
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &layer_ext_count, layer_exts.data());
|
|
|
|
#if 0
|
|
VkValidationFeaturesEXT validation_features = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT };
|
|
|
|
// Tons of false positives around timeline semaphores atm, so don't bother.
|
|
if (find_if(begin(layer_exts), end(layer_exts), [](const VkExtensionProperties &e) {
|
|
return strcmp(e.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) == 0;
|
|
}) != end(layer_exts))
|
|
{
|
|
instance_exts.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
|
|
static const VkValidationFeatureEnableEXT validation_sync_features[1] = {
|
|
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
|
|
};
|
|
LOGI("Enabling VK_EXT_validation_features for synchronization validation.\n");
|
|
validation_features.enabledValidationFeatureCount = 1;
|
|
validation_features.pEnabledValidationFeatures = validation_sync_features;
|
|
info.pNext = &validation_features;
|
|
}
|
|
#endif
|
|
|
|
if (!ext.supports_debug_utils &&
|
|
find_if(begin(layer_exts), end(layer_exts), [](const VkExtensionProperties &e) {
|
|
return strcmp(e.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0;
|
|
}) != end(layer_exts))
|
|
{
|
|
instance_exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
ext.supports_debug_utils = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (ext.supports_surface_capabilities2 && has_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME))
|
|
{
|
|
#ifdef VULKAN_DEBUG
|
|
// It seems like there are some bugs with EXT_swapchain_maint1 in VVL atm.
|
|
const bool support_maint1 = force_no_validation;
|
|
#else
|
|
constexpr bool support_maint1 = true;
|
|
#endif
|
|
|
|
if (support_maint1)
|
|
{
|
|
instance_exts.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
|
|
ext.supports_surface_maintenance1 = true;
|
|
}
|
|
}
|
|
|
|
info.enabledExtensionCount = instance_exts.size();
|
|
info.ppEnabledExtensionNames = instance_exts.empty() ? nullptr : instance_exts.data();
|
|
info.enabledLayerCount = instance_layers.size();
|
|
info.ppEnabledLayerNames = instance_layers.empty() ? nullptr : instance_layers.data();
|
|
|
|
for (auto *ext_name : instance_exts)
|
|
LOGI("Enabling instance extension: %s.\n", ext_name);
|
|
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
if (!init_profile())
|
|
{
|
|
LOGE("Profile is not supported.\n");
|
|
return false;
|
|
}
|
|
|
|
if (instance == VK_NULL_HANDLE && !required_profile.empty())
|
|
if (create_instance_from_profile(info, &instance) != VK_SUCCESS)
|
|
return false;
|
|
#endif
|
|
|
|
// instance != VK_NULL_HANDLE here is deprecated and somewhat broken.
|
|
// For libretro Vulkan context negotiation v1.
|
|
if (instance == VK_NULL_HANDLE)
|
|
{
|
|
if (instance_factory)
|
|
{
|
|
instance = instance_factory->create_instance(&info);
|
|
if (instance == VK_NULL_HANDLE)
|
|
return false;
|
|
}
|
|
else if (vkCreateInstance(&info, nullptr, &instance) != VK_SUCCESS)
|
|
return false;
|
|
|
|
// If we have a pre-existing instance, we can only assume Vulkan 1.1 in legacy interface.
|
|
ext.instance_api_core_version = app_info.apiVersion;
|
|
}
|
|
|
|
enabled_instance_extensions = std::move(instance_exts);
|
|
ext.instance_extensions = enabled_instance_extensions.data();
|
|
ext.num_instance_extensions = uint32_t(enabled_instance_extensions.size());
|
|
|
|
volkLoadInstance(instance);
|
|
|
|
#if defined(VULKAN_DEBUG)
|
|
if (ext.supports_debug_utils)
|
|
{
|
|
VkDebugUtilsMessengerCreateInfoEXT debug_info = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };
|
|
debug_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
|
|
debug_info.pfnUserCallback = vulkan_messenger_cb;
|
|
debug_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
|
|
debug_info.pUserData = this;
|
|
|
|
// For some reason, this segfaults Android, sigh ... We get relevant output in logcat anyways.
|
|
if (vkCreateDebugUtilsMessengerEXT)
|
|
vkCreateDebugUtilsMessengerEXT(instance, &debug_info, nullptr, &debug_messenger);
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
static unsigned device_score(VkPhysicalDevice &gpu)
|
|
{
|
|
VkPhysicalDeviceProperties props = {};
|
|
vkGetPhysicalDeviceProperties(gpu, &props);
|
|
|
|
if (props.apiVersion < VK_API_VERSION_1_1)
|
|
return 0;
|
|
|
|
switch (props.deviceType)
|
|
{
|
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
|
return 3;
|
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
|
return 2;
|
|
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
QueueInfo::QueueInfo()
|
|
{
|
|
for (auto &index : family_indices)
|
|
index = VK_QUEUE_FAMILY_IGNORED;
|
|
}
|
|
|
|
bool Context::physical_device_supports_surface_and_profile(VkPhysicalDevice candidate_gpu, VkSurfaceKHR surface) const
|
|
{
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
if (!required_profile.empty())
|
|
{
|
|
ProfileHolder holder{required_profile};
|
|
if (!holder.profile)
|
|
return false;
|
|
|
|
VkBool32 supported = VK_FALSE;
|
|
if (vpGetPhysicalDeviceProfileSupport(instance, candidate_gpu, holder.profile, &supported) != VK_SUCCESS ||
|
|
!supported)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (surface == VK_NULL_HANDLE)
|
|
return true;
|
|
|
|
VkPhysicalDeviceProperties dev_props;
|
|
vkGetPhysicalDeviceProperties(candidate_gpu, &dev_props);
|
|
if (dev_props.limits.maxUniformBufferRange < VULKAN_MAX_UBO_SIZE)
|
|
{
|
|
LOGW("Device does not support 64 KiB UBOs. Must be *ancient* mobile driver.\n");
|
|
return false;
|
|
}
|
|
|
|
if (dev_props.apiVersion < VK_API_VERSION_1_1)
|
|
{
|
|
LOGW("Device does not support Vulkan 1.1. Skipping.\n");
|
|
return false;
|
|
}
|
|
|
|
uint32_t family_count = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(candidate_gpu, &family_count, nullptr);
|
|
Util::SmallVector<VkQueueFamilyProperties> props(family_count);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(candidate_gpu, &family_count, props.data());
|
|
|
|
for (uint32_t i = 0; i < family_count; i++)
|
|
{
|
|
// A graphics queue candidate must support present for us to select it.
|
|
if ((props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
|
|
{
|
|
VkBool32 supported = VK_FALSE;
|
|
if (vkGetPhysicalDeviceSurfaceSupportKHR(candidate_gpu, i, surface, &supported) == VK_SUCCESS && supported)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
|
const char * const *required_device_extensions, uint32_t num_required_device_extensions,
|
|
const VkPhysicalDeviceFeatures *required_features,
|
|
ContextCreationFlags flags)
|
|
{
|
|
gpu = gpu_;
|
|
if (gpu == VK_NULL_HANDLE)
|
|
{
|
|
uint32_t gpu_count = 0;
|
|
if (vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr) != VK_SUCCESS)
|
|
return false;
|
|
|
|
if (gpu_count == 0)
|
|
return false;
|
|
|
|
std::vector<VkPhysicalDevice> gpus(gpu_count);
|
|
if (vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.data()) != VK_SUCCESS)
|
|
return false;
|
|
|
|
for (auto &g : gpus)
|
|
{
|
|
VkPhysicalDeviceProperties props;
|
|
vkGetPhysicalDeviceProperties(g, &props);
|
|
LOGI("Found Vulkan GPU: %s\n", props.deviceName);
|
|
LOGI(" API: %u.%u.%u\n",
|
|
VK_VERSION_MAJOR(props.apiVersion),
|
|
VK_VERSION_MINOR(props.apiVersion),
|
|
VK_VERSION_PATCH(props.apiVersion));
|
|
LOGI(" Driver: %u.%u.%u\n",
|
|
VK_VERSION_MAJOR(props.driverVersion),
|
|
VK_VERSION_MINOR(props.driverVersion),
|
|
VK_VERSION_PATCH(props.driverVersion));
|
|
}
|
|
|
|
int gpu_index = Util::get_environment_int("GRANITE_VULKAN_DEVICE_INDEX", -1);
|
|
if (gpu_index >= 0 && gpu_index < int(gpu_count))
|
|
gpu = gpus[gpu_index];
|
|
|
|
if (gpu != VK_NULL_HANDLE)
|
|
{
|
|
if (!physical_device_supports_surface_and_profile(gpu, surface))
|
|
{
|
|
LOGE("Selected physical device which does not support surface.\n");
|
|
gpu = VK_NULL_HANDLE;
|
|
}
|
|
}
|
|
|
|
if (gpu == VK_NULL_HANDLE)
|
|
{
|
|
unsigned max_score = 0;
|
|
// Prefer earlier entries in list.
|
|
for (size_t i = gpus.size(); i; i--)
|
|
{
|
|
unsigned score = device_score(gpus[i - 1]);
|
|
if (score >= max_score && physical_device_supports_surface_and_profile(gpus[i - 1], surface))
|
|
{
|
|
max_score = score;
|
|
gpu = gpus[i - 1];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gpu == VK_NULL_HANDLE)
|
|
{
|
|
LOGE("Found not GPU which supports surface.\n");
|
|
return false;
|
|
}
|
|
}
|
|
else if (!physical_device_supports_surface_and_profile(gpu, surface))
|
|
{
|
|
LOGE("Selected physical device does not support surface.\n");
|
|
return false;
|
|
}
|
|
|
|
std::vector<VkExtensionProperties> queried_extensions;
|
|
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
// Only allow extensions that profile declares.
|
|
ProfileHolder profile{required_profile};
|
|
if (profile.profile && required_profile_strict)
|
|
{
|
|
uint32_t ext_count = 0;
|
|
vpGetProfileDeviceExtensionProperties(profile.profile, &ext_count, nullptr);
|
|
queried_extensions.resize(ext_count);
|
|
if (ext_count)
|
|
vpGetProfileDeviceExtensionProperties(profile.profile, &ext_count, queried_extensions.data());
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
uint32_t ext_count = 0;
|
|
vkEnumerateDeviceExtensionProperties(gpu, nullptr, &ext_count, nullptr);
|
|
queried_extensions.resize(ext_count);
|
|
if (ext_count)
|
|
vkEnumerateDeviceExtensionProperties(gpu, nullptr, &ext_count, queried_extensions.data());
|
|
}
|
|
|
|
const auto has_extension = [&](const char *name) -> bool {
|
|
auto itr = find_if(begin(queried_extensions), end(queried_extensions), [name](const VkExtensionProperties &e) -> bool {
|
|
return strcmp(e.extensionName, name) == 0;
|
|
});
|
|
return itr != end(queried_extensions);
|
|
};
|
|
|
|
for (uint32_t i = 0; i < num_required_device_extensions; i++)
|
|
if (!has_extension(required_device_extensions[i]))
|
|
return false;
|
|
|
|
vkGetPhysicalDeviceProperties(gpu, &gpu_props);
|
|
// We can use core device functionality if enabled VkInstance apiVersion and physical device supports it.
|
|
ext.device_api_core_version = std::min(ext.instance_api_core_version, gpu_props.apiVersion);
|
|
|
|
LOGI("Using Vulkan GPU: %s\n", gpu_props.deviceName);
|
|
|
|
// FFmpeg integration requires Vulkan 1.3 core for physical device.
|
|
uint32_t minimum_api_version = (flags & video_context_flags) ? VK_API_VERSION_1_3 : VK_API_VERSION_1_1;
|
|
if (ext.device_api_core_version < minimum_api_version && (flags & video_context_flags) != 0)
|
|
{
|
|
LOGW("Requested FFmpeg-enabled context, but Vulkan 1.3 was not supported. Falling back to 1.1 without support.\n");
|
|
minimum_api_version = VK_API_VERSION_1_1;
|
|
flags &= ~video_context_flags;
|
|
}
|
|
|
|
if (ext.device_api_core_version < minimum_api_version)
|
|
{
|
|
LOGE("Found no Vulkan GPU which supports Vulkan 1.%u.\n",
|
|
VK_API_VERSION_MINOR(minimum_api_version));
|
|
return false;
|
|
}
|
|
|
|
vkGetPhysicalDeviceMemoryProperties(gpu, &mem_props);
|
|
|
|
uint32_t queue_family_count = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties2(gpu, &queue_family_count, nullptr);
|
|
Util::SmallVector<VkQueueFamilyProperties2> queue_props(queue_family_count);
|
|
Util::SmallVector<VkQueueFamilyVideoPropertiesKHR> video_queue_props2(queue_family_count);
|
|
|
|
if ((flags & video_context_flags) != 0 && has_extension(VK_KHR_VIDEO_QUEUE_EXTENSION_NAME))
|
|
ext.supports_video_queue = true;
|
|
|
|
for (uint32_t i = 0; i < queue_family_count; i++)
|
|
{
|
|
queue_props[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
|
|
if (ext.supports_video_queue)
|
|
{
|
|
queue_props[i].pNext = &video_queue_props2[i];
|
|
video_queue_props2[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_VIDEO_PROPERTIES_KHR;
|
|
}
|
|
}
|
|
|
|
Util::SmallVector<uint32_t> queue_offsets(queue_family_count);
|
|
Util::SmallVector<Util::SmallVector<float, QUEUE_INDEX_COUNT>> queue_priorities(queue_family_count);
|
|
vkGetPhysicalDeviceQueueFamilyProperties2(gpu, &queue_family_count, queue_props.data());
|
|
|
|
queue_info = {};
|
|
uint32_t queue_indices[QUEUE_INDEX_COUNT] = {};
|
|
|
|
const auto find_vacant_queue = [&](uint32_t &family, uint32_t &index,
|
|
VkQueueFlags required, VkQueueFlags ignore_flags,
|
|
float priority) -> bool {
|
|
for (unsigned family_index = 0; family_index < queue_family_count; family_index++)
|
|
{
|
|
if ((queue_props[family_index].queueFamilyProperties.queueFlags & ignore_flags) != 0)
|
|
continue;
|
|
|
|
// A graphics queue candidate must support present for us to select it.
|
|
if ((required & VK_QUEUE_GRAPHICS_BIT) != 0 && surface != VK_NULL_HANDLE)
|
|
{
|
|
VkBool32 supported = VK_FALSE;
|
|
if (vkGetPhysicalDeviceSurfaceSupportKHR(gpu, family_index, surface, &supported) != VK_SUCCESS || !supported)
|
|
continue;
|
|
}
|
|
|
|
if (queue_props[family_index].queueFamilyProperties.queueCount &&
|
|
(queue_props[family_index].queueFamilyProperties.queueFlags & required) == required)
|
|
{
|
|
family = family_index;
|
|
queue_props[family_index].queueFamilyProperties.queueCount--;
|
|
index = queue_offsets[family_index]++;
|
|
queue_priorities[family_index].push_back(priority);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
if (!find_vacant_queue(queue_info.family_indices[QUEUE_INDEX_GRAPHICS],
|
|
queue_indices[QUEUE_INDEX_GRAPHICS],
|
|
VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, 0, 0.5f))
|
|
{
|
|
LOGE("Could not find suitable graphics queue.\n");
|
|
return false;
|
|
}
|
|
|
|
// XXX: This assumes timestamp valid bits is the same for all queue types.
|
|
queue_info.timestamp_valid_bits =
|
|
queue_props[queue_info.family_indices[QUEUE_INDEX_GRAPHICS]].queueFamilyProperties.timestampValidBits;
|
|
|
|
// Prefer standalone compute queue. If not, fall back to another graphics queue.
|
|
if (!find_vacant_queue(queue_info.family_indices[QUEUE_INDEX_COMPUTE], queue_indices[QUEUE_INDEX_COMPUTE],
|
|
VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT, 0.5f) &&
|
|
!find_vacant_queue(queue_info.family_indices[QUEUE_INDEX_COMPUTE], queue_indices[QUEUE_INDEX_COMPUTE],
|
|
VK_QUEUE_COMPUTE_BIT, 0, 0.5f))
|
|
{
|
|
// Fallback to the graphics queue if we must.
|
|
queue_info.family_indices[QUEUE_INDEX_COMPUTE] = queue_info.family_indices[QUEUE_INDEX_GRAPHICS];
|
|
queue_indices[QUEUE_INDEX_COMPUTE] = queue_indices[QUEUE_INDEX_GRAPHICS];
|
|
}
|
|
|
|
// For transfer, try to find a queue which only supports transfer, e.g. DMA queue.
|
|
// If not, fallback to a dedicated compute queue.
|
|
// Finally, fallback to same queue as compute.
|
|
if (!find_vacant_queue(queue_info.family_indices[QUEUE_INDEX_TRANSFER], queue_indices[QUEUE_INDEX_TRANSFER],
|
|
VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, 0.5f) &&
|
|
!find_vacant_queue(queue_info.family_indices[QUEUE_INDEX_TRANSFER], queue_indices[QUEUE_INDEX_TRANSFER],
|
|
VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT, 0.5f))
|
|
{
|
|
queue_info.family_indices[QUEUE_INDEX_TRANSFER] = queue_info.family_indices[QUEUE_INDEX_COMPUTE];
|
|
queue_indices[QUEUE_INDEX_TRANSFER] = queue_indices[QUEUE_INDEX_COMPUTE];
|
|
}
|
|
|
|
if (ext.supports_video_queue)
|
|
{
|
|
if ((flags & CONTEXT_CREATION_ENABLE_VIDEO_DECODE_BIT) != 0)
|
|
{
|
|
if (!find_vacant_queue(queue_info.family_indices[QUEUE_INDEX_VIDEO_DECODE],
|
|
queue_indices[QUEUE_INDEX_VIDEO_DECODE],
|
|
VK_QUEUE_VIDEO_DECODE_BIT_KHR, 0, 0.5f))
|
|
{
|
|
queue_info.family_indices[QUEUE_INDEX_VIDEO_DECODE] = VK_QUEUE_FAMILY_IGNORED;
|
|
queue_indices[QUEUE_INDEX_VIDEO_DECODE] = UINT32_MAX;
|
|
}
|
|
}
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_VIDEO_ENCODE_BIT) != 0)
|
|
{
|
|
if (!find_vacant_queue(queue_info.family_indices[QUEUE_INDEX_VIDEO_ENCODE],
|
|
queue_indices[QUEUE_INDEX_VIDEO_ENCODE],
|
|
VK_QUEUE_VIDEO_ENCODE_BIT_KHR, 0, 0.5f))
|
|
{
|
|
queue_info.family_indices[QUEUE_INDEX_VIDEO_ENCODE] = VK_QUEUE_FAMILY_IGNORED;
|
|
queue_indices[QUEUE_INDEX_VIDEO_ENCODE] = UINT32_MAX;
|
|
}
|
|
}
|
|
}
|
|
|
|
VkDeviceCreateInfo device_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
|
|
|
|
Util::SmallVector<VkDeviceQueueCreateInfo> queue_infos;
|
|
for (uint32_t family_index = 0; family_index < queue_family_count; family_index++)
|
|
{
|
|
if (queue_offsets[family_index] == 0)
|
|
continue;
|
|
|
|
VkDeviceQueueCreateInfo info = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
|
|
info.queueFamilyIndex = family_index;
|
|
info.queueCount = queue_offsets[family_index];
|
|
info.pQueuePriorities = queue_priorities[family_index].data();
|
|
queue_infos.push_back(info);
|
|
}
|
|
device_info.pQueueCreateInfos = queue_infos.data();
|
|
device_info.queueCreateInfoCount = uint32_t(queue_infos.size());
|
|
|
|
std::vector<const char *> enabled_extensions;
|
|
|
|
bool requires_swapchain = false;
|
|
for (uint32_t i = 0; i < num_required_device_extensions; i++)
|
|
{
|
|
enabled_extensions.push_back(required_device_extensions[i]);
|
|
if (strcmp(required_device_extensions[i], VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0)
|
|
requires_swapchain = true;
|
|
else if (strcmp(required_device_extensions[i], VK_KHR_PRESENT_ID_EXTENSION_NAME) == 0 ||
|
|
strcmp(required_device_extensions[i], VK_KHR_PRESENT_WAIT_EXTENSION_NAME) == 0 ||
|
|
strcmp(required_device_extensions[i], VK_EXT_HDR_METADATA_EXTENSION_NAME) == 0 ||
|
|
strcmp(required_device_extensions[i], VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME) == 0)
|
|
{
|
|
flags |= CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT;
|
|
}
|
|
}
|
|
|
|
#if defined(ANDROID) && defined(HAVE_SWAPPY)
|
|
// Enable additional extensions required by SwappyVk.
|
|
std::unique_ptr<char[]> swappy_str_buffer;
|
|
if (requires_swapchain)
|
|
{
|
|
uint32_t required_swappy_extension_count = 0;
|
|
|
|
// I'm really not sure why the API just didn't return static const char * strings here,
|
|
// but oh well.
|
|
SwappyVk_determineDeviceExtensions(gpu, uint32_t(queried_extensions.size()),
|
|
queried_extensions.data(),
|
|
&required_swappy_extension_count,
|
|
nullptr);
|
|
swappy_str_buffer.reset(new char[required_swappy_extension_count * (VK_MAX_EXTENSION_NAME_SIZE + 1)]);
|
|
|
|
std::vector<char *> extension_buffer;
|
|
extension_buffer.reserve(required_swappy_extension_count);
|
|
for (uint32_t i = 0; i < required_swappy_extension_count; i++)
|
|
extension_buffer.push_back(swappy_str_buffer.get() + i * (VK_MAX_EXTENSION_NAME_SIZE + 1));
|
|
SwappyVk_determineDeviceExtensions(gpu, uint32_t(queried_extensions.size()),
|
|
queried_extensions.data(),
|
|
&required_swappy_extension_count,
|
|
extension_buffer.data());
|
|
|
|
for (auto *required_ext : extension_buffer)
|
|
enabled_extensions.push_back(required_ext);
|
|
}
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
if (ext.supports_surface_capabilities2 && has_extension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME))
|
|
{
|
|
ext.supports_full_screen_exclusive = true;
|
|
enabled_extensions.push_back(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME);
|
|
}
|
|
#endif
|
|
|
|
if (
|
|
#ifdef _WIN32
|
|
has_extension(VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME) &&
|
|
has_extension(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME)
|
|
#else
|
|
has_extension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME) &&
|
|
has_extension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)
|
|
#endif
|
|
)
|
|
{
|
|
ext.supports_external = true;
|
|
#ifdef _WIN32
|
|
enabled_extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME);
|
|
enabled_extensions.push_back(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
|
|
#else
|
|
enabled_extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
|
|
enabled_extensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
|
|
#endif
|
|
}
|
|
else
|
|
ext.supports_external = false;
|
|
|
|
if (has_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME))
|
|
{
|
|
ext.supports_calibrated_timestamps = true;
|
|
enabled_extensions.push_back(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME);
|
|
ext.supports_conservative_rasterization = true;
|
|
}
|
|
|
|
if (ext.device_api_core_version < VK_API_VERSION_1_2)
|
|
{
|
|
if (!has_extension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME))
|
|
{
|
|
LOGE("VK_KHR_create_renderpass2 is not supported.\n");
|
|
return false;
|
|
}
|
|
|
|
enabled_extensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
|
|
|
|
if (has_extension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME))
|
|
{
|
|
ext.supports_image_format_list = true;
|
|
enabled_extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
|
|
}
|
|
}
|
|
else
|
|
ext.supports_image_format_list = true;
|
|
|
|
// Physical device functionality.
|
|
ext.supports_format_feature_flags2 = ext.device_api_core_version >= VK_API_VERSION_1_3 ||
|
|
has_extension(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME);
|
|
|
|
if (has_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME))
|
|
ext.supports_tooling_info = true;
|
|
|
|
if (ext.supports_video_queue)
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_VIDEO_QUEUE_EXTENSION_NAME);
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_VIDEO_DECODE_BIT) != 0 &&
|
|
has_extension(VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME);
|
|
ext.supports_video_decode_queue = true;
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_VIDEO_H264_BIT) != 0 &&
|
|
has_extension(VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME);
|
|
|
|
if (queue_info.family_indices[QUEUE_INDEX_VIDEO_DECODE] != VK_QUEUE_FAMILY_IGNORED)
|
|
{
|
|
ext.supports_video_decode_h264 =
|
|
(video_queue_props2[queue_info.family_indices[QUEUE_INDEX_VIDEO_DECODE]].videoCodecOperations &
|
|
VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR) != 0;
|
|
}
|
|
}
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_VIDEO_H265_BIT) != 0 &&
|
|
has_extension(VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME);
|
|
|
|
if (queue_info.family_indices[QUEUE_INDEX_VIDEO_DECODE] != VK_QUEUE_FAMILY_IGNORED)
|
|
{
|
|
ext.supports_video_decode_h265 =
|
|
(video_queue_props2[queue_info.family_indices[QUEUE_INDEX_VIDEO_DECODE]].videoCodecOperations &
|
|
VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR) != 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_VIDEO_ENCODE_BIT) != 0 &&
|
|
has_extension(VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME);
|
|
ext.supports_video_encode_queue = true;
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_VIDEO_H264_BIT) != 0 &&
|
|
has_extension(VK_KHR_VIDEO_ENCODE_H264_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_VIDEO_ENCODE_H264_EXTENSION_NAME);
|
|
|
|
if (queue_info.family_indices[QUEUE_INDEX_VIDEO_ENCODE] != VK_QUEUE_FAMILY_IGNORED)
|
|
{
|
|
ext.supports_video_encode_h264 =
|
|
(video_queue_props2[queue_info.family_indices[QUEUE_INDEX_VIDEO_ENCODE]].videoCodecOperations &
|
|
VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR) != 0;
|
|
}
|
|
}
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_VIDEO_H265_BIT) != 0 &&
|
|
has_extension(VK_KHR_VIDEO_ENCODE_H265_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_VIDEO_ENCODE_H265_EXTENSION_NAME);
|
|
|
|
if (queue_info.family_indices[QUEUE_INDEX_VIDEO_ENCODE] != VK_QUEUE_FAMILY_IGNORED)
|
|
{
|
|
ext.supports_video_encode_h265 =
|
|
(video_queue_props2[queue_info.family_indices[QUEUE_INDEX_VIDEO_ENCODE]].videoCodecOperations &
|
|
VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR) != 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pdf2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
|
|
void **ppNext = &pdf2.pNext;
|
|
|
|
#define ADD_CHAIN(s, type) do { \
|
|
s.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ ## type; \
|
|
s.pNext = nullptr; \
|
|
*ppNext = &(s); \
|
|
ppNext = &((s).pNext); \
|
|
} while(0)
|
|
|
|
if (ext.device_api_core_version >= VK_API_VERSION_1_2)
|
|
{
|
|
ADD_CHAIN(ext.vk11_features, VULKAN_1_1_FEATURES);
|
|
ADD_CHAIN(ext.vk12_features, VULKAN_1_2_FEATURES);
|
|
}
|
|
else
|
|
{
|
|
if (has_extension(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME))
|
|
ADD_CHAIN(ext.host_query_reset_features, HOST_QUERY_RESET_FEATURES);
|
|
|
|
if (has_extension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME))
|
|
{
|
|
ADD_CHAIN(ext.float16_int8_features, FLOAT16_INT8_FEATURES_KHR);
|
|
enabled_extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
|
|
}
|
|
|
|
if (has_extension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME))
|
|
{
|
|
ADD_CHAIN(ext.storage_16bit_features, 16BIT_STORAGE_FEATURES_KHR);
|
|
enabled_extensions.push_back(VK_KHR_16BIT_STORAGE_EXTENSION_NAME);
|
|
}
|
|
|
|
if (has_extension(VK_KHR_8BIT_STORAGE_EXTENSION_NAME))
|
|
{
|
|
ADD_CHAIN(ext.storage_8bit_features, 8BIT_STORAGE_FEATURES_KHR);
|
|
enabled_extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
|
|
}
|
|
}
|
|
|
|
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
|
{
|
|
ADD_CHAIN(ext.vk13_features, VULKAN_1_3_FEATURES);
|
|
}
|
|
else
|
|
{
|
|
if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
|
|
{
|
|
ADD_CHAIN(ext.subgroup_size_control_features, SUBGROUP_SIZE_CONTROL_FEATURES_EXT);
|
|
enabled_extensions.push_back(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
|
|
}
|
|
}
|
|
|
|
if (has_extension(VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.compute_shader_derivative_features, COMPUTE_SHADER_DERIVATIVES_FEATURES_NV);
|
|
}
|
|
|
|
if (has_extension(VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.performance_query_features, PERFORMANCE_QUERY_FEATURES_KHR);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.memory_priority_features, MEMORY_PRIORITY_FEATURES_EXT);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
|
|
ext.supports_memory_budget = true;
|
|
}
|
|
|
|
if (has_extension(VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME))
|
|
{
|
|
ext.supports_astc_decode_mode = true;
|
|
enabled_extensions.push_back(VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.astc_decode_features, ASTC_DECODE_FEATURES_EXT);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.pageable_device_local_memory_features, PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT);
|
|
}
|
|
|
|
if (has_extension(VK_NV_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_NV_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.device_generated_commands_features, DEVICE_GENERATED_COMMANDS_FEATURES_NV);
|
|
}
|
|
|
|
if (has_extension(VK_NV_DEVICE_GENERATED_COMMANDS_COMPUTE_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_NV_DEVICE_GENERATED_COMMANDS_COMPUTE_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.device_generated_commands_compute_features, DEVICE_GENERATED_COMMANDS_COMPUTE_FEATURES_NV);
|
|
}
|
|
|
|
if (has_extension(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.descriptor_pool_overallocation_features, DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_MESH_SHADER_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_MESH_SHADER_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.mesh_shader_features, MESH_SHADER_FEATURES_EXT);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.index_type_uint8_features, INDEX_TYPE_UINT8_FEATURES_EXT);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.rgba10x6_formats_features, RGBA10X6_FORMATS_FEATURES_EXT);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME))
|
|
{
|
|
ext.supports_external_memory_host = true;
|
|
enabled_extensions.push_back(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
|
|
}
|
|
|
|
if (has_extension(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.barycentric_features, FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR);
|
|
}
|
|
|
|
if (ext.supports_video_queue && has_extension(VK_KHR_VIDEO_MAINTENANCE_1_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_VIDEO_MAINTENANCE_1_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.video_maintenance1_features, VIDEO_MAINTENANCE_1_FEATURES_KHR);
|
|
}
|
|
|
|
if (has_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
ext.supports_push_descriptor = true;
|
|
}
|
|
|
|
if (has_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.image_compression_control_features, IMAGE_COMPRESSION_CONTROL_FEATURES_EXT);
|
|
}
|
|
|
|
if (has_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.image_compression_control_swapchain_features, IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT);
|
|
}
|
|
|
|
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
|
{
|
|
ext.supports_store_op_none = true;
|
|
}
|
|
else if (has_extension(VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME))
|
|
{
|
|
ext.supports_store_op_none = true;
|
|
enabled_extensions.push_back(VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME);
|
|
}
|
|
else if (has_extension(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME))
|
|
{
|
|
ext.supports_store_op_none = true;
|
|
enabled_extensions.push_back(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME);
|
|
}
|
|
|
|
if ((flags & CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT) != 0 && requires_swapchain)
|
|
{
|
|
if (has_extension(VK_KHR_PRESENT_ID_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_PRESENT_ID_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.present_id_features, PRESENT_ID_FEATURES_KHR);
|
|
}
|
|
|
|
if (has_extension(VK_KHR_PRESENT_WAIT_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_KHR_PRESENT_WAIT_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.present_wait_features, PRESENT_WAIT_FEATURES_KHR);
|
|
}
|
|
|
|
if (ext.supports_surface_maintenance1 && has_extension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME))
|
|
{
|
|
enabled_extensions.push_back(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);
|
|
ADD_CHAIN(ext.swapchain_maintenance1_features, SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT);
|
|
}
|
|
|
|
if (ext.supports_swapchain_colorspace && has_extension(VK_EXT_HDR_METADATA_EXTENSION_NAME))
|
|
{
|
|
ext.supports_hdr_metadata = true;
|
|
enabled_extensions.push_back(VK_EXT_HDR_METADATA_EXTENSION_NAME);
|
|
}
|
|
}
|
|
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
// Override any features in the profile in strict mode.
|
|
if (profile.profile && required_profile_strict)
|
|
{
|
|
vpGetProfileFeatures(profile.profile, &pdf2);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
vkGetPhysicalDeviceFeatures2(gpu, &pdf2);
|
|
}
|
|
|
|
// Promote fallback features to core structs.
|
|
if (ext.host_query_reset_features.hostQueryReset)
|
|
ext.vk12_features.hostQueryReset = VK_TRUE;
|
|
|
|
if (ext.storage_16bit_features.storageBuffer16BitAccess)
|
|
ext.vk11_features.storageBuffer16BitAccess = VK_TRUE;
|
|
if (ext.storage_16bit_features.storageInputOutput16)
|
|
ext.vk11_features.storageInputOutput16 = VK_TRUE;
|
|
if (ext.storage_16bit_features.storagePushConstant16)
|
|
ext.vk11_features.storagePushConstant16 = VK_TRUE;
|
|
if (ext.storage_16bit_features.uniformAndStorageBuffer16BitAccess)
|
|
ext.vk11_features.uniformAndStorageBuffer16BitAccess = VK_TRUE;
|
|
|
|
if (ext.storage_8bit_features.storageBuffer8BitAccess)
|
|
ext.vk12_features.storageBuffer8BitAccess = VK_TRUE;
|
|
if (ext.storage_8bit_features.storagePushConstant8)
|
|
ext.vk12_features.storagePushConstant8 = VK_TRUE;
|
|
if (ext.storage_8bit_features.uniformAndStorageBuffer8BitAccess)
|
|
ext.vk12_features.uniformAndStorageBuffer8BitAccess = VK_TRUE;
|
|
|
|
if (ext.float16_int8_features.shaderFloat16)
|
|
ext.vk12_features.shaderFloat16 = VK_TRUE;
|
|
if (ext.float16_int8_features.shaderInt8)
|
|
ext.vk12_features.shaderInt8 = VK_TRUE;
|
|
|
|
if (ext.subgroup_size_control_features.computeFullSubgroups)
|
|
ext.vk13_features.computeFullSubgroups = VK_TRUE;
|
|
if (ext.subgroup_size_control_features.subgroupSizeControl)
|
|
ext.vk13_features.subgroupSizeControl = VK_TRUE;
|
|
///
|
|
|
|
ext.vk11_features.multiviewGeometryShader = VK_FALSE;
|
|
ext.vk11_features.multiviewTessellationShader = VK_FALSE;
|
|
ext.vk11_features.protectedMemory = VK_FALSE;
|
|
ext.vk11_features.variablePointers = VK_FALSE;
|
|
ext.vk11_features.variablePointersStorageBuffer = VK_FALSE;
|
|
|
|
ext.vk12_features.bufferDeviceAddressCaptureReplay = VK_FALSE;
|
|
ext.vk12_features.bufferDeviceAddressMultiDevice = VK_FALSE;
|
|
ext.vk12_features.imagelessFramebuffer = VK_FALSE;
|
|
|
|
ext.vk13_features.descriptorBindingInlineUniformBlockUpdateAfterBind = VK_FALSE;
|
|
ext.vk13_features.inlineUniformBlock = VK_FALSE;
|
|
ext.vk13_features.privateData = VK_FALSE;
|
|
|
|
ext.mesh_shader_features.primitiveFragmentShadingRateMeshShader = VK_FALSE;
|
|
ext.mesh_shader_features.meshShaderQueries = VK_FALSE;
|
|
ext.mesh_shader_features.multiviewMeshShader = VK_FALSE;
|
|
|
|
ext.device_generated_commands_compute_features.deviceGeneratedComputeCaptureReplay = VK_FALSE;
|
|
// TODO
|
|
ext.device_generated_commands_compute_features.deviceGeneratedComputePipelines = VK_FALSE;
|
|
|
|
// Enable device features we might care about.
|
|
{
|
|
VkPhysicalDeviceFeatures enabled_features = *required_features;
|
|
if (pdf2.features.textureCompressionETC2)
|
|
enabled_features.textureCompressionETC2 = VK_TRUE;
|
|
if (pdf2.features.textureCompressionBC)
|
|
enabled_features.textureCompressionBC = VK_TRUE;
|
|
if (pdf2.features.textureCompressionASTC_LDR)
|
|
enabled_features.textureCompressionASTC_LDR = VK_TRUE;
|
|
if (pdf2.features.fullDrawIndexUint32)
|
|
enabled_features.fullDrawIndexUint32 = VK_TRUE;
|
|
if (pdf2.features.imageCubeArray)
|
|
enabled_features.imageCubeArray = VK_TRUE;
|
|
if (pdf2.features.fillModeNonSolid)
|
|
enabled_features.fillModeNonSolid = VK_TRUE;
|
|
if (pdf2.features.independentBlend)
|
|
enabled_features.independentBlend = VK_TRUE;
|
|
if (pdf2.features.sampleRateShading)
|
|
enabled_features.sampleRateShading = VK_TRUE;
|
|
if (pdf2.features.fragmentStoresAndAtomics)
|
|
enabled_features.fragmentStoresAndAtomics = VK_TRUE;
|
|
if (pdf2.features.shaderStorageImageExtendedFormats)
|
|
enabled_features.shaderStorageImageExtendedFormats = VK_TRUE;
|
|
if (pdf2.features.shaderStorageImageMultisample)
|
|
enabled_features.shaderStorageImageMultisample = VK_TRUE;
|
|
if (pdf2.features.largePoints)
|
|
enabled_features.largePoints = VK_TRUE;
|
|
if (pdf2.features.shaderInt16)
|
|
enabled_features.shaderInt16 = VK_TRUE;
|
|
if (pdf2.features.shaderInt64)
|
|
enabled_features.shaderInt64 = VK_TRUE;
|
|
if (pdf2.features.shaderStorageImageWriteWithoutFormat)
|
|
enabled_features.shaderStorageImageWriteWithoutFormat = VK_TRUE;
|
|
if (pdf2.features.shaderStorageImageReadWithoutFormat)
|
|
enabled_features.shaderStorageImageReadWithoutFormat = VK_TRUE;
|
|
if (pdf2.features.multiDrawIndirect)
|
|
enabled_features.multiDrawIndirect = VK_TRUE;
|
|
|
|
if (pdf2.features.shaderSampledImageArrayDynamicIndexing)
|
|
enabled_features.shaderSampledImageArrayDynamicIndexing = VK_TRUE;
|
|
if (pdf2.features.shaderUniformBufferArrayDynamicIndexing)
|
|
enabled_features.shaderUniformBufferArrayDynamicIndexing = VK_TRUE;
|
|
if (pdf2.features.shaderStorageBufferArrayDynamicIndexing)
|
|
enabled_features.shaderStorageBufferArrayDynamicIndexing = VK_TRUE;
|
|
if (pdf2.features.shaderStorageImageArrayDynamicIndexing)
|
|
enabled_features.shaderStorageImageArrayDynamicIndexing = VK_TRUE;
|
|
if (pdf2.features.shaderImageGatherExtended)
|
|
enabled_features.shaderImageGatherExtended = VK_TRUE;
|
|
|
|
if (pdf2.features.samplerAnisotropy)
|
|
enabled_features.samplerAnisotropy = VK_TRUE;
|
|
|
|
pdf2.features = enabled_features;
|
|
ext.enabled_features = enabled_features;
|
|
}
|
|
|
|
device_info.pNext = &pdf2;
|
|
|
|
// Only need GetPhysicalDeviceProperties2 for Vulkan 1.1-only code, so don't bother getting KHR variant.
|
|
VkPhysicalDeviceProperties2 props = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
|
|
// Fallback, query some important Vulkan 1.1 structs if we cannot use core 1.2 method.
|
|
VkPhysicalDeviceDriverProperties driver_properties = {};
|
|
VkPhysicalDeviceIDProperties id_properties = {};
|
|
VkPhysicalDeviceSubgroupProperties subgroup_properties = {};
|
|
VkPhysicalDeviceSubgroupSizeControlProperties size_control_props = {};
|
|
ppNext = &props.pNext;
|
|
|
|
if (ext.device_api_core_version >= VK_API_VERSION_1_2)
|
|
{
|
|
ADD_CHAIN(ext.vk11_props, VULKAN_1_1_PROPERTIES);
|
|
ADD_CHAIN(ext.vk12_props, VULKAN_1_2_PROPERTIES);
|
|
}
|
|
else
|
|
{
|
|
if (has_extension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME))
|
|
ADD_CHAIN(driver_properties, DRIVER_PROPERTIES);
|
|
ADD_CHAIN(id_properties, ID_PROPERTIES);
|
|
ADD_CHAIN(subgroup_properties, SUBGROUP_PROPERTIES);
|
|
}
|
|
|
|
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
|
ADD_CHAIN(ext.vk13_props, VULKAN_1_3_PROPERTIES);
|
|
else if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
|
|
ADD_CHAIN(size_control_props, SUBGROUP_SIZE_CONTROL_PROPERTIES);
|
|
|
|
if (ext.supports_external_memory_host)
|
|
ADD_CHAIN(ext.host_memory_properties, EXTERNAL_MEMORY_HOST_PROPERTIES_EXT);
|
|
|
|
if (has_extension(VK_NV_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME))
|
|
ADD_CHAIN(ext.device_generated_commands_properties, DEVICE_GENERATED_COMMANDS_PROPERTIES_NV);
|
|
|
|
if (ext.supports_conservative_rasterization)
|
|
ADD_CHAIN(ext.conservative_rasterization_properties, CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT);
|
|
|
|
if (has_extension(VK_EXT_MESH_SHADER_EXTENSION_NAME))
|
|
ADD_CHAIN(ext.mesh_shader_properties, MESH_SHADER_PROPERTIES_EXT);
|
|
|
|
vkGetPhysicalDeviceProperties2(gpu, &props);
|
|
|
|
if (ext.device_api_core_version < VK_API_VERSION_1_2)
|
|
{
|
|
ext.driver_id = driver_properties.driverID;
|
|
ext.supports_driver_properties = has_extension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME);
|
|
ext.vk12_props.driverID = ext.driver_id;
|
|
memcpy(ext.vk11_props.deviceUUID, id_properties.deviceUUID, sizeof(id_properties.deviceUUID));
|
|
memcpy(ext.vk11_props.driverUUID, id_properties.driverUUID, sizeof(id_properties.driverUUID));
|
|
memcpy(ext.vk11_props.deviceLUID, id_properties.deviceLUID, sizeof(id_properties.deviceLUID));
|
|
ext.vk11_props.deviceNodeMask = id_properties.deviceNodeMask;
|
|
ext.vk11_props.deviceLUIDValid = id_properties.deviceLUIDValid;
|
|
ext.vk11_props.subgroupQuadOperationsInAllStages = subgroup_properties.quadOperationsInAllStages;
|
|
ext.vk11_props.subgroupSupportedOperations = subgroup_properties.supportedOperations;
|
|
ext.vk11_props.subgroupSupportedStages = subgroup_properties.supportedStages;
|
|
ext.vk11_props.subgroupSize = subgroup_properties.subgroupSize;
|
|
}
|
|
else
|
|
{
|
|
ext.driver_id = ext.vk12_props.driverID;
|
|
ext.supports_driver_properties = true;
|
|
}
|
|
|
|
if (ext.device_api_core_version < VK_API_VERSION_1_3)
|
|
{
|
|
ext.vk13_props.minSubgroupSize = size_control_props.minSubgroupSize;
|
|
ext.vk13_props.maxSubgroupSize = size_control_props.maxSubgroupSize;
|
|
ext.vk13_props.requiredSubgroupSizeStages = size_control_props.requiredSubgroupSizeStages;
|
|
ext.vk13_props.maxComputeWorkgroupSubgroups = size_control_props.maxComputeWorkgroupSubgroups;
|
|
}
|
|
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
// Override any properties in the profile in strict mode.
|
|
if (profile.profile && required_profile_strict)
|
|
vpGetProfileProperties(profile.profile, &props);
|
|
#endif
|
|
|
|
device_info.enabledExtensionCount = enabled_extensions.size();
|
|
device_info.ppEnabledExtensionNames = enabled_extensions.empty() ? nullptr : enabled_extensions.data();
|
|
|
|
for (auto *enabled_extension : enabled_extensions)
|
|
LOGI("Enabling device extension: %s.\n", enabled_extension);
|
|
|
|
#ifdef GRANITE_VULKAN_PROFILES
|
|
if (!required_profile.empty())
|
|
{
|
|
if (create_device_from_profile(device_info, &device) != VK_SUCCESS)
|
|
return false;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (device_factory)
|
|
{
|
|
device = device_factory->create_device(gpu, &device_info);
|
|
if (device == VK_NULL_HANDLE)
|
|
return false;
|
|
}
|
|
else if (vkCreateDevice(gpu, &device_info, nullptr, &device) != VK_SUCCESS)
|
|
return false;
|
|
}
|
|
|
|
enabled_device_extensions = std::move(enabled_extensions);
|
|
ext.device_extensions = enabled_device_extensions.data();
|
|
ext.num_device_extensions = uint32_t(enabled_device_extensions.size());
|
|
ext.pdf2 = &pdf2;
|
|
|
|
#ifdef GRANITE_VULKAN_FOSSILIZE
|
|
feature_filter.init(ext.device_api_core_version,
|
|
enabled_device_extensions.data(),
|
|
device_info.enabledExtensionCount,
|
|
&pdf2, &props);
|
|
feature_filter.set_device_query_interface(this);
|
|
#endif
|
|
|
|
volkLoadDeviceTable(&device_table, device);
|
|
|
|
if (!device_table.vkCreateRenderPass2)
|
|
device_table.vkCreateRenderPass2 = device_table.vkCreateRenderPass2KHR;
|
|
if (!device_table.vkResetQueryPool)
|
|
device_table.vkResetQueryPool = device_table.vkResetQueryPoolEXT;
|
|
|
|
for (int i = 0; i < QUEUE_INDEX_COUNT; i++)
|
|
{
|
|
if (queue_info.family_indices[i] != VK_QUEUE_FAMILY_IGNORED)
|
|
{
|
|
device_table.vkGetDeviceQueue(device, queue_info.family_indices[i], queue_indices[i],
|
|
&queue_info.queues[i]);
|
|
|
|
queue_info.counts[i] = queue_offsets[queue_info.family_indices[i]];
|
|
|
|
#if defined(ANDROID) && defined(HAVE_SWAPPY)
|
|
SwappyVk_setQueueFamilyIndex(device, queue_info.queues[i], queue_info.family_indices[i]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
queue_info.queues[i] = VK_NULL_HANDLE;
|
|
}
|
|
}
|
|
|
|
#ifdef VULKAN_DEBUG
|
|
static const char *family_names[QUEUE_INDEX_COUNT] = { "Graphics", "Compute", "Transfer", "Video decode", "Video encode" };
|
|
for (int i = 0; i < QUEUE_INDEX_COUNT; i++)
|
|
if (queue_info.family_indices[i] != VK_QUEUE_FAMILY_IGNORED)
|
|
LOGI("%s queue: family %u, index %u.\n", family_names[i], queue_info.family_indices[i], queue_indices[i]);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef GRANITE_VULKAN_FOSSILIZE
|
|
bool Context::format_is_supported(VkFormat format, VkFormatFeatureFlags features)
|
|
{
|
|
if (gpu == VK_NULL_HANDLE)
|
|
return false;
|
|
|
|
VkFormatProperties props;
|
|
vkGetPhysicalDeviceFormatProperties(gpu, format, &props);
|
|
auto supported = props.bufferFeatures | props.linearTilingFeatures | props.optimalTilingFeatures;
|
|
return (supported & features) == features;
|
|
}
|
|
|
|
bool Context::descriptor_set_layout_is_supported(const VkDescriptorSetLayoutCreateInfo *set_layout)
|
|
{
|
|
if (device == VK_NULL_HANDLE)
|
|
return false;
|
|
|
|
VkDescriptorSetLayoutSupport support = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT };
|
|
vkGetDescriptorSetLayoutSupport(device, set_layout, &support);
|
|
return support.supported == VK_TRUE;
|
|
}
|
|
#endif
|
|
}
|