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()
+ }
}