From 8b0d056c547f45b6e3cae2bd25c2a7f05c148ab9 Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Sun, 28 Mar 2021 02:47:35 +0530 Subject: [PATCH] Vulkan Instance + Validation Layer + Debug Report Initialization The GPU class has been extended in this for Vulkan initialization, this is done to the point of initializing the instance alongside loading in `VK_LAYER_KHRONOS_validation` which is also now packed into all Debug APKs for Skyline. In addition, `VK_EXT_debug_report` is also initialized and it's output is piped directly into the Logger. A minor change regarding the type of the `Fps` and `Frametime` globals was changed to `skyline::i32`s which is a more suitable type due to those having a smaller chance of overflowing while being signed as Java doesn't have unsigned integral types. --- .idea/vcs.xml | 5 + app/CMakeLists.txt | 3 +- app/build.gradle | 33 +++++++ app/libraries/tzcode | 2 +- app/src/main/cpp/emu_jni.cpp | 10 +- app/src/main/cpp/skyline/gpu.cpp | 98 +++++++++++++++++++ app/src/main/cpp/skyline/gpu.h | 14 ++- .../cpp/skyline/gpu/presentation_engine.cpp | 4 +- app/src/main/cpp/skyline/gpu/texture.h | 3 +- 9 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 app/src/main/cpp/skyline/gpu.cpp diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 69e1f66e..9cb13448 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -11,5 +11,10 @@ + + + + + \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 753ccc56..3b9a2e21 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -31,7 +31,7 @@ include_directories("libraries/lz4/lib") 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 +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 include_directories("libraries/vkhpp") include_directories("libraries/vkhpp/Vulkan-Headers/include") # We use base Vulkan headers from this to ensure version parity with Vulkan-Hpp @@ -72,6 +72,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/audio/track.cpp ${source_DIR}/skyline/audio/resampler.cpp ${source_DIR}/skyline/audio/adpcm_decoder.cpp + ${source_DIR}/skyline/gpu.cpp ${source_DIR}/skyline/gpu/presentation_engine.cpp ${source_DIR}/skyline/gpu/texture.cpp ${source_DIR}/skyline/soc/gmmu.cpp diff --git a/app/build.gradle b/app/build.gradle index 991e853f..ab177497 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -71,11 +71,44 @@ android { } } + /* Vulkan Validation Layers */ + sourceSets { + debug { + main { + jniLibs { + srcDir "$buildDir/generated/vulkan_layers" + } + } + } + } + + /* Android Assets */ aaptOptions { ignoreAssetsPattern "*.md" } } +/** + * We just want VK_LAYER_KHRONOS_validation in the APK while NDK contains several other legacy layers + * This task copies shared objects associated with that layer into a folder from where JNI can use it + */ +task setupValidationLayer(type: Copy) { + doFirst { + def folder = new File("$buildDir/generated/vulkan_layers") + if (!folder.exists()) { + folder.mkdirs() // We need to recursively create all directories as the copy requires the output directory to exist + } + } + from("${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs") { + include "*/libVkLayer_khronos_validation.so" + } + into "$buildDir/generated/vulkan_layers" +} + +afterEvaluate { + preDebugBuild.dependsOn setupValidationLayer +} + dependencies { /* Filetrees */ implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) diff --git a/app/libraries/tzcode b/app/libraries/tzcode index de5ced3d..f6383113 160000 --- a/app/libraries/tzcode +++ b/app/libraries/tzcode @@ -1 +1 @@ -Subproject commit de5ced3d4b76dd24bc43628127e26a9c7eb098d3 +Subproject commit f638311337d6b0eaf63e84c6e9afe1227539e0d3 diff --git a/app/src/main/cpp/emu_jni.cpp b/app/src/main/cpp/emu_jni.cpp index 898e804b..64832751 100644 --- a/app/src/main/cpp/emu_jni.cpp +++ b/app/src/main/cpp/emu_jni.cpp @@ -19,8 +19,8 @@ #include "skyline/input.h" #include "skyline/kernel/types/KProcess.h" -skyline::u16 Fps; -skyline::u32 FrameTime; +skyline::i32 Fps; +skyline::i32 FrameTime; std::weak_ptr OsWeak; std::weak_ptr GpuWeak; std::weak_ptr InputWeak; @@ -85,11 +85,11 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication( os->Execute(romFd, static_cast(romType)); } catch (std::exception &e) { - logger->Error("An exception has occurred: {}", e.what()); + logger->ErrorNoPrefix("An uncaught exception has occurred: {}", e.what()); } catch (const skyline::signal::SignalException &e) { - logger->Error("An exception has occurred: {}", e.what()); + logger->ErrorNoPrefix("An uncaught exception has occurred: {}", e.what()); } catch (...) { - logger->Error("An unknown exception has occurred"); + logger->ErrorNoPrefix("An unknown uncaught exception has occurred"); } perfetto::TrackEvent::Flush(); diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp new file mode 100644 index 00000000..21733329 --- /dev/null +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#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 + .apiVersion = VK_API_VERSION_1_1, + }; + + #ifdef NDEBUG + std::array requiredLayers{}; + #else + std::array requiredLayers{ + "VK_LAYER_KHRONOS_validation" + }; + #endif + + auto instanceLayers{context.enumerateInstanceLayerProperties()}; + 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); + state.logger->Debug("Vulkan Layers:{}", layers); + } + + for (const auto &requiredLayer : requiredLayers) { + if (![&] { + for (const auto &instanceLayer : instanceLayers) + if (std::string_view(instanceLayer.layerName) == std::string_view(requiredLayer)) + return true; + return false; + }()) + throw exception("Cannot find Vulkan layer: \"{}\"", requiredLayer); + } + + #ifdef NDEBUG + std::array requiredInstanceExtensions{}; + #else + std::array requiredInstanceExtensions{ + VK_EXT_DEBUG_REPORT_EXTENSION_NAME + }; + #endif + + auto instanceExtensions{context.enumerateInstanceExtensionProperties()}; + 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)); + state.logger->Debug("Vulkan Instance Extensions:{}", extensions); + } + + for (const auto &requiredExtension : requiredInstanceExtensions) { + if (![&] { + for (const auto &instanceExtension : instanceExtensions) + if (std::string_view(instanceExtension.extensionName) == std::string_view(requiredExtension)) + return true; + return false; + }()) + throw exception("Cannot find Vulkan instance extension: \"{}\"", requiredExtension); + } + + return vk::raii::Instance(context, vk::InstanceCreateInfo{ + .pApplicationInfo = &applicationInfo, + .enabledLayerCount = requiredLayers.size(), + .ppEnabledLayerNames = requiredLayers.data(), + .enabledExtensionCount = requiredInstanceExtensions.size(), + .ppEnabledExtensionNames = requiredInstanceExtensions.data(), + }); + } + + vk::raii::DebugReportCallbackEXT GPU::CreateDebugReportCallback(const DeviceState &state, const vk::raii::Instance &instance) { + return vk::raii::DebugReportCallbackEXT(instance, vk::DebugReportCallbackCreateInfoEXT{ + .flags = vk::DebugReportFlagBitsEXT::eError | vk::DebugReportFlagBitsEXT::eWarning | vk::DebugReportFlagBitsEXT::ePerformanceWarning | vk::DebugReportFlagBitsEXT::eInformation | vk::DebugReportFlagBitsEXT::eDebug, + .pfnCallback = reinterpret_cast(&DebugCallback), + .pUserData = state.logger.get(), + }); + } + + 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 + Logger::LogLevel::Warn, // VK_DEBUG_REPORT_WARNING_BIT_EXT + Logger::LogLevel::Warn, // VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT + Logger::LogLevel::Error, // VK_DEBUG_REPORT_ERROR_BIT_EXT + Logger::LogLevel::Debug, // VK_DEBUG_REPORT_DEBUG_BIT_EXT + }; + 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; + } +} diff --git a/app/src/main/cpp/skyline/gpu.h b/app/src/main/cpp/skyline/gpu.h index 030ea9c8..55d00e48 100644 --- a/app/src/main/cpp/skyline/gpu.h +++ b/app/src/main/cpp/skyline/gpu.h @@ -10,9 +10,21 @@ namespace skyline::gpu { * @brief An interface to host GPU structures, anything concerning host GPU/Presentation APIs is encapsulated by this */ 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); + public: PresentationEngine presentation; - GPU(const DeviceState &state) : presentation(state) {} + GPU(const DeviceState &state); }; } diff --git a/app/src/main/cpp/skyline/gpu/presentation_engine.cpp b/app/src/main/cpp/skyline/gpu/presentation_engine.cpp index ac3b9164..d5ab20da 100644 --- a/app/src/main/cpp/skyline/gpu/presentation_engine.cpp +++ b/app/src/main/cpp/skyline/gpu/presentation_engine.cpp @@ -5,8 +5,8 @@ #include "jvm.h" #include "presentation_engine.h" -extern skyline::u16 Fps; -extern skyline::u32 FrameTime; +extern skyline::i32 Fps; +extern skyline::i32 FrameTime; namespace skyline::gpu { PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared(state, true)), bufferEvent(std::make_shared(state, true)), presentationTrack(static_cast(trace::TrackIds::Presentation), perfetto::ProcessTrack::Current()) { diff --git a/app/src/main/cpp/skyline/gpu/texture.h b/app/src/main/cpp/skyline/gpu/texture.h index a9bfca3d..5bc1dda3 100644 --- a/app/src/main/cpp/skyline/gpu/texture.h +++ b/app/src/main/cpp/skyline/gpu/texture.h @@ -70,7 +70,8 @@ namespace skyline { }; /** - * @brief The linearity of a texture, refer to Chapter 20.1 of the Tegra X1 TRM for information + * @brief The layout of a texture in GPU memory + * @note Refer to Chapter 20.1 of the Tegra X1 TRM for information */ enum class TileMode { Linear, //!< This is a purely linear texture