diff --git a/.idea/scopes/SkylineLibraries.xml b/.idea/scopes/SkylineLibraries.xml index 6a2f3579..c13db64e 100644 --- a/.idea/scopes/SkylineLibraries.xml +++ b/.idea/scopes/SkylineLibraries.xml @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 3b9a2e21..5580b11a 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -29,6 +29,7 @@ set(LZ4_BUILD_LEGACY_LZ4C OFF CACHE BOOL "Build lz4c progam with legacy argument add_subdirectory("libraries/lz4/build/cmake") 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_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 diff --git a/app/build.gradle b/app/build.gradle index ab177497..70a41b12 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,7 +15,7 @@ android { targetSdkVersion 30 versionCode 3 - versionName "0.3" + versionName "0.0.3" ndk { abiFilters "arm64-v8a" diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index 21733329..e290aeb0 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -1,22 +1,22 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include #include "gpu.h" namespace skyline::gpu { vk::raii::Instance GPU::CreateInstance(const DeviceState &state, const vk::raii::Context &context) { vk::ApplicationInfo applicationInfo{ .pApplicationName = "Skyline", - .applicationVersion = VK_MAKE_VERSION('S', 'K', 'Y'), // "SKY" magic as the application version - .pEngineName = "GPU", - .engineVersion = VK_MAKE_VERSION('G', 'P', 'U'), // "GPU" magic as engine version + .applicationVersion = state.jvm->GetVersionCode(), // Get the application version from JNI + .pEngineName = "FTX1", // "Fast Tegra X1" .apiVersion = VK_API_VERSION_1_1, }; #ifdef NDEBUG - std::array requiredLayers{}; + constexpr std::array requiredLayers{}; #else - std::array requiredLayers{ + constexpr std::array requiredLayers{ "VK_LAYER_KHRONOS_validation" }; #endif @@ -25,7 +25,7 @@ namespace skyline::gpu { if (state.logger->configLevel >= Logger::LogLevel::Debug) { std::string layers; 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); } @@ -40,9 +40,9 @@ namespace skyline::gpu { } #ifdef NDEBUG - std::array requiredInstanceExtensions{}; + constexpr std::array requiredInstanceExtensions{}; #else - std::array requiredInstanceExtensions{ + constexpr std::array requiredInstanceExtensions{ VK_EXT_DEBUG_REPORT_EXTENSION_NAME }; #endif @@ -51,7 +51,7 @@ namespace skyline::gpu { if (state.logger->configLevel >= Logger::LogLevel::Debug) { std::string extensions; 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); } @@ -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) { constexpr std::array severityLookup{ 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(flags))), util::Format("Vk{}:{}[0x{:X}]:I{}:L{}: {}", layerPrefix, vk::to_string(vk::DebugReportObjectTypeEXT(objectType)), object, messageCode, location, message)); 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 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) {} } diff --git a/app/src/main/cpp/skyline/gpu.h b/app/src/main/cpp/skyline/gpu.h index 55d00e48..b17a276b 100644 --- a/app/src/main/cpp/skyline/gpu.h +++ b/app/src/main/cpp/skyline/gpu.h @@ -11,18 +11,24 @@ namespace skyline::gpu { */ class GPU { 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::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 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: + 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; GPU(const DeviceState &state); diff --git a/app/src/main/cpp/skyline/jvm.cpp b/app/src/main/cpp/skyline/jvm.cpp index 892bfd33..6a148c67 100644 --- a/app/src/main/cpp/skyline/jvm.cpp +++ b/app/src/main/cpp/skyline/jvm.cpp @@ -46,7 +46,7 @@ namespace skyline { thread_local inline JniEnvironment env; - JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(environ->NewGlobalRef(instance)), instanceClass(reinterpret_cast(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(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); } @@ -90,4 +90,8 @@ namespace skyline { void JvmManager::ClearVibrationDevice(jint index) { env->CallVoidMethod(instance, clearVibrationDeviceId, index); } + + u32 JvmManager::GetVersionCode() { + return env->CallIntMethod(instance, getVersionCodeId); + } } diff --git a/app/src/main/cpp/skyline/jvm.h b/app/src/main/cpp/skyline/jvm.h index 3de3b151..9406daee 100644 --- a/app/src/main/cpp/skyline/jvm.h +++ b/app/src/main/cpp/skyline/jvm.h @@ -95,9 +95,16 @@ namespace skyline { */ 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: jmethodID initializeControllersId; jmethodID vibrateDeviceId; jmethodID clearVibrationDeviceId; + jmethodID getVersionCodeId; }; } diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index 0be6ffa5..62efc493 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -454,4 +454,14 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo fun clearVibrationDevice(index : Int) { 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() + } }