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