mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-24 00:11:51 +01:00
Vulkan PhysicalDevice + Device Initialization + Report Accurate Version
Vulkan Device initialization is handled now, it supports required extensions but support for optional extensions/features/properties will come in later when we require those. In addition, we now correctly report the version of Skyline to Vulkan which can be accessed from debugging tools. There's also a minor change regarding the search pattern for `SkylineLibraries` which now only searches in headers of libraries and it also explicitly excludes the redundant `vulkan.hpp` from the `Vulkan-Headers` repository.
This commit is contained in:
parent
8b0d056c54
commit
0a38bebb2e
2
.idea/scopes/SkylineLibraries.xml
generated
2
.idea/scopes/SkylineLibraries.xml
generated
@ -1,3 +1,3 @@
|
|||||||
<component name="DependencyValidationManager">
|
<component name="DependencyValidationManager">
|
||||||
<scope name="SkylineLibraries" pattern="file[skyline.app]:libraries//*" />
|
<scope name="SkylineLibraries" pattern="(file[skyline.app]:libraries/fmt/include//*||file[skyline.app]:libraries/frozen/include//*||file[skyline.app]:libraries/lz4/lib//*||file[skyline.app]:libraries/oboe/include//*||file[skyline.app]:libraries/perfetto/include//*||file:libraries/pugixml/src/pugixml.hpp||file[skyline.app]:libraries/tzcode/include/*||file[skyline.app]:libraries/vkhpp/vulkan//*||file[skyline.app]:libraries/vkhpp/Vulkan-Headers/include//*)&&!file:libraries/vkhpp/Vulkan-Headers/include/vulkan/vulkan.hpp" />
|
||||||
</component>
|
</component>
|
@ -29,6 +29,7 @@ set(LZ4_BUILD_LEGACY_LZ4C OFF CACHE BOOL "Build lz4c progam with legacy argument
|
|||||||
add_subdirectory("libraries/lz4/build/cmake")
|
add_subdirectory("libraries/lz4/build/cmake")
|
||||||
include_directories("libraries/lz4/lib")
|
include_directories("libraries/lz4/lib")
|
||||||
|
|
||||||
|
add_compile_definitions(VK_USE_PLATFORM_ANDROID_KHR) # We want all the Android-specific structures to be defined
|
||||||
add_compile_definitions(VULKAN_HPP_NO_SPACESHIP_OPERATOR) # libcxx doesn't implement operator<=> for std::array which breaks this
|
add_compile_definitions(VULKAN_HPP_NO_SPACESHIP_OPERATOR) # libcxx doesn't implement operator<=> for std::array which breaks this
|
||||||
add_compile_definitions(VULKAN_HPP_NO_STRUCT_CONSTRUCTORS) # We want to use designated initializers in Vulkan-Hpp
|
add_compile_definitions(VULKAN_HPP_NO_STRUCT_CONSTRUCTORS) # We want to use designated initializers in Vulkan-Hpp
|
||||||
add_compile_definitions(VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1) # We use the dynamic loader rather than the static one to avoid an additional level of indirection
|
add_compile_definitions(VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1) # We use the dynamic loader rather than the static one to avoid an additional level of indirection
|
||||||
|
@ -15,7 +15,7 @@ android {
|
|||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
|
|
||||||
versionCode 3
|
versionCode 3
|
||||||
versionName "0.3"
|
versionName "0.0.3"
|
||||||
|
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters "arm64-v8a"
|
abiFilters "arm64-v8a"
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <jvm.h>
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
|
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
vk::raii::Instance GPU::CreateInstance(const DeviceState &state, const vk::raii::Context &context) {
|
vk::raii::Instance GPU::CreateInstance(const DeviceState &state, const vk::raii::Context &context) {
|
||||||
vk::ApplicationInfo applicationInfo{
|
vk::ApplicationInfo applicationInfo{
|
||||||
.pApplicationName = "Skyline",
|
.pApplicationName = "Skyline",
|
||||||
.applicationVersion = VK_MAKE_VERSION('S', 'K', 'Y'), // "SKY" magic as the application version
|
.applicationVersion = state.jvm->GetVersionCode(), // Get the application version from JNI
|
||||||
.pEngineName = "GPU",
|
.pEngineName = "FTX1", // "Fast Tegra X1"
|
||||||
.engineVersion = VK_MAKE_VERSION('G', 'P', 'U'), // "GPU" magic as engine version
|
|
||||||
.apiVersion = VK_API_VERSION_1_1,
|
.apiVersion = VK_API_VERSION_1_1,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
std::array<const char *, 0> requiredLayers{};
|
constexpr std::array<const char *, 0> requiredLayers{};
|
||||||
#else
|
#else
|
||||||
std::array<const char *, 1> requiredLayers{
|
constexpr std::array<const char *, 1> requiredLayers{
|
||||||
"VK_LAYER_KHRONOS_validation"
|
"VK_LAYER_KHRONOS_validation"
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -25,7 +25,7 @@ namespace skyline::gpu {
|
|||||||
if (state.logger->configLevel >= Logger::LogLevel::Debug) {
|
if (state.logger->configLevel >= Logger::LogLevel::Debug) {
|
||||||
std::string layers;
|
std::string layers;
|
||||||
for (const auto &instanceLayer : instanceLayers)
|
for (const auto &instanceLayer : instanceLayers)
|
||||||
layers += fmt::format("\n* {} (Sv{}.{}.{}, Iv{}.{}.{}) - {}", instanceLayer.layerName, VK_VERSION_MAJOR(instanceLayer.specVersion), VK_VERSION_MINOR(instanceLayer.specVersion), VK_VERSION_PATCH(instanceLayer.specVersion), VK_VERSION_MAJOR(instanceLayer.implementationVersion), VK_VERSION_MINOR(instanceLayer.implementationVersion), VK_VERSION_PATCH(instanceLayer.implementationVersion), instanceLayer.description);
|
layers += util::Format("\n* {} (Sv{}.{}.{}, Iv{}.{}.{}) - {}", instanceLayer.layerName, VK_VERSION_MAJOR(instanceLayer.specVersion), VK_VERSION_MINOR(instanceLayer.specVersion), VK_VERSION_PATCH(instanceLayer.specVersion), VK_VERSION_MAJOR(instanceLayer.implementationVersion), VK_VERSION_MINOR(instanceLayer.implementationVersion), VK_VERSION_PATCH(instanceLayer.implementationVersion), instanceLayer.description);
|
||||||
state.logger->Debug("Vulkan Layers:{}", layers);
|
state.logger->Debug("Vulkan Layers:{}", layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,9 +40,9 @@ namespace skyline::gpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
std::array<const char*, 0> requiredInstanceExtensions{};
|
constexpr std::array<const char*, 0> requiredInstanceExtensions{};
|
||||||
#else
|
#else
|
||||||
std::array<const char *, 1> requiredInstanceExtensions{
|
constexpr std::array<const char *, 1> requiredInstanceExtensions{
|
||||||
VK_EXT_DEBUG_REPORT_EXTENSION_NAME
|
VK_EXT_DEBUG_REPORT_EXTENSION_NAME
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -51,7 +51,7 @@ namespace skyline::gpu {
|
|||||||
if (state.logger->configLevel >= Logger::LogLevel::Debug) {
|
if (state.logger->configLevel >= Logger::LogLevel::Debug) {
|
||||||
std::string extensions;
|
std::string extensions;
|
||||||
for (const auto &instanceExtension : instanceExtensions)
|
for (const auto &instanceExtension : instanceExtensions)
|
||||||
extensions += fmt::format("\n* {} (v{}.{}.{})", instanceExtension.extensionName, VK_VERSION_MAJOR(instanceExtension.specVersion), VK_VERSION_MINOR(instanceExtension.specVersion), VK_VERSION_PATCH(instanceExtension.specVersion));
|
extensions += util::Format("\n* {} (v{}.{}.{})", instanceExtension.extensionName, VK_VERSION_MAJOR(instanceExtension.specVersion), VK_VERSION_MINOR(instanceExtension.specVersion), VK_VERSION_PATCH(instanceExtension.specVersion));
|
||||||
state.logger->Debug("Vulkan Instance Extensions:{}", extensions);
|
state.logger->Debug("Vulkan Instance Extensions:{}", extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,8 +82,6 @@ namespace skyline::gpu {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
GPU::GPU(const DeviceState &state) : presentation(state), instance(CreateInstance(state, context)), debugReportCallback(CreateDebugReportCallback(state, instance)) {}
|
|
||||||
|
|
||||||
VkBool32 GPU::DebugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char *layerPrefix, const char *message, Logger *logger) {
|
VkBool32 GPU::DebugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char *layerPrefix, const char *message, Logger *logger) {
|
||||||
constexpr std::array<Logger::LogLevel, 5> severityLookup{
|
constexpr std::array<Logger::LogLevel, 5> severityLookup{
|
||||||
Logger::LogLevel::Info, // VK_DEBUG_REPORT_INFORMATION_BIT_EXT
|
Logger::LogLevel::Info, // VK_DEBUG_REPORT_INFORMATION_BIT_EXT
|
||||||
@ -95,4 +93,63 @@ namespace skyline::gpu {
|
|||||||
logger->Write(severityLookup.at(std::countr_zero(static_cast<u32>(flags))), util::Format("Vk{}:{}[0x{:X}]:I{}:L{}: {}", layerPrefix, vk::to_string(vk::DebugReportObjectTypeEXT(objectType)), object, messageCode, location, message));
|
logger->Write(severityLookup.at(std::countr_zero(static_cast<u32>(flags))), util::Format("Vk{}:{}[0x{:X}]:I{}:L{}: {}", layerPrefix, vk::to_string(vk::DebugReportObjectTypeEXT(objectType)), object, messageCode, location, message));
|
||||||
return VK_FALSE;
|
return VK_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vk::raii::PhysicalDevice GPU::CreatePhysicalDevice(const DeviceState &state, const vk::raii::Instance &instance) {
|
||||||
|
return std::move(vk::raii::PhysicalDevices(instance).front()); // We just select the first device as we aren't expecting multiple GPUs
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::Device GPU::CreateDevice(const DeviceState &state, const vk::raii::PhysicalDevice &physicalDevice) {
|
||||||
|
auto properties{physicalDevice.getProperties2().properties}; // We should check for required properties here, if/when we have them
|
||||||
|
|
||||||
|
// auto features{physicalDevice.getFeatures2().features}; // Same as above
|
||||||
|
|
||||||
|
constexpr std::array<const char *, 1> requiredDeviceExtensions{
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
|
};
|
||||||
|
auto deviceExtensions{physicalDevice.enumerateDeviceExtensionProperties()};
|
||||||
|
for (const auto &requiredExtension : requiredDeviceExtensions) {
|
||||||
|
if (![&] {
|
||||||
|
for (const auto &deviceExtension : deviceExtensions)
|
||||||
|
if (std::string_view(deviceExtension.extensionName) == std::string_view(requiredExtension))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}())
|
||||||
|
throw exception("Cannot find Vulkan device extension: \"{}\"", requiredExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto queueFamilies{physicalDevice.getQueueFamilyProperties2()};
|
||||||
|
if (auto family{queueFamilies.front().queueFamilyProperties}; !(family.queueFlags & vk::QueueFlagBits::eGraphics && family.queueFlags & vk::QueueFlagBits::eCompute))
|
||||||
|
// We only check the first queue family as essentially all mobile GPUs only have a single queue family which supports all operations
|
||||||
|
throw exception("The first queue family doesn't support both eGraphics and eCompute workloads");
|
||||||
|
|
||||||
|
float queuePriority{1.f}; //!< As we only have one queue, it's priority is set to the maximum of 1.0
|
||||||
|
vk::DeviceQueueCreateInfo queue{
|
||||||
|
.queueFamilyIndex = 0,
|
||||||
|
.queueCount = 1,
|
||||||
|
.pQueuePriorities = &queuePriority,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (state.logger->configLevel >= Logger::LogLevel::Error) {
|
||||||
|
std::string extensionString;
|
||||||
|
for (const auto &extension : deviceExtensions)
|
||||||
|
extensionString += util::Format("\n* {} (v{}.{}.{})", extension.extensionName, VK_VERSION_MAJOR(extension.specVersion), VK_VERSION_MINOR(extension.specVersion), VK_VERSION_PATCH(extension.specVersion));
|
||||||
|
|
||||||
|
std::string queueString;
|
||||||
|
for (const auto &queueFamily : queueFamilies) {
|
||||||
|
auto &family{queueFamily.queueFamilyProperties};
|
||||||
|
queueString += util::Format("\n* {}x{}{}{}{}{}: TSB{}, MIG({},{},{})", family.queueCount, family.queueFlags & vk::QueueFlagBits::eGraphics ? 'G' : '-', family.queueFlags & vk::QueueFlagBits::eCompute ? 'C' : '-', family.queueFlags & vk::QueueFlagBits::eTransfer ? 'T' : '-', family.queueFlags & vk::QueueFlagBits::eSparseBinding ? 'S' : '-', family.queueFlags & vk::QueueFlagBits::eProtected ? 'P' : '-', family.timestampValidBits, family.minImageTransferGranularity.width, family.minImageTransferGranularity.height, family.minImageTransferGranularity.depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.logger->Error("Vulkan Device:\nName: {}\nType: {}\nVulkan Version: {}.{}.{}\nDriver Version: {}.{}.{}\nQueues:{}\nExtensions:{}", properties.deviceName, vk::to_string(properties.deviceType), VK_VERSION_MAJOR(properties.apiVersion), VK_VERSION_MINOR(properties.apiVersion), VK_VERSION_PATCH(properties.apiVersion), VK_VERSION_MAJOR(properties.driverVersion), VK_VERSION_MINOR(properties.driverVersion), VK_VERSION_PATCH(properties.driverVersion), queueString, extensionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vk::raii::Device(physicalDevice, vk::DeviceCreateInfo{
|
||||||
|
.queueCreateInfoCount = 1,
|
||||||
|
.pQueueCreateInfos = &queue,
|
||||||
|
.enabledExtensionCount = requiredDeviceExtensions.size(),
|
||||||
|
.ppEnabledExtensionNames = requiredDeviceExtensions.data(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU::GPU(const DeviceState &state) : vkInstance(CreateInstance(state, vkContext)), vkDebugReportCallback(CreateDebugReportCallback(state, vkInstance)), vkPhysicalDevice(CreatePhysicalDevice(state, vkInstance)), vkDevice(CreateDevice(state, vkPhysicalDevice)), vkQueue(vkDevice, 0, 0), presentation(state) {}
|
||||||
}
|
}
|
||||||
|
@ -11,18 +11,24 @@ namespace skyline::gpu {
|
|||||||
*/
|
*/
|
||||||
class GPU {
|
class GPU {
|
||||||
private:
|
private:
|
||||||
vk::raii::Context context;
|
|
||||||
vk::raii::Instance instance;
|
|
||||||
vk::raii::DebugReportCallbackEXT debugReportCallback;
|
|
||||||
vk::Device device;
|
|
||||||
|
|
||||||
static vk::raii::Instance CreateInstance(const DeviceState &state, const vk::raii::Context &context);
|
static vk::raii::Instance CreateInstance(const DeviceState &state, const vk::raii::Context &context);
|
||||||
|
|
||||||
static vk::raii::DebugReportCallbackEXT CreateDebugReportCallback(const DeviceState &state, const vk::raii::Instance &instance);
|
static vk::raii::DebugReportCallbackEXT CreateDebugReportCallback(const DeviceState &state, const vk::raii::Instance &instance);
|
||||||
|
|
||||||
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char *layerPrefix, const char *message, Logger *logger);
|
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char *layerPrefix, const char *message, Logger *logger);
|
||||||
|
|
||||||
|
static vk::raii::PhysicalDevice CreatePhysicalDevice(const DeviceState &state, const vk::raii::Instance &instance);
|
||||||
|
|
||||||
|
static vk::raii::Device CreateDevice(const DeviceState &state, const vk::raii::PhysicalDevice &physicalDevice);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
vk::raii::Context vkContext; //!< An overarching context for Vulkan with
|
||||||
|
vk::raii::Instance vkInstance; //!< An instance of Vulkan with all application context
|
||||||
|
vk::raii::DebugReportCallbackEXT vkDebugReportCallback; //!< An RAII Vulkan debug report manager which calls into DebugCallback
|
||||||
|
vk::raii::PhysicalDevice vkPhysicalDevice; //!< The underlying physical Vulkan device from which we derieve our logical device
|
||||||
|
vk::raii::Device vkDevice; //!< The logical Vulkan device which we want to render using
|
||||||
|
vk::raii::Queue vkQueue; //!< A Vulkan Queue supporting graphics and compute operations
|
||||||
|
|
||||||
PresentationEngine presentation;
|
PresentationEngine presentation;
|
||||||
|
|
||||||
GPU(const DeviceState &state);
|
GPU(const DeviceState &state);
|
||||||
|
@ -46,7 +46,7 @@ namespace skyline {
|
|||||||
|
|
||||||
thread_local inline JniEnvironment env;
|
thread_local inline JniEnvironment env;
|
||||||
|
|
||||||
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(environ->NewGlobalRef(instance)), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))), initializeControllersId(environ->GetMethodID(instanceClass, "initializeControllers", "()V")), vibrateDeviceId(environ->GetMethodID(instanceClass, "vibrateDevice", "(I[J[I)V")), clearVibrationDeviceId(environ->GetMethodID(instanceClass, "clearVibrationDevice", "(I)V")) {
|
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(environ->NewGlobalRef(instance)), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))), initializeControllersId(environ->GetMethodID(instanceClass, "initializeControllers", "()V")), vibrateDeviceId(environ->GetMethodID(instanceClass, "vibrateDevice", "(I[J[I)V")), clearVibrationDeviceId(environ->GetMethodID(instanceClass, "clearVibrationDevice", "(I)V")), getVersionCodeId(environ->GetMethodID(instanceClass, "getVersionCode", "()I")) {
|
||||||
env.Initialize(environ);
|
env.Initialize(environ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,4 +90,8 @@ namespace skyline {
|
|||||||
void JvmManager::ClearVibrationDevice(jint index) {
|
void JvmManager::ClearVibrationDevice(jint index) {
|
||||||
env->CallVoidMethod(instance, clearVibrationDeviceId, index);
|
env->CallVoidMethod(instance, clearVibrationDeviceId, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 JvmManager::GetVersionCode() {
|
||||||
|
return env->CallIntMethod(instance, getVersionCodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,9 +95,16 @@ namespace skyline {
|
|||||||
*/
|
*/
|
||||||
void ClearVibrationDevice(jint index);
|
void ClearVibrationDevice(jint index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A call to EmulationActivity.getVersionCode in Kotlin
|
||||||
|
* @return A version code in Vulkan's format with 14-bit patch + 10-bit major and minor components
|
||||||
|
*/
|
||||||
|
u32 GetVersionCode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
jmethodID initializeControllersId;
|
jmethodID initializeControllersId;
|
||||||
jmethodID vibrateDeviceId;
|
jmethodID vibrateDeviceId;
|
||||||
jmethodID clearVibrationDeviceId;
|
jmethodID clearVibrationDeviceId;
|
||||||
|
jmethodID getVersionCodeId;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -454,4 +454,14 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
|||||||
fun clearVibrationDevice(index : Int) {
|
fun clearVibrationDevice(index : Int) {
|
||||||
vibrators[index]?.cancel()
|
vibrators[index]?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A version code in Vulkan's format with 14-bit patch + 10-bit major and minor components
|
||||||
|
*/
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
@Suppress("unused")
|
||||||
|
fun getVersionCode() : Int {
|
||||||
|
val (major, minor, patch) = BuildConfig.VERSION_NAME.split('.').map { it.toUInt() }
|
||||||
|
return ((major shl 22) or (minor shl 12) or (patch)).toInt()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user