Presentation Engine Vulkan Surface + Swapchain Initialization

This commit adds in VkSurface/VkSwapchain initialization and recreation. It also adapts GraphicsBuffferProducer and Texture to fit in with those changes but it doesn't yet implement presenting those buffers nor uploading guest buffers onto the host.
This commit is contained in:
PixelyIon 2021-04-04 16:39:32 +05:30 committed by ◱ Mark
parent 9a09a4e815
commit 795472004e
7 changed files with 317 additions and 248 deletions

View File

@ -40,10 +40,15 @@ namespace skyline::gpu {
} }
#ifdef NDEBUG #ifdef NDEBUG
constexpr std::array<const char*, 0> requiredInstanceExtensions{}; constexpr std::array<const char*, 2> requiredInstanceExtensions{
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
};
#else #else
constexpr std::array<const char *, 1> 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_ANDROID_SURFACE_EXTENSION_NAME,
}; };
#endif #endif
@ -135,8 +140,7 @@ 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::Error) {
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));
@ -146,7 +150,7 @@ namespace skyline::gpu {
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->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); 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);
} }
return vk::raii::Device(physicalDevice, vk::DeviceCreateInfo{ return vk::raii::Device(physicalDevice, vk::DeviceCreateInfo{
@ -157,5 +161,5 @@ namespace skyline::gpu {
}); });
} }
GPU::GPU(const DeviceState &state) : vkInstance(CreateInstance(state, vkContext)), vkDebugReportCallback(CreateDebugReportCallback(state, vkInstance)), vkPhysicalDevice(CreatePhysicalDevice(state, vkInstance)), vkDevice(CreateDevice(state, vkPhysicalDevice, vkQueueFamilyIndex)), vkQueue(vkDevice, vkQueueFamilyIndex, 0), presentation(state) {} GPU::GPU(const DeviceState &state) : vkInstance(CreateInstance(state, vkContext)), vkDebugReportCallback(CreateDebugReportCallback(state, vkInstance)), vkPhysicalDevice(CreatePhysicalDevice(state, vkInstance)), vkDevice(CreateDevice(state, vkPhysicalDevice, vkQueueFamilyIndex)), vkQueue(vkDevice, vkQueueFamilyIndex, 0), presentation(state, *this) {}
} }

View File

@ -2,6 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include <gpu.h>
#include "jvm.h" #include "jvm.h"
#include "presentation_engine.h" #include "presentation_engine.h"
@ -9,22 +10,57 @@ extern skyline::i32 Fps;
extern skyline::i32 FrameTime; extern skyline::i32 FrameTime;
namespace skyline::gpu { 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()) { PresentationEngine::PresentationEngine(const DeviceState &state, const GPU &gpu) : state(state), gpu(gpu), 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()) {
auto desc{presentationTrack.Serialize()}; auto desc{presentationTrack.Serialize()};
desc.set_name("Presentation"); desc.set_name("Presentation");
perfetto::TrackEvent::SetTrackDescriptor(presentationTrack, desc); perfetto::TrackEvent::SetTrackDescriptor(presentationTrack, desc);
} }
PresentationEngine::~PresentationEngine() { PresentationEngine::~PresentationEngine() {
if (window)
ANativeWindow_release(window);
auto env{state.jvm->GetEnv()}; auto env{state.jvm->GetEnv()};
if (!env->IsSameObject(surface, nullptr)) if (!env->IsSameObject(surface, nullptr))
env->DeleteGlobalRef(surface); env->DeleteGlobalRef(surface);
} }
void PresentationEngine::UpdateSwapchain(u32 imageCount, vk::Format imageFormat, vk::Extent2D imageExtent) {
if (!imageCount)
return;
auto capabilities{gpu.vkPhysicalDevice.getSurfaceCapabilitiesKHR(**vkSurface)};
if (imageCount < capabilities.minImageCount || (capabilities.maxImageCount && imageCount > capabilities.maxImageCount))
throw exception("Cannot update swapchain to accomodate image count: {} ({}-{})", imageCount, capabilities.minImageCount, capabilities.maxImageCount);
if (capabilities.minImageExtent.height > imageExtent.height || capabilities.minImageExtent.width > imageExtent.width || capabilities.maxImageExtent.height < imageExtent.height || capabilities.maxImageExtent.width < imageExtent.width)
throw exception("Cannot update swapchain to accomodate image extent: {}x{} ({}x{}-{}x{})", imageExtent.width, imageExtent.height, capabilities.minImageExtent.width, capabilities.minImageExtent.height, capabilities.maxImageExtent.width, capabilities.maxImageExtent.height);
constexpr vk::ImageUsageFlags presentUsage{vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst};
if ((capabilities.supportedUsageFlags & presentUsage) != presentUsage)
throw exception("Swapchain doesn't support image usage '{}': {}", vk::to_string(presentUsage), vk::to_string(capabilities.supportedUsageFlags));
vkSwapchain = vk::raii::SwapchainKHR(gpu.vkDevice, vk::SwapchainCreateInfoKHR{
.surface = **vkSurface,
.minImageCount = imageCount,
.imageFormat = imageFormat,
.imageColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear,
.imageExtent = imageExtent,
.imageArrayLayers = 1,
.imageUsage = presentUsage,
.imageSharingMode = vk::SharingMode::eExclusive,
.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eInherit,
.presentMode = vk::PresentModeKHR::eFifo,
.clipped = false,
.oldSwapchain = vkSwapchain ? **vkSwapchain : vk::SwapchainKHR{},
});
swapchain = SwapchainContext{
.imageCount = imageCount,
.imageFormat = imageFormat,
.imageExtent = imageExtent,
};
}
void PresentationEngine::UpdateSurface(jobject newSurface) { void PresentationEngine::UpdateSurface(jobject newSurface) {
std::lock_guard lock(windowMutex); std::lock_guard lock(mutex);
auto env{state.jvm->GetEnv()}; auto env{state.jvm->GetEnv()};
if (!env->IsSameObject(surface, nullptr)) { if (!env->IsSameObject(surface, nullptr)) {
env->DeleteGlobalRef(surface); env->DeleteGlobalRef(surface);
@ -32,51 +68,47 @@ namespace skyline::gpu {
} }
if (!env->IsSameObject(newSurface, nullptr)) if (!env->IsSameObject(newSurface, nullptr))
surface = env->NewGlobalRef(newSurface); surface = env->NewGlobalRef(newSurface);
if (surface) { if (surface) {
window = ANativeWindow_fromSurface(env, surface); vkSurface.emplace(gpu.vkInstance, vk::AndroidSurfaceCreateInfoKHR{
ANativeWindow_acquire(window); .window = ANativeWindow_fromSurface(env, surface),
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window)); });
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window)); if (!gpu.vkPhysicalDevice.getSurfaceSupportKHR(gpu.vkQueueFamilyIndex, **vkSurface))
format = ANativeWindow_getFormat(window); throw exception("Vulkan Queue doesn't support presentation with surface");
windowConditional.notify_all();
UpdateSwapchain(swapchain.imageCount, swapchain.imageFormat, swapchain.imageExtent);
surfaceCondition.notify_all();
} else { } else {
window = nullptr; vkSurface.reset();
} }
} }
std::shared_ptr<Texture> PresentationEngine::CreatePresentationTexture(const std::shared_ptr<GuestTexture> &texture, u32 slot) {
std::unique_lock lock(mutex);
if (swapchain.imageCount <= slot)
UpdateSwapchain(slot + 1, texture->format.vkFormat, texture->dimensions);
return texture->InitializeTexture(vk::raii::Image(gpu.vkDevice, vkSwapchain->getImages().at(slot)));
}
u32 PresentationEngine::GetFreeTexture() {
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();
}
return nextImage.second;
}
void PresentationEngine::Present(const std::shared_ptr<Texture> &texture) { void PresentationEngine::Present(const std::shared_ptr<Texture> &texture) {
std::unique_lock lock(windowMutex); std::unique_lock lock(mutex);
if (!window) surfaceCondition.wait(lock, [this]() { return vkSurface.has_value(); });
windowConditional.wait(lock, [this]() { return window; });
auto textureFormat{[&texture]() {
switch (texture->format.vkFormat) {
case vk::Format::eR8G8B8A8Unorm:
return WINDOW_FORMAT_RGBA_8888;
case vk::Format::eR5G6B5UnormPack16:
return WINDOW_FORMAT_RGB_565;
default:
throw exception("Cannot find corresponding Android surface format");
}
}()};
if (resolution != texture->dimensions || textureFormat != format) {
ANativeWindow_setBuffersGeometry(window, texture->dimensions.width, texture->dimensions.height, textureFormat);
resolution = texture->dimensions;
format = textureFormat;
}
ANativeWindow_Buffer buffer;
ARect rect;
ANativeWindow_lock(window, &buffer, &rect);
std::memcpy(buffer.bits, texture->backing.data(), texture->backing.size());
ANativeWindow_unlockAndPost(window);
vsyncEvent->Signal(); vsyncEvent->Signal();
if (frameTimestamp) { if (frameTimestamp) {
auto now{util::GetTimeNs()}; auto now{util::GetTimeNs()};
FrameTime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals FrameTime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp)); Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));

View File

@ -10,29 +10,58 @@
struct ANativeWindow; struct ANativeWindow;
namespace skyline::gpu { namespace skyline::gpu {
/**
* @brief All host presentation is handled by this, it manages the host surface and swapchain alongside dynamically recreating it when required
*/
class PresentationEngine { class PresentationEngine {
private: private:
const DeviceState &state; const DeviceState &state;
std::mutex windowMutex; const GPU &gpu;
std::condition_variable windowConditional; 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
jobject surface{}; //!< The Surface object backing the ANativeWindow 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<vk::raii::SwapchainKHR> vkSwapchain; //!< The Vulkan swapchain and the properties associated with it
struct SwapchainContext {
u32 imageCount{};
vk::Format imageFormat{};
vk::Extent2D imageExtent{};
} swapchain; //!< The properties of the currently created swapchain
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown u64 frameTimestamp{}; //!< The timestamp of the last frame being shown
perfetto::Track presentationTrack; //!< Perfetto track used for presentation events perfetto::Track presentationTrack; //!< Perfetto track used for presentation events
void UpdateSwapchain(u32 imageCount, vk::Format imageFormat, vk::Extent2D imageExtent);
public: public:
texture::Dimensions resolution{}; texture::Dimensions resolution{};
i32 format{}; i32 format{};
std::shared_ptr<kernel::type::KEvent> vsyncEvent; //!< Signalled every time a frame is drawn std::shared_ptr<kernel::type::KEvent> vsyncEvent; //!< Signalled every time a frame is drawn
std::shared_ptr<kernel::type::KEvent> bufferEvent; //!< Signalled every time a buffer is freed std::shared_ptr<kernel::type::KEvent> bufferEvent; //!< Signalled every time a buffer is freed
PresentationEngine(const DeviceState &state); PresentationEngine(const DeviceState &state, const GPU& gpu);
~PresentationEngine(); ~PresentationEngine();
/**
* @brief Replaces the underlying Android surface with a new one, it handles resetting the swapchain and such
*/
void UpdateSurface(jobject newSurface); void UpdateSurface(jobject newSurface);
void Present(const std::shared_ptr<Texture> &texture); /**
* @brief Creates a Texture object from a GuestTexture as a part of the Vulkan swapchain
*/
std::shared_ptr<Texture> CreatePresentationTexture(const std::shared_ptr<GuestTexture> &texture, u32 slot);
ANativeWindow *window{}; /**
* @return The slot of the texture that's available to write into
*/
u32 GetFreeTexture();
/**
* @brief Send the supplied texture to the presentation queue to be displayed
*/
void Present(const std::shared_ptr<Texture> &texture);
}; };
} }

View File

@ -1,24 +1,23 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <gpu.h>
#include <common/trace.h> #include <common/trace.h>
#include <android/native_window.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <unistd.h>
#include "texture.h" #include "texture.h"
namespace skyline::gpu { namespace skyline::gpu {
GuestTexture::GuestTexture(const DeviceState &state, u8 *pointer, texture::Dimensions dimensions, texture::Format format, texture::TileMode tiling, texture::TileConfig layout) : state(state), pointer(pointer), dimensions(dimensions), format(format), tileMode(tiling), tileConfig(layout) {} GuestTexture::GuestTexture(const DeviceState &state, u8 *pointer, texture::Dimensions dimensions, texture::Format format, texture::TileMode tiling, texture::TileConfig layout) : state(state), pointer(pointer), dimensions(dimensions), format(format), tileMode(tiling), tileConfig(layout) {}
std::shared_ptr<Texture> GuestTexture::InitializeTexture(std::optional<texture::Format> pFormat, std::optional<texture::Dimensions> pDimensions, texture::Swizzle swizzle) { std::shared_ptr<Texture> GuestTexture::InitializeTexture(vk::raii::Image &&backing, std::optional<texture::Format> pFormat, std::optional<texture::Dimensions> pDimensions, texture::Swizzle swizzle) {
if (!host.expired()) if (!host.expired())
throw exception("Trying to create multiple Texture objects from a single GuestTexture"); throw exception("Trying to create multiple Texture objects from a single GuestTexture");
auto sharedHost{std::make_shared<Texture>(state, shared_from_this(), pDimensions ? *pDimensions : dimensions, pFormat ? *pFormat : format, swizzle)}; auto sharedHost{std::make_shared<Texture>(std::move(backing), shared_from_this(), pDimensions ? *pDimensions : dimensions, pFormat ? *pFormat : format, swizzle)};
host = sharedHost; host = sharedHost;
return sharedHost; return sharedHost;
} }
Texture::Texture(const DeviceState &state, std::shared_ptr<GuestTexture> guest, texture::Dimensions dimensions, texture::Format format, texture::Swizzle swizzle) : state(state), guest(std::move(guest)), dimensions(dimensions), format(format), swizzle(swizzle) { Texture::Texture(vk::raii::Image&& backing, std::shared_ptr<GuestTexture> guest, texture::Dimensions dimensions, texture::Format format, texture::Swizzle swizzle) : backing(std::move(backing)), guest(std::move(guest)), dimensions(dimensions), format(format), swizzle(swizzle) {
SynchronizeHost(); SynchronizeHost();
} }
@ -26,8 +25,8 @@ namespace skyline::gpu {
TRACE_EVENT("gpu", "Texture::SynchronizeHost"); TRACE_EVENT("gpu", "Texture::SynchronizeHost");
auto pointer{guest->pointer}; auto pointer{guest->pointer};
auto size{format.GetSize(dimensions)}; auto size{format.GetSize(dimensions)};
backing.resize(size); u8* output{nullptr};
auto output{backing.data()}; return;
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

View File

@ -6,11 +6,7 @@
#include <common.h> #include <common.h>
#include <vulkan/vulkan_raii.hpp> #include <vulkan/vulkan_raii.hpp>
namespace skyline { namespace skyline::gpu {
namespace service::hosbinder {
class GraphicBufferProducer;
}
namespace gpu {
namespace texture { namespace texture {
struct Dimensions { struct Dimensions {
u32 width; u32 width;
@ -24,6 +20,30 @@ namespace skyline {
constexpr Dimensions(u32 width, u32 height, u32 depth) : width(width), height(height), depth(depth) {} constexpr Dimensions(u32 width, u32 height, u32 depth) : width(width), height(height), depth(depth) {}
auto operator<=>(const Dimensions &) const = default; auto operator<=>(const Dimensions &) const = default;
vk::ImageType GetType() {
if (depth)
return vk::ImageType::e3D;
else if (width)
return vk::ImageType::e2D;
else
return vk::ImageType::e1D;
}
operator vk::Extent2D() {
return vk::Extent2D{
.width = width,
.height = height,
};
}
operator vk::Extent3D() {
return vk::Extent3D{
.width = width,
.height = height,
.depth = depth,
};
}
}; };
/** /**
@ -109,6 +129,7 @@ namespace skyline {
} }
class Texture; class Texture;
class PresentationEngine; //!< A forward declaration of PresentationEngine as we require it to be able to create a Texture object
/** /**
* @brief A texture present in guest memory, it can be used to create a corresponding Texture object for usage on the host * @brief A texture present in guest memory, it can be used to create a corresponding Texture object for usage on the host
@ -133,33 +154,30 @@ namespace skyline {
/** /**
* @brief Creates a corresponding host texture object for this guest texture * @brief Creates a corresponding host texture object for this guest texture
* @param backing The Vulkan Image that is used as the backing on the host
* @param format The format of the host texture (Defaults to the format of the guest texture) * @param format The format of the host texture (Defaults to the format of the guest texture)
* @param dimensions The dimensions of the host texture (Defaults to the dimensions of the host texture) * @param dimensions The dimensions of the host texture (Defaults to the dimensions of the host texture)
* @param swizzle The channel swizzle of the host texture (Defaults to no channel swizzling) * @param swizzle The channel swizzle of the host texture (Defaults to no channel swizzling)
* @return A shared pointer to the host texture object * @return A shared pointer to the host texture object
* @note There can only be one host texture for a corresponding guest texture * @note There can only be one host texture for a corresponding guest texture
*/ */
std::shared_ptr<Texture> InitializeTexture(std::optional<texture::Format> format = std::nullopt, std::optional<texture::Dimensions> dimensions = std::nullopt, texture::Swizzle swizzle = {}); std::shared_ptr<Texture> InitializeTexture(vk::raii::Image &&backing, std::optional<texture::Format> format = std::nullopt, std::optional<texture::Dimensions> dimensions = std::nullopt, texture::Swizzle swizzle = {});
}; };
/** /**
* @brief A texture which is backed by host constructs while being synchronized with the underlying guest texture * @brief A texture which is backed by host constructs while being synchronized with the underlying guest texture
*/ */
class Texture { class Texture {
private:
const DeviceState &state;
public: public:
std::vector<u8> backing; //!< The object that holds a host copy of the guest texture (Will be replaced with a vk::Image) vk::raii::Image backing; //!< The object that holds a host copy of the guest texture
std::shared_ptr<GuestTexture> guest; //!< The guest texture from which this was created, it's required for syncing std::shared_ptr<GuestTexture> guest; //!< The guest texture from which this was created, it's required for syncing
texture::Dimensions dimensions; texture::Dimensions dimensions;
texture::Format format; texture::Format format;
texture::Swizzle swizzle; texture::Swizzle swizzle;
public: public:
Texture(const DeviceState &state, std::shared_ptr<GuestTexture> guest, texture::Dimensions dimensions, texture::Format format, texture::Swizzle swizzle); Texture(vk::raii::Image &&backing, std::shared_ptr<GuestTexture> guest, texture::Dimensions dimensions, texture::Format format, texture::Swizzle swizzle);
public:
/** /**
* @brief Convert this texture to the specified tiling mode * @brief Convert this texture to the specified tiling mode
* @param tileMode The tiling mode to convert it to * @param tileMode The tiling mode to convert it to
@ -193,4 +211,3 @@ namespace skyline {
void SynchronizeGuest(); void SynchronizeGuest();
}; };
} }
}

View File

@ -4,6 +4,7 @@
#include <android/hardware_buffer.h> #include <android/hardware_buffer.h>
#include <gpu.h> #include <gpu.h>
#include <gpu/format.h> #include <gpu/format.h>
#include <common/settings.h>
#include <services/nvdrv/driver.h> #include <services/nvdrv/driver.h>
#include <services/nvdrv/devices/nvmap.h> #include <services/nvdrv/devices/nvmap.h>
#include <services/common/fence.h> #include <services/common/fence.h>
@ -22,7 +23,7 @@ namespace skyline::service::hosbinder {
out.Push<u32>(0); out.Push<u32>(0);
out.Push(queue.at(slot)->gbpBuffer); out.Push(queue.at(slot)->gbpBuffer);
state.logger->Debug("Slot: {}", slot, sizeof(GbpBuffer)); state.logger->Debug("#{}", slot, sizeof(GbpBuffer));
} }
void GraphicBufferProducer::DequeueBuffer(Parcel &in, Parcel &out) { void GraphicBufferProducer::DequeueBuffer(Parcel &in, Parcel &out) {
@ -32,21 +33,16 @@ namespace skyline::service::hosbinder {
u32 format{in.Pop<u32>()}; u32 format{in.Pop<u32>()};
u32 usage{in.Pop<u32>()}; u32 usage{in.Pop<u32>()};
std::optional<u32> slot{std::nullopt}; u32 slot{state.gpu->presentation.GetFreeTexture()};
while (!slot) { auto &buffer{queue.at(slot)};
for (auto &buffer : queue) { if ((format != 0 && buffer->gbpBuffer.format != format) || buffer->gbpBuffer.width != width || buffer->gbpBuffer.height != height || (buffer->gbpBuffer.usage & usage) != usage) {
if (buffer.second->status == BufferStatus::Free && (format == 0 || buffer.second->gbpBuffer.format == format) && buffer.second->gbpBuffer.width == width && buffer.second->gbpBuffer.height == height && (buffer.second->gbpBuffer.usage & usage) == usage) { throw exception("Buffer which has been dequeued isn't compatible with the supplied parameters: {}x{}={}x{} F{}={} U{}={}", width, height, buffer->gbpBuffer.width, buffer->gbpBuffer.height, format, buffer->gbpBuffer.format, usage, buffer->gbpBuffer.usage);
slot = buffer.first;
buffer.second->status = BufferStatus::Dequeued;
break;
}
}
} }
out.Push(*slot); out.Push(slot);
out.Push(std::array<u32, 13>{1, 0x24}); // Unknown out.Push(std::array<u32, 13>{1, 0x24}); // Unknown
state.logger->Debug("Width: {}, Height: {}, Format: {}, Usage: {}, Slot: {}", width, height, format, usage, *slot); state.logger->Debug("#{} - Dimensions: {}x{}, Format: {}, Usage: 0x{:X}", slot, width, height, format, usage);
} }
void GraphicBufferProducer::QueueBuffer(Parcel &in, Parcel &out) { void GraphicBufferProducer::QueueBuffer(Parcel &in, Parcel &out) {
@ -61,14 +57,11 @@ namespace skyline::service::hosbinder {
u64 _unk0_; u64 _unk0_;
u32 swapInterval; u32 swapInterval;
std::array<nvdrv::Fence, 4> fence; std::array<nvdrv::Fence, 4> fence;
} &data = in.Pop<Data>(); } &data{in.Pop<Data>()};
auto buffer{queue.at(data.slot)}; auto buffer{queue.at(data.slot)};
buffer->status = BufferStatus::Queued;
buffer->texture->SynchronizeHost(); buffer->texture->SynchronizeHost();
state.gpu->presentation.Present(buffer->texture); state.gpu->presentation.Present(buffer->texture);
queue.at(data.slot)->status = BufferStatus::Free;
state.gpu->presentation.bufferEvent->Signal(); state.gpu->presentation.bufferEvent->Signal();
struct { struct {
@ -81,16 +74,17 @@ namespace skyline::service::hosbinder {
}; };
out.Push(output); out.Push(output);
state.logger->Debug("Timestamp: {}, Auto Timestamp: {}, Crop: [T: {}, B: {}, L: {}, R: {}], Scaling Mode: {}, Transform: {}, Sticky Transform: {}, Swap Interval: {}, Slot: {}", data.timestamp, data.autoTimestamp, data.crop.top, data.crop.bottom, data.crop.left, data.crop.right, data.scalingMode, data.transform, data.stickyTransform, data.swapInterval, data.slot); state.logger->Debug("#{} - {}Timestamp: {}, Crop: ({}-{})x({}-{}), Scale Mode: {}, Transform: {} [Sticky: {}], Swap Interval: {}", data.slot, data.autoTimestamp ? "Auto " : "", data.timestamp, data.crop.top, data.crop.bottom, data.crop.left, data.crop.right, data.scalingMode, data.transform, data.stickyTransform, data.swapInterval);
} }
void GraphicBufferProducer::CancelBuffer(Parcel &in) { void GraphicBufferProducer::CancelBuffer(Parcel &in) {
u32 slot{in.Pop<u32>()}; u32 slot{in.Pop<u32>()};
//auto fences{in.Pop<std::array<nvdrv::Fence, 4>>()}; //auto fences{in.Pop<std::array<nvdrv::Fence, 4>>()};
queue.at(slot)->status = BufferStatus::Free; // We cannot force the host GPU API to give us back a particular buffer due to how the swapchain works
// As a result of this, we just assume it'll be presented and dequeued at some point and not cancel the buffer here
state.logger->Debug("Slot: {}", slot); state.logger->Debug("#{}", slot);
} }
void GraphicBufferProducer::Connect(Parcel &out) { void GraphicBufferProducer::Connect(Parcel &out) {
@ -102,7 +96,7 @@ namespace skyline::service::hosbinder {
u32 status{}; //!< The status of the buffer queue u32 status{}; //!< The status of the buffer queue
} data{}; } data{};
out.Push(data); out.Push(data);
state.logger->Debug("Connect"); state.logger->Debug("{}x{}", data.width, data.height);
} }
void GraphicBufferProducer::SetPreallocatedBuffer(Parcel &in) { void GraphicBufferProducer::SetPreallocatedBuffer(Parcel &in) {
@ -149,10 +143,11 @@ namespace skyline::service::hosbinder {
auto texture{std::make_shared<gpu::GuestTexture>(state, nvBuffer->ptr + gbpBuffer.offset, gpu::texture::Dimensions(gbpBuffer.width, gbpBuffer.height), format, gpu::texture::TileMode::Block, gpu::texture::TileConfig{.surfaceWidth = static_cast<u16>(gbpBuffer.stride), .blockHeight = static_cast<u8>(1U << gbpBuffer.blockHeightLog2), .blockDepth = 1})}; auto texture{std::make_shared<gpu::GuestTexture>(state, nvBuffer->ptr + gbpBuffer.offset, gpu::texture::Dimensions(gbpBuffer.width, gbpBuffer.height), format, gpu::texture::TileMode::Block, gpu::texture::TileConfig{.surfaceWidth = static_cast<u16>(gbpBuffer.stride), .blockHeight = static_cast<u8>(1U << gbpBuffer.blockHeightLog2), .blockDepth = 1})};
queue[data.slot] = std::make_shared<Buffer>(gbpBuffer, texture->InitializeTexture()); queue.resize(std::max(data.slot + 1, static_cast<u32>(queue.size())));
queue[data.slot] = std::make_shared<Buffer>(gbpBuffer, state.gpu->presentation.CreatePresentationTexture(texture, data.slot));
state.gpu->presentation.bufferEvent->Signal(); state.gpu->presentation.bufferEvent->Signal();
state.logger->Debug("Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", data.slot, gbpBuffer.magic, gbpBuffer.width, gbpBuffer.height, gbpBuffer.stride, gbpBuffer.format, gbpBuffer.usage, gbpBuffer.index, gbpBuffer.nvmapId, gbpBuffer.nvmapHandle, gbpBuffer.offset, (1U << gbpBuffer.blockHeightLog2), gbpBuffer.size); state.logger->Debug("#{} - Dimensions: {}x{} [Stride: {}], Format: {}, Block Height: {}, Usage: 0x{:X}, Index: {}, NvMap: {}: {}, Buffer Start/End: 0x{:X} -> 0x{:X}", data.slot, gbpBuffer.width, gbpBuffer.stride, gbpBuffer.height, gbpBuffer.format, (1U << gbpBuffer.blockHeightLog2), gbpBuffer.usage, gbpBuffer.index, gbpBuffer.nvmapHandle ? "Handle" : "ID", gbpBuffer.nvmapHandle ? gbpBuffer.nvmapHandle : gbpBuffer.nvmapId, gbpBuffer.offset, gbpBuffer.offset + gbpBuffer.size);
} }
void GraphicBufferProducer::OnTransact(TransactionCode code, Parcel &in, Parcel &out) { void GraphicBufferProducer::OnTransact(TransactionCode code, Parcel &in, Parcel &out) {

View File

@ -35,18 +35,11 @@ namespace skyline::service::hosbinder {
u32 _pad5_[58]; u32 _pad5_[58];
}; };
enum class BufferStatus {
Free, //!< The buffer is free
Dequeued, //!< The buffer has been dequeued from the display
Queued, //!< The buffer is queued to be displayed
};
/** /**
* @brief A wrapper over GbpBuffer which contains additional state that we track for a buffer * @brief A wrapper over GbpBuffer which contains additional state that we track for a buffer
*/ */
class Buffer { class Buffer {
public: public:
BufferStatus status{BufferStatus::Free};
std::shared_ptr<gpu::Texture> texture; std::shared_ptr<gpu::Texture> texture;
GbpBuffer gbpBuffer; GbpBuffer gbpBuffer;
@ -78,7 +71,7 @@ namespace skyline::service::hosbinder {
class GraphicBufferProducer { class GraphicBufferProducer {
private: private:
const DeviceState &state; const DeviceState &state;
std::unordered_map<u32, std::shared_ptr<Buffer>> queue; //!< A vector of shared pointers to all the queued buffers std::vector<std::shared_ptr<Buffer>> queue; //!< A vector of shared pointers to all the queued buffers
/** /**
* @brief Request for the GbpBuffer of a buffer * @brief Request for the GbpBuffer of a buffer