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
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
constexpr std::array<const char *, 1> requiredInstanceExtensions{
VK_EXT_DEBUG_REPORT_EXTENSION_NAME
constexpr std::array<const char *, 3> requiredInstanceExtensions{
VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
};
#endif
@ -98,7 +103,7 @@ namespace skyline::gpu {
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, typeof(vk::DeviceQueueCreateInfo::queueCount)& vkQueueFamilyIndex) {
vk::raii::Device GPU::CreateDevice(const DeviceState &state, const vk::raii::PhysicalDevice &physicalDevice, typeof(vk::DeviceQueueCreateInfo::queueCount) &vkQueueFamilyIndex) {
auto properties{physicalDevice.getProperties()}; // We should check for required properties here, if/when we have them
// auto features{physicalDevice.getFeatures()}; // Same as above
@ -135,8 +140,7 @@ namespace skyline::gpu {
throw exception("Cannot find a queue family with both eGraphics and eCompute bits set");
}()};
if (state.logger->configLevel >= Logger::LogLevel::Error) {
if (state.logger->configLevel >= Logger::LogLevel::Debug) {
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));
@ -146,7 +150,7 @@ namespace skyline::gpu {
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 ? " <--" : "");
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{
@ -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/)
#include <android/native_window_jni.h>
#include <gpu.h>
#include "jvm.h"
#include "presentation_engine.h"
@ -9,22 +10,57 @@ extern skyline::i32 Fps;
extern skyline::i32 FrameTime;
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()};
desc.set_name("Presentation");
perfetto::TrackEvent::SetTrackDescriptor(presentationTrack, desc);
}
PresentationEngine::~PresentationEngine() {
if (window)
ANativeWindow_release(window);
auto env{state.jvm->GetEnv()};
if (!env->IsSameObject(surface, nullptr))
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) {
std::lock_guard lock(windowMutex);
std::lock_guard lock(mutex);
auto env{state.jvm->GetEnv()};
if (!env->IsSameObject(surface, nullptr)) {
env->DeleteGlobalRef(surface);
@ -32,51 +68,47 @@ namespace skyline::gpu {
}
if (!env->IsSameObject(newSurface, nullptr))
surface = env->NewGlobalRef(newSurface);
if (surface) {
window = ANativeWindow_fromSurface(env, surface);
ANativeWindow_acquire(window);
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
format = ANativeWindow_getFormat(window);
windowConditional.notify_all();
vkSurface.emplace(gpu.vkInstance, vk::AndroidSurfaceCreateInfoKHR{
.window = ANativeWindow_fromSurface(env, surface),
});
if (!gpu.vkPhysicalDevice.getSurfaceSupportKHR(gpu.vkQueueFamilyIndex, **vkSurface))
throw exception("Vulkan Queue doesn't support presentation with surface");
UpdateSwapchain(swapchain.imageCount, swapchain.imageFormat, swapchain.imageExtent);
surfaceCondition.notify_all();
} 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) {
std::unique_lock lock(windowMutex);
if (!window)
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);
std::unique_lock lock(mutex);
surfaceCondition.wait(lock, [this]() { return vkSurface.has_value(); });
vsyncEvent->Signal();
if (frameTimestamp) {
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
Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));

View File

@ -10,29 +10,58 @@
struct ANativeWindow;
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 {
private:
const DeviceState &state;
std::mutex windowMutex;
std::condition_variable windowConditional;
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
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
perfetto::Track presentationTrack; //!< Perfetto track used for presentation events
void UpdateSwapchain(u32 imageCount, vk::Format imageFormat, vk::Extent2D imageExtent);
public:
texture::Dimensions resolution{};
i32 format{};
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
PresentationEngine(const DeviceState &state);
PresentationEngine(const DeviceState &state, const GPU& gpu);
~PresentationEngine();
/**
* @brief Replaces the underlying Android surface with a new one, it handles resetting the swapchain and such
*/
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
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <gpu.h>
#include <common/trace.h>
#include <android/native_window.h>
#include <kernel/types/KProcess.h>
#include <unistd.h>
#include "texture.h"
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) {}
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())
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;
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();
}
@ -26,8 +25,8 @@ namespace skyline::gpu {
TRACE_EVENT("gpu", "Texture::SynchronizeHost");
auto pointer{guest->pointer};
auto size{format.GetSize(dimensions)};
backing.resize(size);
auto output{backing.data()};
u8* output{nullptr};
return;
if (guest->tileMode == texture::TileMode::Block) {
// Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32

View File

@ -6,11 +6,7 @@
#include <common.h>
#include <vulkan/vulkan_raii.hpp>
namespace skyline {
namespace service::hosbinder {
class GraphicBufferProducer;
}
namespace gpu {
namespace skyline::gpu {
namespace texture {
struct Dimensions {
u32 width;
@ -24,6 +20,30 @@ namespace skyline {
constexpr Dimensions(u32 width, u32 height, u32 depth) : width(width), height(height), depth(depth) {}
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 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
@ -133,33 +154,30 @@ namespace skyline {
/**
* @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 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)
* @return A shared pointer to the host texture object
* @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
*/
class Texture {
private:
const DeviceState &state;
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
texture::Dimensions dimensions;
texture::Format format;
texture::Swizzle swizzle;
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
* @param tileMode The tiling mode to convert it to
@ -192,5 +210,4 @@ namespace skyline {
*/
void SynchronizeGuest();
};
}
}

View File

@ -4,6 +4,7 @@
#include <android/hardware_buffer.h>
#include <gpu.h>
#include <gpu/format.h>
#include <common/settings.h>
#include <services/nvdrv/driver.h>
#include <services/nvdrv/devices/nvmap.h>
#include <services/common/fence.h>
@ -22,7 +23,7 @@ namespace skyline::service::hosbinder {
out.Push<u32>(0);
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) {
@ -32,21 +33,16 @@ namespace skyline::service::hosbinder {
u32 format{in.Pop<u32>()};
u32 usage{in.Pop<u32>()};
std::optional<u32> slot{std::nullopt};
while (!slot) {
for (auto &buffer : queue) {
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) {
slot = buffer.first;
buffer.second->status = BufferStatus::Dequeued;
break;
}
}
u32 slot{state.gpu->presentation.GetFreeTexture()};
auto &buffer{queue.at(slot)};
if ((format != 0 && buffer->gbpBuffer.format != format) || buffer->gbpBuffer.width != width || buffer->gbpBuffer.height != height || (buffer->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);
}
out.Push(*slot);
out.Push(slot);
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) {
@ -61,14 +57,11 @@ namespace skyline::service::hosbinder {
u64 _unk0_;
u32 swapInterval;
std::array<nvdrv::Fence, 4> fence;
} &data = in.Pop<Data>();
} &data{in.Pop<Data>()};
auto buffer{queue.at(data.slot)};
buffer->status = BufferStatus::Queued;
buffer->texture->SynchronizeHost();
state.gpu->presentation.Present(buffer->texture);
queue.at(data.slot)->status = BufferStatus::Free;
state.gpu->presentation.bufferEvent->Signal();
struct {
@ -81,16 +74,17 @@ namespace skyline::service::hosbinder {
};
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) {
u32 slot{in.Pop<u32>()};
//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) {
@ -102,7 +96,7 @@ namespace skyline::service::hosbinder {
u32 status{}; //!< The status of the buffer queue
} data{};
out.Push(data);
state.logger->Debug("Connect");
state.logger->Debug("{}x{}", data.width, data.height);
}
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})};
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.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) {

View File

@ -35,18 +35,11 @@ namespace skyline::service::hosbinder {
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
*/
class Buffer {
public:
BufferStatus status{BufferStatus::Free};
std::shared_ptr<gpu::Texture> texture;
GbpBuffer gbpBuffer;
@ -78,7 +71,7 @@ namespace skyline::service::hosbinder {
class GraphicBufferProducer {
private:
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