mirror of
https://github.com/Takiiiiiiii/strato.git
synced 2025-07-17 08:46:39 +00:00
Rework GraphicBufferProducer
This commit makes GraphicBufferProducer significantly more accurate by matching the behavior of AOSP alongside mirroring the tweaks made by Nintendo. It eliminates a lot of the magic structures and enumerations used prior and replaces them with the correct values from AOSP or HOS. There was a lot of functional inaccuracy as well which was fixed, we emulate the exact subset of HOS behavior that we need to. A lot of the intermediate layers such as GraphicBufferConsumer or Gralloc/Sync are not emulated as they're pointless abstractions here.
This commit is contained in:
@ -3,7 +3,7 @@
|
||||
|
||||
#include <android/native_window_jni.h>
|
||||
#include <gpu.h>
|
||||
#include "jvm.h"
|
||||
#include <jvm.h>
|
||||
#include "presentation_engine.h"
|
||||
|
||||
extern skyline::i32 Fps;
|
||||
@ -22,7 +22,30 @@ namespace skyline::gpu {
|
||||
env->DeleteGlobalRef(surface);
|
||||
}
|
||||
|
||||
void PresentationEngine::UpdateSwapchain(u32 imageCount, vk::Format imageFormat, vk::Extent2D imageExtent) {
|
||||
service::hosbinder::NativeWindowTransform GetAndroidTransform(vk::SurfaceTransformFlagBitsKHR transform) {
|
||||
using NativeWindowTransform = service::hosbinder::NativeWindowTransform;
|
||||
switch (transform) {
|
||||
case vk::SurfaceTransformFlagBitsKHR::eIdentity:
|
||||
case vk::SurfaceTransformFlagBitsKHR::eInherit:
|
||||
return NativeWindowTransform::Identity;
|
||||
case vk::SurfaceTransformFlagBitsKHR::eRotate90:
|
||||
return NativeWindowTransform::Rotate90;
|
||||
case vk::SurfaceTransformFlagBitsKHR::eRotate180:
|
||||
return NativeWindowTransform::Rotate180;
|
||||
case vk::SurfaceTransformFlagBitsKHR::eRotate270:
|
||||
return NativeWindowTransform::Rotate270;
|
||||
case vk::SurfaceTransformFlagBitsKHR::eHorizontalMirror:
|
||||
return NativeWindowTransform::MirrorHorizontal;
|
||||
case vk::SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate90:
|
||||
return NativeWindowTransform::MirrorHorizontalRotate90;
|
||||
case vk::SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate180:
|
||||
return NativeWindowTransform::MirrorVertical;
|
||||
case vk::SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate270:
|
||||
return NativeWindowTransform::MirrorVerticalRotate90;
|
||||
}
|
||||
}
|
||||
|
||||
void PresentationEngine::UpdateSwapchain(u16 imageCount, vk::Format imageFormat, vk::Extent2D imageExtent) {
|
||||
if (!imageCount)
|
||||
return;
|
||||
|
||||
@ -36,6 +59,8 @@ namespace skyline::gpu {
|
||||
if ((capabilities.supportedUsageFlags & presentUsage) != presentUsage)
|
||||
throw exception("Swapchain doesn't support image usage '{}': {}", vk::to_string(presentUsage), vk::to_string(capabilities.supportedUsageFlags));
|
||||
|
||||
transformHint = GetAndroidTransform(capabilities.currentTransform);
|
||||
|
||||
vkSwapchain = vk::raii::SwapchainKHR(gpu.vkDevice, vk::SwapchainCreateInfoKHR{
|
||||
.surface = **vkSurface,
|
||||
.minImageCount = imageCount,
|
||||
@ -59,7 +84,7 @@ namespace skyline::gpu {
|
||||
}
|
||||
|
||||
void PresentationEngine::UpdateSurface(jobject newSurface) {
|
||||
std::lock_guard lock(mutex);
|
||||
std::lock_guard guard(mutex);
|
||||
|
||||
auto env{state.jvm->GetEnv()};
|
||||
if (!env->IsSameObject(surface, nullptr)) {
|
||||
@ -85,26 +110,45 @@ namespace skyline::gpu {
|
||||
}
|
||||
|
||||
std::shared_ptr<Texture> PresentationEngine::CreatePresentationTexture(const std::shared_ptr<GuestTexture> &texture, u32 slot) {
|
||||
std::unique_lock lock(mutex);
|
||||
std::lock_guard guard(mutex);
|
||||
if (swapchain.imageCount <= slot)
|
||||
UpdateSwapchain(slot + 1, texture->format.vkFormat, texture->dimensions);
|
||||
UpdateSwapchain(std::max(slot + 1, 2U), texture->format.vkFormat, texture->dimensions);
|
||||
return texture->InitializeTexture(vk::raii::Image(gpu.vkDevice, vkSwapchain->getImages().at(slot)));
|
||||
}
|
||||
|
||||
u32 PresentationEngine::GetFreeTexture() {
|
||||
service::hosbinder::AndroidStatus PresentationEngine::GetFreeTexture(bool async, i32 &slot) {
|
||||
using AndroidStatus = service::hosbinder::AndroidStatus;
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
auto nextImage{vkSwapchain->acquireNextImage(std::numeric_limits<u64>::max())};
|
||||
if (nextImage.first == vk::Result::eErrorSurfaceLostKHR || nextImage.first == vk::Result::eSuboptimalKHR) {
|
||||
surfaceCondition.wait(lock, [this]() { return vkSurface.has_value(); });
|
||||
return GetFreeTexture();
|
||||
if (swapchain.dequeuedCount < swapchain.imageCount) {
|
||||
swapchain.dequeuedCount++;
|
||||
|
||||
vk::raii::Fence fence(state.gpu->vkDevice, vk::FenceCreateInfo{});
|
||||
auto timeout{async ? 0ULL : std::numeric_limits<u64>::max()}; // We cannot block for a buffer to be retrieved in async mode
|
||||
auto nextImage{vkSwapchain->acquireNextImage(timeout, {}, *fence)};
|
||||
if (nextImage.first == vk::Result::eTimeout) {
|
||||
return AndroidStatus::WouldBlock;
|
||||
} else if (nextImage.first == vk::Result::eErrorSurfaceLostKHR || nextImage.first == vk::Result::eSuboptimalKHR) {
|
||||
surfaceCondition.wait(lock, [this]() { return vkSurface.has_value(); });
|
||||
return GetFreeTexture(async, slot);
|
||||
}
|
||||
|
||||
gpu.vkDevice.waitForFences(*fence, true, std::numeric_limits<u64>::max());
|
||||
|
||||
slot = nextImage.second;
|
||||
return AndroidStatus::Ok;
|
||||
}
|
||||
return nextImage.second;
|
||||
return AndroidStatus::Busy;
|
||||
}
|
||||
|
||||
void PresentationEngine::Present(const std::shared_ptr<Texture> &texture) {
|
||||
void PresentationEngine::Present(i32 slot) {
|
||||
std::unique_lock lock(mutex);
|
||||
surfaceCondition.wait(lock, [this]() { return vkSurface.has_value(); });
|
||||
|
||||
if (--swapchain.dequeuedCount < 0) [[unlikely]] {
|
||||
throw exception("Swapchain has been presented more times than images from it have been acquired: {} (Image Count: {})", swapchain.dequeuedCount, swapchain.imageCount);
|
||||
}
|
||||
|
||||
vsyncEvent->Signal();
|
||||
|
||||
if (frameTimestamp) {
|
||||
@ -119,4 +163,12 @@ namespace skyline::gpu {
|
||||
frameTimestamp = util::GetTimeNs();
|
||||
}
|
||||
}
|
||||
|
||||
service::hosbinder::NativeWindowTransform PresentationEngine::GetTransformHint() {
|
||||
std::unique_lock lock(mutex);
|
||||
surfaceCondition.wait(lock, [this]() { return vkSurface.has_value(); });
|
||||
if (!transformHint)
|
||||
transformHint = GetAndroidTransform(gpu.vkPhysicalDevice.getSurfaceCapabilitiesKHR(**vkSurface).currentTransform);
|
||||
return *transformHint;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include <common/trace.h>
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include <services/hosbinder/native_window.h>
|
||||
#include <services/hosbinder/android_types.h>
|
||||
#include "texture.h"
|
||||
|
||||
struct ANativeWindow;
|
||||
@ -18,13 +20,15 @@ namespace skyline::gpu {
|
||||
const DeviceState &state;
|
||||
const GPU &gpu;
|
||||
std::mutex mutex; //!< Synchronizes access to the surface objects
|
||||
std::condition_variable surfaceCondition; //!< Allows us to efficiently wait for the window object to be set
|
||||
std::condition_variable surfaceCondition; //!< Allows us to efficiently wait for Vulkan surface to be initialized
|
||||
jobject surface{}; //!< The Surface object backing the ANativeWindow
|
||||
|
||||
std::optional<vk::raii::SurfaceKHR> vkSurface; //!< The Vulkan Surface object that is backed by ANativeWindow
|
||||
std::optional<service::hosbinder::NativeWindowTransform> transformHint; //!< The optimal transform for the application to render as
|
||||
std::optional<vk::raii::SwapchainKHR> vkSwapchain; //!< The Vulkan swapchain and the properties associated with it
|
||||
struct SwapchainContext {
|
||||
u32 imageCount{};
|
||||
u16 imageCount{};
|
||||
i32 dequeuedCount{};
|
||||
vk::Format imageFormat{};
|
||||
vk::Extent2D imageExtent{};
|
||||
} swapchain; //!< The properties of the currently created swapchain
|
||||
@ -32,7 +36,7 @@ namespace skyline::gpu {
|
||||
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown
|
||||
perfetto::Track presentationTrack; //!< Perfetto track used for presentation events
|
||||
|
||||
void UpdateSwapchain(u32 imageCount, vk::Format imageFormat, vk::Extent2D imageExtent);
|
||||
void UpdateSwapchain(u16 imageCount, vk::Format imageFormat, vk::Extent2D imageExtent);
|
||||
|
||||
public:
|
||||
texture::Dimensions resolution{};
|
||||
@ -55,13 +59,19 @@ namespace skyline::gpu {
|
||||
std::shared_ptr<Texture> CreatePresentationTexture(const std::shared_ptr<GuestTexture> &texture, u32 slot);
|
||||
|
||||
/**
|
||||
* @return The slot of the texture that's available to write into
|
||||
* @param async If to return immediately when a texture is not available
|
||||
* @param slot The slot the freed texture is in is written into this, it is untouched if there's an error
|
||||
*/
|
||||
u32 GetFreeTexture();
|
||||
service::hosbinder::AndroidStatus GetFreeTexture(bool async, i32& slot);
|
||||
|
||||
/**
|
||||
* @brief Send the supplied texture to the presentation queue to be displayed
|
||||
* @brief Send a texture from a slot to the presentation queue to be displayed
|
||||
*/
|
||||
void Present(const std::shared_ptr<Texture> &texture);
|
||||
void Present(i32 slot);
|
||||
|
||||
/**
|
||||
* @return A transform that the application should render with to elide costly transforms later
|
||||
*/
|
||||
service::hosbinder::NativeWindowTransform GetTransformHint();
|
||||
};
|
||||
}
|
||||
|
@ -94,9 +94,9 @@ namespace skyline::gpu {
|
||||
* @note Refer to Chapter 20.1 of the Tegra X1 TRM for information
|
||||
*/
|
||||
enum class TileMode {
|
||||
Linear, //!< This is a purely linear texture
|
||||
Pitch, //!< This is a pitch-linear texture
|
||||
Block, //!< This is a 16Bx2 block-linear texture
|
||||
Linear, //!< All pixels are arranged linearly
|
||||
Pitch, //!< All pixels are arranged linearly but rows aligned to the pitch
|
||||
Block, //!< All pixels are arranged into blocks and swizzled in a Z-order curve to optimize for spacial locality
|
||||
};
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user