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.
This commit is contained in:
PixelyIon 2021-03-28 02:47:35 +05:30 committed by ◱ Mark
parent 5be7860cf7
commit 8ceed74371
9 changed files with 161 additions and 11 deletions

5
.idea/vcs.xml generated
View File

@ -11,5 +11,10 @@
<mapping directory="$PROJECT_DIR$/app/libraries/tzcode" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vk-headers" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vkhpp" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vkhpp/Vulkan-Headers" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vkhpp/glfw" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vkhpp/glm" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vkhpp/glslang" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vkhpp/tinyxml2" vcs="Git" />
</component>
</project>

View File

@ -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

View File

@ -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'])

@ -1 +1 @@
Subproject commit de5ced3d4b76dd24bc43628127e26a9c7eb098d3
Subproject commit f638311337d6b0eaf63e84c6e9afe1227539e0d3

View File

@ -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<skyline::kernel::OS> OsWeak;
std::weak_ptr<skyline::gpu::GPU> GpuWeak;
std::weak_ptr<skyline::input::Input> InputWeak;
@ -85,11 +85,11 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
os->Execute(romFd, static_cast<skyline::loader::RomFormat>(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();

View File

@ -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<const char *, 0> requiredLayers{};
#else
std::array<const char *, 1> 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<const char*, 0> requiredInstanceExtensions{};
#else
std::array<const char *, 1> 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<PFN_vkDebugReportCallbackEXT>(&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<Logger::LogLevel, 5> 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<u32>(flags))), util::Format("Vk{}:{}[0x{:X}]:I{}:L{}: {}", layerPrefix, vk::to_string(vk::DebugReportObjectTypeEXT(objectType)), object, messageCode, location, message));
return VK_FALSE;
}
}

View File

@ -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);
};
}

View File

@ -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<kernel::type::KEvent>(state, true)), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)), presentationTrack(static_cast<u64>(trace::TrackIds::Presentation), perfetto::ProcessTrack::Current()) {

View File

@ -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