Fix GraphicBufferProducer Format Bug

The format provided in `GraphicBuffer` can be misleading and is supplied as `None` by the Deko3D Swapchain, it instead supplies the real format in the `NvGraphicHandle` which we now utilize instead of the one in `GraphicBuffer`.
This commit is contained in:
PixelyIon 2021-07-23 08:08:47 +05:30
parent 18cf0b82d6
commit bc378ad135
2 changed files with 19 additions and 16 deletions

View File

@ -138,8 +138,9 @@ namespace skyline::service::hosbinder {
// All buffers must be preallocated on the client application and attached to an Android buffer using SetPreallocatedBuffer
return AndroidStatus::NoMemory;
}
auto &surface{buffer->graphicBuffer->graphicHandle.surfaces.front()};
if (buffer->graphicBuffer->format != format || surface.width != width || surface.height != height || (buffer->graphicBuffer->usage & usage) != usage) {
auto &handle{buffer->graphicBuffer->graphicHandle};
auto &surface{handle.surfaces.front()};
if (handle.format != format || surface.width != width || surface.height != height || (buffer->graphicBuffer->usage & usage) != usage) {
state.logger->Warn("Buffer which has been dequeued isn't compatible with the supplied parameters: Dimensions: {}x{}={}x{}, Format: {}={}, Usage: 0x{:X}=0x{:X}", width, height, surface.width, surface.height, ToString(format), ToString(buffer->graphicBuffer->format), usage, buffer->graphicBuffer->usage);
// Nintendo doesn't deallocate the slot which was picked in here and reallocate it as a compatible buffer
// This is related to the comment above, Nintendo only allocates buffers on the client side
@ -264,7 +265,7 @@ namespace skyline::service::hosbinder {
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 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(handle.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;
}
@ -305,8 +306,17 @@ namespace skyline::service::hosbinder {
if (!buffer.texture) [[unlikely]] {
// We lazily create a texture if one isn't present at queue time, this allows us to look up the texture in the texture cache
// If we deterministically know that the texture is written by the CPU then we can allocate a CPU-shared host texture for fast uploads
auto &handle{graphicBuffer.graphicHandle};
if (handle.magic != NvGraphicHandle::Magic)
throw exception("Unexpected NvGraphicHandle magic: {}", handle.surfaceCount);
else if (handle.surfaceCount < 1)
throw exception("At least one surface is required in a buffer: {}", handle.surfaceCount);
else if (handle.surfaceCount > 1)
throw exception("Multi-planar surfaces are not supported: {}", handle.surfaceCount);
gpu::texture::Format format;
switch (graphicBuffer.format) {
switch (handle.format) {
case AndroidPixelFormat::RGBA8888:
case AndroidPixelFormat::RGBX8888:
format = gpu::format::RGBA8888Unorm;
@ -317,17 +327,9 @@ namespace skyline::service::hosbinder {
break;
default:
throw exception("Unknown format in buffer: '{}' ({})", ToString(graphicBuffer.format), static_cast<u32>(graphicBuffer.format));
throw exception("Unknown format in buffer: '{}' ({})", ToString(handle.format), static_cast<u32>(handle.format));
}
auto &handle{graphicBuffer.graphicHandle};
if (handle.magic != NvGraphicHandle::Magic)
throw exception("Unexpected NvGraphicHandle magic: {}", handle.surfaceCount);
else if (handle.surfaceCount < 1)
throw exception("At least one surface is required in a buffer: {}", handle.surfaceCount);
else if (handle.surfaceCount > 1)
throw exception("Multi-planar surfaces are not supported: {}", handle.surfaceCount);
auto &surface{graphicBuffer.graphicHandle.surfaces.at(0)};
if (surface.scanFormat != NvDisplayScanFormat::Progressive)
throw exception("Non-Progressive surfaces are not supported: {}", ToString(surface.scanFormat));
@ -595,7 +597,7 @@ namespace skyline::service::hosbinder {
else if (surface.layout == NvSurfaceLayout::Tiled)
throw exception("Legacy 16Bx16 tiled surfaces are not supported");
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(handle.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);
} else {
state.logger->Debug("#{} - No GraphicBuffer", slot);
}

View File

@ -188,7 +188,7 @@ namespace skyline::service::hosbinder {
static_assert(sizeof(NvSurface) == 0x58);
/**
* @brief The integers of the native_handle used by Nvidia to marshall the surfaces in this buffer
* @brief The format of the native_handle integers used by Nvidia to marshall the surface metadata of the GraphicBuffer
*/
struct NvGraphicHandle {
constexpr static u32 Magic{0xDAFFCAFF};
@ -199,7 +199,7 @@ namespace skyline::service::hosbinder {
u32 ownerPid; //!< Same as the upper 32-bits of the ID in the GraphicBuffer (0x2F)
u32 type;
u32 usage; //!< The Gralloc usage flags, same as GraphicBuffer
u32 format; //!< The internal format of the buffer
AndroidPixelFormat format; //!< The internal format of the buffer
u32 externalFormat; //!< The external format that's exposed by Gralloc
u32 stride;
u32 size; //!< The size of the buffer in bytes
@ -213,6 +213,7 @@ namespace skyline::service::hosbinder {
/**
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/ui/GraphicBuffer.cpp;l=266-301
* @note None of the values from here should really be used aside from Gralloc usage, the ones in NvGraphicHandle take precedence
*/
struct GraphicBuffer {
constexpr static u32 Magic{'GBFR'}; //!< The magic is in little-endian, we do not need to use 'util::MakeMagic'