Address CR Comments + Increase minSdkVersion to 29 (Android 10)

This commit is contained in:
PixelyIon 2021-07-10 19:08:18 +05:30 committed by ◱ Mark
parent 19be9f4b66
commit ec1da1b3f5
17 changed files with 165 additions and 176 deletions

141
.gitignore vendored
View File

@ -1,98 +1,91 @@
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm # Source: https://github.com/github/gitignore/blob/master/Android.gitignore
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff # Built application files
.idea/**/workspace.xml *.apk
.idea/**/tasks.xml *.aar
.idea/**/usage.statistics.xml *.ap_
.idea/**/dictionaries *.aab
.idea/**/shelf
# Files for the ART/Dalvik VM # Files for the ART/Dalvik VM
*.dex *.dex
# Java class files # Java class files
*.class *.class
# Generated files # Generated files
.idea/**/contentModel.xml bin/
gen/
# Sensitive or high-churn files out/
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
.idea/modules.xml
.idea/*.iml
.idea/modules
*.iml
*.ipr
# CMake
cmake-build-*/
# Gradle files # Gradle files
.gradle/ .gradle/
build/ build/
# Android Studio # Local configuration file (sdk path, etc)
.idea/caches
.idea/assetWizardSettings.xml
.idea/runConfigurations.xml
.idea/deploymentTargetDropDown.xml
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# Local configuration file (SDK path, etc)
local.properties local.properties
# External native build folder generated in Android Studio 2.2 and later # Proguard folder generated by Eclipse
.externalNativeBuild/ proguard/
.cxx/
# IntelliJ # Log Files
out/ *.log
# mpeltonen/sbt-idea plugin # Android Studio Navigation editor temp files
.idea_modules/ .navigation/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# Android Studio captures folder # Android Studio captures folder
captures/ captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
.idea/jarRepositories.xml
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
.idea/navEditor.xml
# Keystore files
*.jks
*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
lint/reports/
# Android Profiling
*.hprof
# VSCode # VSCode
.vscode/ .vscode/

View File

@ -11,7 +11,7 @@ android {
defaultConfig { defaultConfig {
applicationId "skyline.emu" applicationId "skyline.emu"
minSdkVersion 26 minSdkVersion 29
targetSdkVersion 30 targetSdkVersion 30
versionCode 3 versionCode 3

View File

@ -30,16 +30,13 @@ namespace skyline::gpu {
} }
for (const auto &requiredLayer : requiredLayers) { for (const auto &requiredLayer : requiredLayers) {
if (![&] { if (!std::any_of(instanceLayers.begin(), instanceLayers.end(), [&](const vk::LayerProperties &instanceLayer) {
for (const auto &instanceLayer : instanceLayers) return std::string_view(instanceLayer.layerName) == std::string_view(requiredLayer);
if (std::string_view(instanceLayer.layerName) == std::string_view(requiredLayer)) }))
return true;
return false;
}())
throw exception("Cannot find Vulkan layer: \"{}\"", requiredLayer); throw exception("Cannot find Vulkan layer: \"{}\"", requiredLayer);
} }
constexpr std::array<const char*, 3> requiredInstanceExtensions{ constexpr std::array<const char *, 3> requiredInstanceExtensions{
VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
@ -54,12 +51,9 @@ namespace skyline::gpu {
} }
for (const auto &requiredExtension : requiredInstanceExtensions) { for (const auto &requiredExtension : requiredInstanceExtensions) {
if (![&] { if (!std::any_of(instanceExtensions.begin(), instanceExtensions.end(), [&](const vk::ExtensionProperties &instanceExtension) {
for (const auto &instanceExtension : instanceExtensions) return std::string_view(instanceExtension.extensionName) == std::string_view(requiredExtension);
if (std::string_view(instanceExtension.extensionName) == std::string_view(requiredExtension)) }))
return true;
return false;
}())
throw exception("Cannot find Vulkan instance extension: \"{}\"", requiredExtension); throw exception("Cannot find Vulkan instance extension: \"{}\"", requiredExtension);
} }
@ -106,17 +100,14 @@ namespace skyline::gpu {
}; };
auto deviceExtensions{physicalDevice.enumerateDeviceExtensionProperties()}; auto deviceExtensions{physicalDevice.enumerateDeviceExtensionProperties()};
for (const auto &requiredExtension : requiredDeviceExtensions) { for (const auto &requiredExtension : requiredDeviceExtensions) {
if (![&] { if (!std::any_of(deviceExtensions.begin(), deviceExtensions.end(), [&](const vk::ExtensionProperties &deviceExtension) {
for (const auto &deviceExtension : deviceExtensions) return std::string_view(deviceExtension.extensionName) == std::string_view(requiredExtension);
if (std::string_view(deviceExtension.extensionName) == std::string_view(requiredExtension)) }))
return true;
return false;
}())
throw exception("Cannot find Vulkan device extension: \"{}\"", requiredExtension); throw exception("Cannot find Vulkan device extension: \"{}\"", requiredExtension);
} }
auto queueFamilies{physicalDevice.getQueueFamilyProperties()}; auto queueFamilies{physicalDevice.getQueueFamilyProperties()};
float queuePriority{1.f}; //!< The priority of the only queue we use, it's set to the maximum of 1.0 float queuePriority{1.0f}; //!< The priority of the only queue we use, it's set to the maximum of 1.0
vk::DeviceQueueCreateInfo queue{[&] { vk::DeviceQueueCreateInfo queue{[&] {
typeof(vk::DeviceQueueCreateInfo::queueFamilyIndex) index{}; typeof(vk::DeviceQueueCreateInfo::queueFamilyIndex) index{};
for (const auto &queueFamily : queueFamilies) { for (const auto &queueFamily : queueFamilies) {
@ -133,17 +124,17 @@ namespace skyline::gpu {
throw exception("Cannot find a queue family with both eGraphics and eCompute bits set"); throw exception("Cannot find a queue family with both eGraphics and eCompute bits set");
}()}; }()};
if (state.logger->configLevel >= Logger::LogLevel::Debug) { if (state.logger->configLevel >= Logger::LogLevel::Info) {
std::string extensionString; std::string extensionString;
for (const auto &extension : deviceExtensions) 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)); 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; std::string queueString;
typeof(vk::DeviceQueueCreateInfo::queueFamilyIndex) familyIndex{}; u32 familyIndex{};
for (const auto &queueFamily : queueFamilies) for (const auto &queueFamily : queueFamilies)
queueString += util::Format("\n* {}x{}{}{}{}{}: TSB{} MIG({}x{}x{}){}", queueFamily.queueCount, queueFamily.queueFlags & vk::QueueFlagBits::eGraphics ? 'G' : '-', queueFamily.queueFlags & vk::QueueFlagBits::eCompute ? 'C' : '-', queueFamily.queueFlags & vk::QueueFlagBits::eTransfer ? 'T' : '-', queueFamily.queueFlags & vk::QueueFlagBits::eSparseBinding ? 'S' : '-', queueFamily.queueFlags & vk::QueueFlagBits::eProtected ? 'P' : '-', queueFamily.timestampValidBits, queueFamily.minImageTransferGranularity.width, queueFamily.minImageTransferGranularity.height, queueFamily.minImageTransferGranularity.depth, familyIndex++ == vkQueueFamilyIndex ? " <--" : ""); queueString += util::Format("\n* {}x{}{}{}{}{}: TSB{} MIG({}x{}x{}){}", queueFamily.queueCount, queueFamily.queueFlags & vk::QueueFlagBits::eGraphics ? 'G' : '-', queueFamily.queueFlags & vk::QueueFlagBits::eCompute ? 'C' : '-', queueFamily.queueFlags & vk::QueueFlagBits::eTransfer ? 'T' : '-', queueFamily.queueFlags & vk::QueueFlagBits::eSparseBinding ? 'S' : '-', queueFamily.queueFlags & vk::QueueFlagBits::eProtected ? 'P' : '-', queueFamily.timestampValidBits, queueFamily.minImageTransferGranularity.width, queueFamily.minImageTransferGranularity.height, queueFamily.minImageTransferGranularity.depth, familyIndex++ == vkQueueFamilyIndex ? " <--" : "");
state.logger->Debug("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); state.logger->Info("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{ return vk::raii::Device(physicalDevice, vk::DeviceCreateInfo{

View File

@ -30,7 +30,7 @@ namespace skyline::gpu {
vk::raii::Instance vkInstance; vk::raii::Instance vkInstance;
vk::raii::DebugReportCallbackEXT vkDebugReportCallback; //!< An RAII Vulkan debug report manager which calls into 'GPU::DebugCallback' vk::raii::DebugReportCallbackEXT vkDebugReportCallback; //!< An RAII Vulkan debug report manager which calls into 'GPU::DebugCallback'
vk::raii::PhysicalDevice vkPhysicalDevice; vk::raii::PhysicalDevice vkPhysicalDevice;
typeof(vk::DeviceQueueCreateInfo::queueCount) vkQueueFamilyIndex{}; u32 vkQueueFamilyIndex{};
vk::raii::Device vkDevice; vk::raii::Device vkDevice;
std::mutex queueMutex; //!< Synchronizes access to the queue as it is externally synchronized std::mutex queueMutex; //!< Synchronizes access to the queue as it is externally synchronized
vk::raii::Queue vkQueue; //!< A Vulkan Queue supporting graphics and compute operations vk::raii::Queue vkQueue; //!< A Vulkan Queue supporting graphics and compute operations

View File

@ -5,7 +5,7 @@
#include "command_scheduler.h" #include "command_scheduler.h"
namespace skyline::gpu { namespace skyline::gpu {
CommandScheduler::CommandBufferSlot::CommandBufferSlot(vk::raii::Device &device, vk::CommandBuffer commandBuffer, vk::raii::CommandPool &pool) : active(true), device(device), commandBuffer(device, commandBuffer, pool), fence(device, vk::FenceCreateInfo{}), cycle(std::make_shared<FenceCycle>(device, *fence)) {} CommandScheduler::CommandBufferSlot::CommandBufferSlot(vk::raii::Device &device, vk::CommandBuffer commandBuffer, vk::raii::CommandPool &pool) : device(device), commandBuffer(device, commandBuffer, pool), fence(device, vk::FenceCreateInfo{}), cycle(std::make_shared<FenceCycle>(device, *fence)) {}
bool CommandScheduler::CommandBufferSlot::AllocateIfFree(CommandScheduler::CommandBufferSlot &slot) { bool CommandScheduler::CommandBufferSlot::AllocateIfFree(CommandScheduler::CommandBufferSlot &slot) {
if (slot.active.test_and_set(std::memory_order_acq_rel)) { if (slot.active.test_and_set(std::memory_order_acq_rel)) {
@ -44,7 +44,7 @@ namespace skyline::gpu {
} }
void CommandScheduler::SubmitCommandBuffer(const vk::raii::CommandBuffer &commandBuffer, vk::Fence fence) { void CommandScheduler::SubmitCommandBuffer(const vk::raii::CommandBuffer &commandBuffer, vk::Fence fence) {
std::lock_guard lock(gpu.queueMutex); std::scoped_lock lock(gpu.queueMutex);
gpu.vkQueue.submit(vk::SubmitInfo{ gpu.vkQueue.submit(vk::SubmitInfo{
.commandBufferCount = 1, .commandBufferCount = 1,
.pCommandBuffers = &*commandBuffer, .pCommandBuffers = &*commandBuffer,

View File

@ -15,7 +15,7 @@ namespace skyline::gpu {
* @brief A wrapper around a command buffer which tracks its state to avoid concurrent usage * @brief A wrapper around a command buffer which tracks its state to avoid concurrent usage
*/ */
struct CommandBufferSlot { struct CommandBufferSlot {
std::atomic_flag active; //!< If the command buffer is currently being recorded to std::atomic_flag active{true}; //!< If the command buffer is currently being recorded to
const vk::raii::Device &device; const vk::raii::Device &device;
vk::raii::CommandBuffer commandBuffer; vk::raii::CommandBuffer commandBuffer;
vk::raii::Fence fence; //!< A fence used for tracking all submits of a buffer vk::raii::Fence fence; //!< A fence used for tracking all submits of a buffer

View File

@ -116,10 +116,9 @@ namespace skyline::gpu {
if (!signalled.test(std::memory_order_consume)) { if (!signalled.test(std::memory_order_consume)) {
auto it{dependencies.begin()}, next{std::next(it)}; auto it{dependencies.begin()}, next{std::next(it)};
if (it != dependencies.end()) { if (it != dependencies.end()) {
while (next != dependencies.end()) { for (; next != dependencies.end(); next++) {
(*it)->next = *next; (*it)->next = *next;
it = next; it = next;
next = std::next(next);
} }
} }
AttachObject(*dependencies.begin()); AttachObject(*dependencies.begin());

View File

@ -4,6 +4,8 @@
#pragma once #pragma once
/* A collection of various types from AOSP that allow us to access private APIs for Native Window which we utilize for emulating the guest SF more accurately */
/** /**
* @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativebase/include/nativebase/nativebase.h;l=29;drc=cb496acbe593326e8d5d563847067d02b2df40ec * @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativebase/include/nativebase/nativebase.h;l=29;drc=cb496acbe593326e8d5d563847067d02b2df40ec
*/ */

View File

@ -38,19 +38,22 @@ namespace skyline::gpu {
} }
} }
void PresentationEngine::ChoreographerCallback(long frameTimeNanos, PresentationEngine *engine) { void PresentationEngine::ChoreographerCallback(int64_t frameTimeNanos, PresentationEngine *engine) {
u64 cycleLength{frameTimeNanos - engine->lastChoreographerTime}; // If the duration of this cycle deviates by ±0.5ms from the current refresh cycle duration then we reevaluate it
if (std::abs(static_cast<i64>(cycleLength - engine->refreshCycleDuration)) > (constant::NsInMillisecond / 2)) { i64 cycleLength{frameTimeNanos - engine->lastChoreographerTime};
if (std::abs(cycleLength - engine->refreshCycleDuration) > (constant::NsInMillisecond / 2)) {
if (engine->window) if (engine->window)
engine->window->perform(engine->window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION, &engine->refreshCycleDuration); engine->window->perform(engine->window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION, &engine->refreshCycleDuration);
else else
engine->refreshCycleDuration = cycleLength; engine->refreshCycleDuration = cycleLength;
} }
// Record the current cycle's timestamp and signal the V-Sync event to notify the game that a frame has been displayed
engine->lastChoreographerTime = frameTimeNanos; engine->lastChoreographerTime = frameTimeNanos;
engine->vsyncEvent->Signal(); engine->vsyncEvent->Signal();
AChoreographer_postFrameCallback(AChoreographer_getInstance(), reinterpret_cast<AChoreographer_frameCallback>(&ChoreographerCallback), engine); // Post the frame callback to be triggered on the next display refresh
AChoreographer_postFrameCallback64(AChoreographer_getInstance(), reinterpret_cast<AChoreographer_frameCallback>(&ChoreographerCallback), engine);
} }
void PresentationEngine::ChoreographerThread() { void PresentationEngine::ChoreographerThread() {
@ -58,7 +61,7 @@ namespace skyline::gpu {
try { try {
signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, signal::ExceptionalSignalHandler); signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, signal::ExceptionalSignalHandler);
choreographerLooper = ALooper_prepare(0); choreographerLooper = ALooper_prepare(0);
AChoreographer_postFrameCallback(AChoreographer_getInstance(), reinterpret_cast<AChoreographer_frameCallback>(&ChoreographerCallback), this); AChoreographer_postFrameCallback64(AChoreographer_getInstance(), reinterpret_cast<AChoreographer_frameCallback>(&ChoreographerCallback), this);
ALooper_pollAll(-1, nullptr, nullptr, nullptr); // Will block and process callbacks till ALooper_wake() is called ALooper_pollAll(-1, nullptr, nullptr, nullptr); // Will block and process callbacks till ALooper_wake() is called
} catch (const signal::SignalException &e) { } catch (const signal::SignalException &e) {
state.logger->Error("{}\nStack Trace:{}", e.what(), state.loader->GetStackTrace(e.frames)); state.logger->Error("{}\nStack Trace:{}", e.what(), state.loader->GetStackTrace(e.frames));
@ -180,12 +183,15 @@ namespace skyline::gpu {
if (window->common.version != sizeof(ANativeWindow)) if (window->common.version != sizeof(ANativeWindow))
throw exception("ANativeWindow* has unexpected version: {} instead of {}", window->common.version, sizeof(ANativeWindow)); throw exception("ANativeWindow* has unexpected version: {} instead of {}", window->common.version, sizeof(ANativeWindow));
if (windowCrop) int result;
window->perform(window, NATIVE_WINDOW_SET_CROP, &windowCrop); if (windowCrop && (result = window->perform(window, NATIVE_WINDOW_SET_CROP, &windowCrop)))
if (windowScalingMode != NativeWindowScalingMode::ScaleToWindow) throw exception("Setting the layer crop to ({}-{})x({}-{}) failed with {}", windowCrop.left, windowCrop.right, windowCrop.top, windowCrop.bottom, result);
window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE, static_cast<i32>(windowScalingMode));
window->perform(window, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS, true); if (windowScalingMode != NativeWindowScalingMode::ScaleToWindow && (result = window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE, static_cast<i32>(windowScalingMode))))
throw exception("Setting the layer scaling mode to '{}' failed with {}", ToString(windowScalingMode), result);
if ((result = window->perform(window, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS, true)))
throw exception("Enabling frame timestamps failed with {}", result);
surfaceCondition.notify_all(); surfaceCondition.notify_all();
} else { } else {
@ -272,15 +278,19 @@ namespace skyline::gpu {
} }
if (frameTimestamp) { if (frameTimestamp) {
auto now{util::GetTimeNs()}; i64 now{static_cast<i64>(util::GetTimeNs())};
auto sampleWeight{swapInterval ? constant::NsInSecond / (refreshCycleDuration * swapInterval) : 10}; //!< The weight of each sample in calculating the average, we arbitrarily average 10 samples for unlocked FPS auto sampleWeight{swapInterval ? constant::NsInSecond / (refreshCycleDuration * swapInterval) : 10}; //!< The weight of each sample in calculating the average, we arbitrarily average 10 samples for unlocked FPS
u64 currentFrametime{now - frameTimestamp}; auto weightedAverage{[](auto weight, auto previousAverage, auto current) {
averageFrametimeNs = (((sampleWeight - 1) * averageFrametimeNs) / sampleWeight) + (currentFrametime / sampleWeight); return (((weight - 1) * previousAverage) + current) / weight;
}}; //!< Modified moving average (https://en.wikipedia.org/wiki/Moving_average#Modified_moving_average)
i64 currentFrametime{now - frameTimestamp};
averageFrametimeNs = weightedAverage(sampleWeight, averageFrametimeNs, currentFrametime);
AverageFrametimeMs = static_cast<jfloat>(averageFrametimeNs) / constant::NsInMillisecond; AverageFrametimeMs = static_cast<jfloat>(averageFrametimeNs) / constant::NsInMillisecond;
i64 currentFrametimeDeviation{std::abs(static_cast<i64>(averageFrametimeNs - currentFrametime))}; i64 currentFrametimeDeviation{std::abs(averageFrametimeNs - currentFrametime)};
averageFrametimeDeviationNs = (((sampleWeight - 1) * averageFrametimeDeviationNs) / sampleWeight) + (currentFrametimeDeviation / sampleWeight); averageFrametimeDeviationNs = weightedAverage(sampleWeight, averageFrametimeDeviationNs, currentFrametimeDeviation);
AverageFrametimeDeviationMs = static_cast<jfloat>(averageFrametimeDeviationNs) / constant::NsInMillisecond; AverageFrametimeDeviationMs = static_cast<jfloat>(averageFrametimeDeviationNs) / constant::NsInMillisecond;
Fps = std::round(static_cast<float>(constant::NsInSecond) / averageFrametimeNs); Fps = std::round(static_cast<float>(constant::NsInSecond) / averageFrametimeNs);

View File

@ -21,9 +21,9 @@ namespace skyline::gpu {
GPU &gpu; GPU &gpu;
std::mutex mutex; //!< Synchronizes access to the surface objects std::mutex mutex; //!< Synchronizes access to the surface objects
std::condition_variable surfaceCondition; //!< Allows us to efficiently wait for Vulkan surface to be initialized std::condition_variable surfaceCondition; //!< Signalled when a valid Vulkan surface is available
jobject jSurface{}; //!< The Java Surface object backing the ANativeWindow jobject jSurface{}; //!< The Java Surface object backing the ANativeWindow
ANativeWindow *window{}; //!< A pointer to an Android Native Window which is the surface we draw to ANativeWindow *window{}; //!< The backing Android Native Window for the surface we draw to, we keep this around to access private APIs not exposed via Vulkan
service::hosbinder::AndroidRect windowCrop{}; //!< A rectangle with the bounds of the current crop performed on the image prior to presentation service::hosbinder::AndroidRect windowCrop{}; //!< A rectangle with the bounds of the current crop performed on the image prior to presentation
service::hosbinder::NativeWindowScalingMode windowScalingMode{service::hosbinder::NativeWindowScalingMode::ScaleToWindow}; //!< The mode in which the cropped image is scaled up to the surface service::hosbinder::NativeWindowScalingMode windowScalingMode{service::hosbinder::NativeWindowScalingMode::ScaleToWindow}; //!< The mode in which the cropped image is scaled up to the surface
service::hosbinder::NativeWindowTransform windowTransform{}; //!< The transformation performed on the image prior to presentation service::hosbinder::NativeWindowTransform windowTransform{}; //!< The transformation performed on the image prior to presentation
@ -40,23 +40,23 @@ namespace skyline::gpu {
static constexpr size_t MaxSwapchainImageCount{6}; //!< The maximum amount of swapchain textures, this affects the amount of images that can be in the swapchain static constexpr size_t MaxSwapchainImageCount{6}; //!< The maximum amount of swapchain textures, this affects the amount of images that can be in the swapchain
std::array<std::shared_ptr<Texture>, MaxSwapchainImageCount> images; //!< All the swapchain textures in the same order as supplied by the host swapchain std::array<std::shared_ptr<Texture>, MaxSwapchainImageCount> images; //!< All the swapchain textures in the same order as supplied by the host swapchain
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown in nanoseconds i64 frameTimestamp{}; //!< The timestamp of the last frame being shown in nanoseconds
u64 averageFrametimeNs{}; //!< The average time between frames in nanoseconds i64 averageFrametimeNs{}; //!< The average time between frames in nanoseconds
u64 averageFrametimeDeviationNs{}; //!< The average deviation of frametimes in nanoseconds i64 averageFrametimeDeviationNs{}; //!< The average deviation of frametimes in nanoseconds
perfetto::Track presentationTrack; //!< Perfetto track used for presentation events perfetto::Track presentationTrack; //!< Perfetto track used for presentation events
std::thread choreographerThread; //!< A thread for signalling the V-Sync event using AChoreographer std::thread choreographerThread; //!< A thread for signalling the V-Sync event and measure the refresh cycle duration using AChoreographer
ALooper *choreographerLooper{}; //!< The looper object associated with the Choreographer thread ALooper *choreographerLooper{};
u64 lastChoreographerTime{}; //!< The timestamp of the last invocation of Choreographer::doFrame i64 lastChoreographerTime{}; //!< The timestamp of the last invocation of Choreographer::doFrame
u64 refreshCycleDuration{}; //!< The duration of a single refresh cycle for the display in nanoseconds i64 refreshCycleDuration{}; //!< The duration of a single refresh cycle for the display in nanoseconds
/** /**
* @url https://developer.android.com/ndk/reference/group/choreographer#achoreographer_framecallback * @url https://developer.android.com/ndk/reference/group/choreographer#achoreographer_postframecallback64
*/ */
static void ChoreographerCallback(long frameTimeNanos, PresentationEngine *engine); static void ChoreographerCallback(int64_t frameTimeNanos, PresentationEngine *engine);
/** /**
* @brief The entry point for the the Choreographer thread, the function runs ALooper on the thread * @brief The entry point for the the Choreographer thread, sets up the AChoreographer callback then runs ALooper on the thread
*/ */
void ChoreographerThread(); void ChoreographerThread();

View File

@ -140,7 +140,7 @@ namespace skyline::gpu {
u8 *bufferData; u8 *bufferData;
auto stagingBuffer{[&]() -> std::shared_ptr<memory::StagingBuffer> { auto stagingBuffer{[&]() -> std::shared_ptr<memory::StagingBuffer> {
if (tiling == vk::ImageTiling::eOptimal || !std::holds_alternative<memory::Image>(backing)) { if (tiling == vk::ImageTiling::eOptimal || !std::holds_alternative<memory::Image>(backing)) {
// We need a staging buffer for all optimal copies (Since we aren't aware of the host optimal layout) and linear textures which we cannot map on the CPU since we do not have access to their backing VkDeviceMemory // We need a staging buffer for all optimal copies (since we aren't aware of the host optimal layout) and linear textures which we cannot map on the CPU since we do not have access to their backing VkDeviceMemory
auto stagingBuffer{gpu.memory.AllocateStagingBuffer(size)}; auto stagingBuffer{gpu.memory.AllocateStagingBuffer(size)};
bufferData = stagingBuffer->data(); bufferData = stagingBuffer->data();
return stagingBuffer; return stagingBuffer;
@ -156,19 +156,19 @@ namespace skyline::gpu {
if (guest->tileMode == texture::TileMode::Block) { if (guest->tileMode == texture::TileMode::Block) {
// Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32 // Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32
constexpr u8 sectorWidth{16}; // The width of a sector in bytes constexpr u8 SectorWidth{16}; // The width of a sector in bytes
constexpr u8 sectorHeight{2}; // The height of a sector in lines constexpr u8 SectorHeight{2}; // The height of a sector in lines
constexpr u8 gobWidth{64}; // The width of a GOB in bytes constexpr u8 GobWidth{64}; // The width of a GOB in bytes
constexpr u8 gobHeight{8}; // The height of a GOB in lines constexpr u8 GobHeight{8}; // The height of a GOB in lines
auto blockHeight{guest->tileConfig.blockHeight}; // The height of the blocks in GOBs auto blockHeight{guest->tileConfig.blockHeight}; // The height of the blocks in GOBs
auto robHeight{gobHeight * blockHeight}; // The height of a single ROB (Row of Blocks) in lines auto robHeight{GobHeight * blockHeight}; // The height of a single ROB (Row of Blocks) in lines
auto surfaceHeight{dimensions.height / guest->format.blockHeight}; // The height of the surface in lines auto surfaceHeight{dimensions.height / guest->format.blockHeight}; // The height of the surface in lines
auto surfaceHeightRobs{util::AlignUp(surfaceHeight, robHeight) / robHeight}; // The height of the surface in ROBs (Row Of Blocks) auto surfaceHeightRobs{util::AlignUp(surfaceHeight, robHeight) / robHeight}; // The height of the surface in ROBs (Row Of Blocks)
auto robWidthBytes{util::AlignUp((guest->tileConfig.surfaceWidth / guest->format.blockWidth) * guest->format.bpb, gobWidth)}; // The width of a ROB in bytes auto robWidthBytes{util::AlignUp((guest->tileConfig.surfaceWidth / guest->format.blockWidth) * guest->format.bpb, GobWidth)}; // The width of a ROB in bytes
auto robWidthBlocks{robWidthBytes / gobWidth}; // The width of a ROB in blocks (and GOBs because block width == 1 on the Tegra X1) auto robWidthBlocks{robWidthBytes / GobWidth}; // The width of a ROB in blocks (and GOBs because block width == 1 on the Tegra X1)
auto robBytes{robWidthBytes * robHeight}; // The size of a ROB in bytes auto robBytes{robWidthBytes * robHeight}; // The size of a ROB in bytes
auto gobYOffset{robWidthBytes * gobHeight}; // The offset of the next Y-axis GOB from the current one in linear space auto gobYOffset{robWidthBytes * GobHeight}; // The offset of the next Y-axis GOB from the current one in linear space
auto inputSector{pointer}; // The address of the input sector auto inputSector{pointer}; // The address of the input sector
auto outputRob{bufferData}; // The address of the output block auto outputRob{bufferData}; // The address of the output block
@ -178,22 +178,22 @@ namespace skyline::gpu {
for (u32 block{}; block < robWidthBlocks; block++) { // Every ROB contains `surfaceWidthBlocks` Blocks for (u32 block{}; block < robWidthBlocks; block++) { // Every ROB contains `surfaceWidthBlocks` Blocks
auto outputGob{outputBlock}; // We iterate through a GOB independently of the block auto outputGob{outputBlock}; // We iterate through a GOB independently of the block
for (u32 gobY{}; gobY < blockHeight; gobY++) { // Every Block contains `blockHeight` Y-axis GOBs for (u32 gobY{}; gobY < blockHeight; gobY++) { // Every Block contains `blockHeight` Y-axis GOBs
for (u32 index{}; index < sectorWidth * sectorHeight; index++) { // Every Y-axis GOB contains `sectorWidth * sectorHeight` sectors for (u32 index{}; index < SectorWidth * SectorHeight; index++) { // Every Y-axis GOB contains `sectorWidth * sectorHeight` sectors
u32 xT{((index << 3) & 0b10000) | ((index << 1) & 0b100000)}; // Morton-Swizzle on the X-axis u32 xT{((index << 3) & 0b10000) | ((index << 1) & 0b100000)}; // Morton-Swizzle on the X-axis
u32 yT{((index >> 1) & 0b110) | (index & 0b1)}; // Morton-Swizzle on the Y-axis u32 yT{((index >> 1) & 0b110) | (index & 0b1)}; // Morton-Swizzle on the Y-axis
std::memcpy(outputGob + (yT * robWidthBytes) + xT, inputSector, sectorWidth); std::memcpy(outputGob + (yT * robWidthBytes) + xT, inputSector, SectorWidth);
inputSector += sectorWidth; // `sectorWidth` bytes are of sequential image data inputSector += SectorWidth; // `sectorWidth` bytes are of sequential image data
} }
outputGob += gobYOffset; // Increment the output GOB to the next Y-axis GOB outputGob += gobYOffset; // Increment the output GOB to the next Y-axis GOB
} }
inputSector += paddingY; // Increment the input sector to the next sector inputSector += paddingY; // Increment the input sector to the next sector
outputBlock += gobWidth; // Increment the output block to the next block (As Block Width = 1 GOB Width) outputBlock += GobWidth; // Increment the output block to the next block (As Block Width = 1 GOB Width)
} }
outputRob += robBytes; // Increment the output block to the next ROB outputRob += robBytes; // Increment the output block to the next ROB
y += robHeight; // Increment the Y position to the next ROB y += robHeight; // Increment the Y position to the next ROB
blockHeight = static_cast<u8>(std::min(static_cast<u32>(blockHeight), (surfaceHeight - y) / gobHeight)); // Calculate the amount of Y GOBs which aren't padding blockHeight = static_cast<u8>(std::min(static_cast<u32>(blockHeight), (surfaceHeight - y) / GobHeight)); // Calculate the amount of Y GOBs which aren't padding
paddingY = (guest->tileConfig.blockHeight - blockHeight) * (sectorWidth * sectorWidth * sectorHeight); // Calculate the amount of padding between contiguous sectors paddingY = (guest->tileConfig.blockHeight - blockHeight) * (SectorWidth * SectorWidth * SectorHeight); // Calculate the amount of padding between contiguous sectors
} }
} else if (guest->tileMode == texture::TileMode::Pitch) { } else if (guest->tileMode == texture::TileMode::Pitch) {
auto sizeLine{guest->format.GetSize(dimensions.width, 1)}; // The size of a single line of pixel data auto sizeLine{guest->format.GetSize(dimensions.width, 1)}; // The size of a single line of pixel data

View File

@ -264,6 +264,7 @@ namespace skyline::gpu {
/** /**
* @brief Acquires an exclusive lock on the texture for the calling thread * @brief Acquires an exclusive lock on the texture for the calling thread
* @note Naming is in accordance to the BasicLockable named requirement
*/ */
void lock() { void lock() {
mutex.lock(); mutex.lock();
@ -271,6 +272,7 @@ namespace skyline::gpu {
/** /**
* @brief Relinquishes an existing lock on the texture by the calling thread * @brief Relinquishes an existing lock on the texture by the calling thread
* @note Naming is in accordance to the BasicLockable named requirement
*/ */
void unlock() { void unlock() {
mutex.unlock(); mutex.unlock();
@ -278,6 +280,7 @@ namespace skyline::gpu {
/** /**
* @brief Attempts to acquire an exclusive lock but returns immediately if it's captured by another thread * @brief Attempts to acquire an exclusive lock but returns immediately if it's captured by another thread
* @note Naming is in accordance to the Lockable named requirement
*/ */
bool try_lock() { bool try_lock() {
return mutex.try_lock(); return mutex.try_lock();

View File

@ -14,8 +14,8 @@
namespace skyline::service::hosbinder { namespace skyline::service::hosbinder {
GraphicBufferProducer::GraphicBufferProducer(const DeviceState &state) : state(state), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)) {} GraphicBufferProducer::GraphicBufferProducer(const DeviceState &state) : state(state), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)) {}
u8 GraphicBufferProducer::GetPendingBufferCount() { u32 GraphicBufferProducer::GetPendingBufferCount() {
u8 count{}; u32 count{};
for (auto it{queue.begin()}, end{it + activeSlotCount}; it < end; it++) for (auto it{queue.begin()}, end{it + activeSlotCount}; it < end; it++)
if (it->state == BufferState::Queued) if (it->state == BufferState::Queued)
count++; count++;
@ -113,7 +113,7 @@ namespace skyline::service::hosbinder {
size_t index{}; size_t index{};
std::string bufferString; std::string bufferString;
for (auto &bufferSlot : queue) for (auto &bufferSlot : queue)
bufferString += util::Format("\n#{} - State: {}, Has Graphic Buffer: {}, Frame Number: {}", ++index, ToString(bufferSlot.state), static_cast<bool>(bufferSlot.graphicBuffer), bufferSlot.frameNumber); bufferString += util::Format("\n#{} - State: {}, Has Graphic Buffer: {}, Frame Number: {}", ++index, ToString(bufferSlot.state), bufferSlot.graphicBuffer != nullptr, bufferSlot.frameNumber);
state.logger->Warn("Cannot find any free buffers to dequeue:{}", bufferString); state.logger->Warn("Cannot find any free buffers to dequeue:{}", bufferString);
return AndroidStatus::InvalidOperation; return AndroidStatus::InvalidOperation;
} }
@ -210,11 +210,11 @@ namespace skyline::service::hosbinder {
if (graphicBuffer.magic != GraphicBuffer::Magic) if (graphicBuffer.magic != GraphicBuffer::Magic)
throw exception("Unexpected GraphicBuffer magic: 0x{} (Expected: 0x{})", graphicBuffer.magic, GraphicBuffer::Magic); throw exception("Unexpected GraphicBuffer magic: 0x{} (Expected: 0x{})", graphicBuffer.magic, GraphicBuffer::Magic);
else if (graphicBuffer.intCount != sizeof(NvGraphicHandle) / sizeof(u32)) else if (graphicBuffer.intCount != sizeof(NvGraphicHandle) / sizeof(u32))
throw exception("Unexpected GraphicBuffer native_handle integer count: 0x{} (Expected: 0x{})", graphicBuffer.intCount, sizeof(NvGraphicHandle)); throw exception("Unexpected GraphicBuffer native_handle integer count: 0x{} (Expected: 0x{})", graphicBuffer.intCount, sizeof(NvGraphicHandle) / sizeof(u32));
auto &handle{graphicBuffer.graphicHandle}; auto &handle{graphicBuffer.graphicHandle};
if (handle.magic != NvGraphicHandle::Magic) if (handle.magic != NvGraphicHandle::Magic)
throw exception("Unexpected NvGraphicHandle magic: {}", handle.surfaceCount); throw exception("Unexpected NvGraphicHandle magic: {}", handle.magic);
else if (handle.surfaceCount < 1) else if (handle.surfaceCount < 1)
throw exception("At least one surface is required in a buffer: {}", handle.surfaceCount); throw exception("At least one surface is required in a buffer: {}", handle.surfaceCount);
else if (handle.surfaceCount > 1) else if (handle.surfaceCount > 1)
@ -234,7 +234,7 @@ namespace skyline::service::hosbinder {
slot = std::distance(queue.begin(), bufferSlot); slot = std::distance(queue.begin(), bufferSlot);
preallocatedBufferCount = std::count_if(queue.begin(), queue.end(), [](const BufferSlot &slot) { return slot.graphicBuffer && slot.isPreallocated; }); preallocatedBufferCount = std::count_if(queue.begin(), queue.end(), [](const BufferSlot &slot) { return slot.graphicBuffer && slot.isPreallocated; });
activeSlotCount = std::count_if(queue.begin(), queue.end(), [](const BufferSlot &slot) { return static_cast<bool>(slot.graphicBuffer); }); activeSlotCount = std::count_if(queue.begin(), queue.end(), [](const BufferSlot &slot) { return slot.graphicBuffer != nullptr; });
state.logger->Debug("#{} - Dimensions: {}x{} [Stride: {}], Format: {}, Layout: {}, {}: {}, Usage: 0x{:X}, NvMap {}: {}, Buffer Start/End: 0x{:X} -> 0x{:X}", slot, surface.width, surface.height, handle.stride, ToString(graphicBuffer.format), ToString(surface.layout), surface.layout == NvSurfaceLayout::Blocklinear ? "Block Height" : "Pitch", surface.layout == NvSurfaceLayout::Blocklinear ? 1U << surface.blockHeightLog2 : surface.pitch, graphicBuffer.usage, surface.nvmapHandle ? "Handle" : "ID", surface.nvmapHandle ? surface.nvmapHandle : handle.nvmapId, surface.offset, surface.offset + surface.size); state.logger->Debug("#{} - Dimensions: {}x{} [Stride: {}], Format: {}, Layout: {}, {}: {}, Usage: 0x{:X}, NvMap {}: {}, Buffer Start/End: 0x{:X} -> 0x{:X}", slot, surface.width, surface.height, handle.stride, ToString(graphicBuffer.format), ToString(surface.layout), surface.layout == NvSurfaceLayout::Blocklinear ? "Block Height" : "Pitch", surface.layout == NvSurfaceLayout::Blocklinear ? 1U << surface.blockHeightLog2 : surface.pitch, graphicBuffer.usage, surface.nvmapHandle ? "Handle" : "ID", surface.nvmapHandle ? surface.nvmapHandle : handle.nvmapId, surface.offset, surface.offset + surface.size);
return AndroidStatus::Ok; return AndroidStatus::Ok;
@ -547,7 +547,7 @@ namespace skyline::service::hosbinder {
buffer.state = BufferState::Free; buffer.state = BufferState::Free;
buffer.frameNumber = 0; buffer.frameNumber = 0;
buffer.wasBufferRequested = false; buffer.wasBufferRequested = false;
buffer.isPreallocated = static_cast<bool>(graphicBuffer); buffer.isPreallocated = graphicBuffer != nullptr;
buffer.graphicBuffer = graphicBuffer ? std::make_unique<GraphicBuffer>(*graphicBuffer) : nullptr; buffer.graphicBuffer = graphicBuffer ? std::make_unique<GraphicBuffer>(*graphicBuffer) : nullptr;
buffer.texture = {}; buffer.texture = {};
@ -577,7 +577,7 @@ namespace skyline::service::hosbinder {
} }
preallocatedBufferCount = std::count_if(queue.begin(), queue.end(), [](const BufferSlot &slot) { return slot.graphicBuffer && slot.isPreallocated; }); preallocatedBufferCount = std::count_if(queue.begin(), queue.end(), [](const BufferSlot &slot) { return slot.graphicBuffer && slot.isPreallocated; });
activeSlotCount = std::count_if(queue.begin(), queue.end(), [](const BufferSlot &slot) { return static_cast<bool>(slot.graphicBuffer); }); activeSlotCount = std::count_if(queue.begin(), queue.end(), [](const BufferSlot &slot) { return slot.graphicBuffer != nullptr; });
bufferEvent->Signal(); bufferEvent->Signal();
@ -668,7 +668,7 @@ namespace skyline::service::hosbinder {
} }
case TransactionCode::Connect: { case TransactionCode::Connect: {
auto hasProducerListener{static_cast<bool>(in.Pop<u32>())}; bool hasProducerListener{in.Pop<u32>() != 0};
if (hasProducerListener) if (hasProducerListener)
throw exception("Callbacks using IProducerListener are not supported"); throw exception("Callbacks using IProducerListener are not supported");

View File

@ -70,7 +70,7 @@ namespace skyline::service::hosbinder {
/** /**
* @return The amount of buffers which have been queued onto the consumer * @return The amount of buffers which have been queued onto the consumer
*/ */
u8 GetPendingBufferCount(); u32 GetPendingBufferCount();
/** /**
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h;l=67-80; * @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h;l=67-80;

View File

@ -37,7 +37,7 @@ namespace skyline::service::hosbinder {
throw exception("Adjusting Binder object reference count for unknown object: #{}", binderHandle); throw exception("Adjusting Binder object reference count for unknown object: #{}", binderHandle);
auto value{request.Pop<i32>()}; auto value{request.Pop<i32>()};
bool isStrong{static_cast<bool>(request.Pop<u32>())}; bool isStrong{request.Pop<u32>() != 0};
if (isStrong) { if (isStrong) {
if (layerStrongReferenceCount != InitialStrongReferenceCount) if (layerStrongReferenceCount != InitialStrongReferenceCount)
layerStrongReferenceCount += value; layerStrongReferenceCount += value;

View File

@ -33,17 +33,15 @@ namespace skyline::service::hosbinder {
class IHOSBinderDriver : public BaseService { class IHOSBinderDriver : public BaseService {
private: private:
DisplayId displayId{DisplayId::Null}; //!< The ID of the display that the layer is connected to DisplayId displayId{DisplayId::Null}; //!< The ID of the display that the layer is connected to
constexpr static u64 DefaultLayerId{1}; //!< The VI ID of the default (and only) layer in our surface stack
constexpr static u32 DefaultBinderLayerHandle{1}; //!< The handle as assigned by SurfaceFlinger of the default layer
constexpr static i32 InitialStrongReferenceCount{std::numeric_limits<i32>::min()}; //!< Initial value for the strong reference count, weak references will keep the object alive till the strong reference count is first mutated constexpr static i32 InitialStrongReferenceCount{std::numeric_limits<i32>::min()}; //!< Initial value for the strong reference count, weak references will keep the object alive till the strong reference count is first mutated
i32 layerStrongReferenceCount; //!< The amount of strong references to the layer object i32 layerStrongReferenceCount; //!< The amount of strong references to the layer object
i32 layerWeakReferenceCount; //!< The amount of weak references to the layer object i32 layerWeakReferenceCount; //!< The amount of weak references to the layer object, these only matter when there are no strong references
constexpr static u64 DefaultLayerId{1}; //!< The VI ID of the default (and only) layer in our surface stack
constexpr static u32 DefaultBinderLayerHandle{1}; //!< The handle as assigned by SurfaceFlinger of the default layer
std::optional<GraphicBufferProducer> layer; //!< The IGraphicBufferProducer backing the layer (NativeWindow) std::optional<GraphicBufferProducer> layer; //!< The IGraphicBufferProducer backing the layer (NativeWindow)
void AddWeakLayerReference(u32 count = 1) {
}
public: public:
IHOSBinderDriver(const DeviceState &state, ServiceManager &manager); IHOSBinderDriver(const DeviceState &state, ServiceManager &manager);

View File

@ -54,7 +54,7 @@ namespace skyline::service::hosbinder {
*/ */
template<typename ValueType> template<typename ValueType>
ValueType *PopOptionalFlattenable() { ValueType *PopOptionalFlattenable() {
bool hasObject{static_cast<bool>(Pop<u32>())}; bool hasObject{Pop<u32>() != 0};
if (hasObject) { if (hasObject) {
auto size{Pop<u64>()}; auto size{Pop<u64>()};
if (size != sizeof(ValueType)) if (size != sizeof(ValueType))
@ -65,14 +65,11 @@ namespace skyline::service::hosbinder {
} }
} }
/**
* @brief Writes a value to the Parcel
*/
template<typename ValueType> template<typename ValueType>
void Push(const ValueType &value) { void Push(const ValueType &value) {
auto offset{data.size()}; auto offset{data.size()};
data.resize(offset + sizeof(ValueType)); data.resize(offset + sizeof(ValueType));
*(reinterpret_cast<ValueType *>(data.data() + offset)) = value; std::memcpy(data.data() + offset, &value, sizeof(ValueType));
} }
/** /**
@ -98,20 +95,16 @@ namespace skyline::service::hosbinder {
} }
} }
/**
* @brief Writes an object to the Parcel
*/
template<typename ObjectType> template<typename ObjectType>
void PushObject(const ObjectType &object) { void PushObject(const ObjectType &object) {
auto offset{objects.size()}; auto offset{objects.size()};
objects.resize(offset + sizeof(ObjectType)); objects.resize(offset + sizeof(ObjectType));
*(reinterpret_cast<ObjectType *>(objects.data() + offset)) = object; std::memcpy(objects.data() + offset, &object, sizeof(ObjectType));
} }
/** /**
* @brief Writes the Parcel object out * @param buffer The buffer to write the flattened Parcel into
* @param buffer The buffer to write the Parcel object to * @return The total size of the Parcel
* @return The total size of the message
*/ */
u64 WriteParcel(span<u8> buffer); u64 WriteParcel(span<u8> buffer);
}; };