mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-29 17:45:28 +03:00
Calculate Blocklinear Texture Aligned Size Correctly
The size of blocklinear textures did not consider alignment to Block/ROB boundaries before, it is aligned to them now. Incorrect sizes led to textures not being aliased correctly due to different size calculations for GraphicBufferProducer surfaces and Maxwell3D color RTs.
This commit is contained in:
parent
95685b8207
commit
723189a948
@ -337,7 +337,8 @@ namespace skyline::gpu::interconnect {
|
|||||||
return &*renderTarget.view;
|
return &*renderTarget.view;
|
||||||
|
|
||||||
if (renderTarget.guest.mappings.empty()) {
|
if (renderTarget.guest.mappings.empty()) {
|
||||||
auto size{std::max<u64>(renderTarget.guest.layerStride * (renderTarget.guest.layerCount - renderTarget.guest.baseArrayLayer), renderTarget.guest.format->GetSize(renderTarget.guest.dimensions))};
|
size_t layerStride{renderTarget.guest.GetLayerSize()};
|
||||||
|
size_t size{layerStride * (renderTarget.guest.layerCount - renderTarget.guest.baseArrayLayer)};
|
||||||
auto mappings{channelCtx.asCtx->gmmu.TranslateRange(renderTarget.iova, size)};
|
auto mappings{channelCtx.asCtx->gmmu.TranslateRange(renderTarget.iova, size)};
|
||||||
renderTarget.guest.mappings.assign(mappings.begin(), mappings.end());
|
renderTarget.guest.mappings.assign(mappings.begin(), mappings.end());
|
||||||
}
|
}
|
||||||
|
@ -5,25 +5,38 @@
|
|||||||
|
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
|
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu::texture {
|
||||||
/**
|
// Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32
|
||||||
* @brief Copies the contents of a blocklinear guest texture to a linear output buffer
|
constexpr u8 SectorWidth{16}; // The width of a sector in bytes
|
||||||
*/
|
constexpr u8 SectorHeight{2}; // The height of a sector in lines
|
||||||
void CopyBlockLinearToLinear(GuestTexture &guest, u8 *guestInput, u8 *linearOutput) {
|
constexpr u8 GobWidth{64}; // The width of a GOB in bytes
|
||||||
// Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32
|
constexpr u8 GobHeight{8}; // The height of a GOB in lines
|
||||||
constexpr u8 SectorWidth{16}; // The width of a sector in bytes
|
|
||||||
constexpr u8 SectorHeight{2}; // The height of a sector in lines
|
|
||||||
constexpr u8 GobWidth{64}; // The width of a GOB in bytes
|
|
||||||
constexpr u8 GobHeight{8}; // The height of a GOB in lines
|
|
||||||
|
|
||||||
|
size_t GetBlockLinearLayerSize(const GuestTexture &guest) {
|
||||||
u32 blockHeight{guest.tileConfig.blockHeight}; //!< The height of the blocks in GOBs
|
u32 blockHeight{guest.tileConfig.blockHeight}; //!< The height of the blocks in GOBs
|
||||||
u32 robHeight{GobHeight * blockHeight}; //!< The height of a single ROB (Row of Blocks) in lines
|
u32 robHeight{GobHeight * blockHeight}; //!< The height of a single ROB (Row of Blocks) in lines
|
||||||
u32 surfaceHeight{guest.dimensions.height / guest.format->blockHeight}; //!< The height of the surface in lines
|
u32 surfaceHeight{guest.dimensions.height / guest.format->blockHeight}; //!< The height of the surface in lines
|
||||||
u32 surfaceHeightRobs{util::AlignUp(surfaceHeight, robHeight) / robHeight}; //!< The height of the surface in ROBs (Row Of Blocks)
|
u32 surfaceHeightRobs{util::AlignUp(surfaceHeight, robHeight) / robHeight}; //!< The height of the surface in ROBs (Row Of Blocks)
|
||||||
|
|
||||||
u32 robWidthBytes{util::AlignUp((guest.dimensions.width / guest.format->blockWidth) * guest.format->bpb, GobWidth)}; //!< The width of a ROB in bytes
|
u32 robWidthBytes{util::AlignUp((guest.dimensions.width / guest.format->blockWidth) * guest.format->bpb, GobWidth)}; //!< The width of a ROB in bytes
|
||||||
u32 robWidthBlocks{robWidthBytes / GobWidth}; //!< The width of a ROB in blocks (and GOBs because block width == 1 on the Tegra X1)
|
u32 robWidthBlocks{robWidthBytes / GobWidth}; //!< The width of a ROB in blocks (and GOBs because block width == 1 on the Tegra X1)
|
||||||
u32 robBytes{robWidthBytes * robHeight}; //!< The size of a ROB in bytes
|
u32 robBytes{robWidthBytes * robHeight}; //!< The size of a ROB in bytes
|
||||||
u32 gobYOffset{robWidthBytes * GobHeight}; //!< The offset of the next Y-axis GOB from the current one in linear space
|
|
||||||
|
return robBytes * surfaceHeightRobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copies the contents of a blocklinear guest texture to a linear output buffer
|
||||||
|
*/
|
||||||
|
void CopyBlockLinearToLinear(const GuestTexture &guest, u8 *guestInput, u8 *linearOutput) {
|
||||||
|
u32 blockHeight{guest.tileConfig.blockHeight};
|
||||||
|
u32 robHeight{GobHeight * blockHeight};
|
||||||
|
u32 surfaceHeight{guest.dimensions.height / guest.format->blockHeight};
|
||||||
|
u32 surfaceHeightRobs{util::AlignUp(surfaceHeight, robHeight) / robHeight};
|
||||||
|
u32 robWidthBytes{util::AlignUp((guest.dimensions.width / guest.format->blockWidth) * guest.format->bpb, GobWidth)};
|
||||||
|
u32 robWidthBlocks{robWidthBytes / GobWidth};
|
||||||
|
u32 robBytes{robWidthBytes * robHeight};
|
||||||
|
u32 gobYOffset{robWidthBytes * GobHeight};
|
||||||
|
|
||||||
auto inputSector{guestInput};
|
auto inputSector{guestInput};
|
||||||
auto outputRob{linearOutput};
|
auto outputRob{linearOutput};
|
||||||
@ -55,53 +68,47 @@ namespace skyline::gpu {
|
|||||||
/**
|
/**
|
||||||
* @brief Copies the contents of a blocklinear guest texture to a linear output buffer
|
* @brief Copies the contents of a blocklinear guest texture to a linear output buffer
|
||||||
*/
|
*/
|
||||||
void CopyLinearToBlockLinear(GuestTexture &guest, u8 *linearInput, u8 *guestOutput) {
|
void CopyLinearToBlockLinear(const GuestTexture &guest, u8 *linearInput, u8 *guestOutput) {
|
||||||
// Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32
|
u32 blockHeight{guest.tileConfig.blockHeight};
|
||||||
constexpr u8 SectorWidth{16}; // The width of a sector in bytes
|
u32 robHeight{GobHeight * blockHeight};
|
||||||
constexpr u8 SectorHeight{2}; // The height of a sector in lines
|
u32 surfaceHeight{guest.dimensions.height / guest.format->blockHeight};
|
||||||
constexpr u8 GobWidth{64}; // The width of a GOB in bytes
|
u32 surfaceHeightRobs{util::AlignUp(surfaceHeight, robHeight) / robHeight};
|
||||||
constexpr u8 GobHeight{8}; // The height of a GOB in lines
|
u32 robWidthBytes{util::AlignUp((guest.dimensions.width / guest.format->blockWidth) * guest.format->bpb, GobWidth)};
|
||||||
|
u32 robWidthBlocks{robWidthBytes / GobWidth};
|
||||||
u32 blockHeight{guest.tileConfig.blockHeight}; //!< The height of the blocks in GOBs
|
u32 robBytes{robWidthBytes * robHeight};
|
||||||
u32 robHeight{GobHeight * blockHeight}; //!< The height of a single ROB (Row of Blocks) in lines
|
u32 gobYOffset{robWidthBytes * GobHeight};
|
||||||
u32 surfaceHeight{guest.dimensions.height / guest.format->blockHeight}; //!< The height of the surface in lines
|
|
||||||
u32 surfaceHeightRobs{util::AlignUp(surfaceHeight, robHeight) / robHeight}; //!< The height of the surface in ROBs (Row Of Blocks)
|
|
||||||
u32 robWidthBytes{util::AlignUp((guest.dimensions.width / guest.format->blockWidth) * guest.format->bpb, GobWidth)}; //!< The width of a ROB in bytes
|
|
||||||
u32 robWidthBlocks{robWidthBytes / GobWidth}; //!< The width of a ROB in blocks (and GOBs because block width == 1 on the Tegra X1)
|
|
||||||
u32 robBytes{robWidthBytes * robHeight}; //!< The size of a ROB in bytes
|
|
||||||
u32 gobYOffset{robWidthBytes * GobHeight}; //!< The offset of the next Y-axis GOB from the current one in linear space
|
|
||||||
|
|
||||||
auto outputSector{guestOutput};
|
auto outputSector{guestOutput};
|
||||||
auto inputRob{linearInput};
|
auto inputRob{linearInput};
|
||||||
|
|
||||||
for (u32 rob{}, y{}, paddingY{}; rob < surfaceHeightRobs; rob++) { // Every Surface contains `surfaceHeightRobs` ROBs
|
for (u32 rob{}, y{}, paddingY{}; rob < surfaceHeightRobs; rob++) {
|
||||||
auto outputBlock{inputRob}; // We iterate through a block independently of the ROB
|
auto outputBlock{inputRob};
|
||||||
for (u32 block{}; block < robWidthBlocks; block++) { // Every ROB contains `surfaceWidthBlocks` Blocks
|
for (u32 block{}; block < robWidthBlocks; block++) {
|
||||||
auto inputGob{outputBlock}; // We iterate through a GOB independently of the block
|
auto inputGob{outputBlock};
|
||||||
for (u32 gobY{}; gobY < blockHeight; gobY++) { // Every Block contains `blockHeight` Y-axis GOBs
|
for (u32 gobY{}; gobY < blockHeight; gobY++) {
|
||||||
for (u32 index{}; index < SectorWidth * SectorHeight; index++) { // Every Y-axis GOB contains `sectorWidth * sectorHeight` sectors
|
for (u32 index{}; index < SectorWidth * SectorHeight; index++) {
|
||||||
u32 xT{((index << 3) & 0b10000) | ((index << 1) & 0b100000)}; // Morton-Swizzle on the X-axis
|
u32 xT{((index << 3) & 0b10000) | ((index << 1) & 0b100000)};
|
||||||
u32 yT{((index >> 1) & 0b110) | (index & 0b1)}; // Morton-Swizzle on the Y-axis
|
u32 yT{((index >> 1) & 0b110) | (index & 0b1)};
|
||||||
std::memcpy(outputSector, inputGob + (yT * robWidthBytes) + xT, SectorWidth);
|
std::memcpy(outputSector, inputGob + (yT * robWidthBytes) + xT, SectorWidth);
|
||||||
outputSector += SectorWidth; // `sectorWidth` bytes are of sequential image data
|
outputSector += SectorWidth;
|
||||||
}
|
}
|
||||||
inputGob += gobYOffset; // Increment the output GOB to the next Y-axis GOB
|
inputGob += gobYOffset;
|
||||||
}
|
}
|
||||||
outputSector += paddingY; // Increment the input sector to the next sector
|
outputSector += paddingY;
|
||||||
outputBlock += GobWidth; // Increment the output block to the next block (As Block Width = 1 GOB Width)
|
outputBlock += GobWidth;
|
||||||
}
|
}
|
||||||
inputRob += robBytes; // Increment the output block to the next ROB
|
inputRob += robBytes;
|
||||||
|
|
||||||
y += robHeight; // Increment the Y position to the next ROB
|
y += robHeight;
|
||||||
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));
|
||||||
paddingY = (guest.tileConfig.blockHeight - blockHeight) * (SectorWidth * SectorWidth * SectorHeight); // Calculate the amount of padding between contiguous sectors
|
paddingY = (guest.tileConfig.blockHeight - blockHeight) * (SectorWidth * SectorWidth * SectorHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copies the contents of a pitch-linear guest texture to a linear output buffer
|
* @brief Copies the contents of a pitch-linear guest texture to a linear output buffer
|
||||||
*/
|
*/
|
||||||
void CopyPitchLinearToLinear(GuestTexture &guest, u8 *guestInput, u8 *linearOutput) {
|
void CopyPitchLinearToLinear(const GuestTexture &guest, u8 *guestInput, u8 *linearOutput) {
|
||||||
auto sizeLine{guest.format->GetSize(guest.dimensions.width, 1)}; //!< The size of a single line of pixel data
|
auto sizeLine{guest.format->GetSize(guest.dimensions.width, 1)}; //!< The size of a single line of pixel data
|
||||||
auto sizeStride{guest.tileConfig.pitch}; //!< The size of a single stride of pixel data
|
auto sizeStride{guest.tileConfig.pitch}; //!< The size of a single stride of pixel data
|
||||||
|
|
||||||
@ -118,7 +125,7 @@ namespace skyline::gpu {
|
|||||||
/**
|
/**
|
||||||
* @brief Copies the contents of a linear buffer to a pitch-linear guest texture
|
* @brief Copies the contents of a linear buffer to a pitch-linear guest texture
|
||||||
*/
|
*/
|
||||||
void CopyLinearToPitchLinear(GuestTexture &guest, u8 *linearInput, u8 *guestOutput) {
|
void CopyLinearToPitchLinear(const GuestTexture &guest, u8 *linearInput, u8 *guestOutput) {
|
||||||
auto sizeLine{guest.format->GetSize(guest.dimensions.width, 1)}; //!< The size of a single line of pixel data
|
auto sizeLine{guest.format->GetSize(guest.dimensions.width, 1)}; //!< The size of a single line of pixel data
|
||||||
auto sizeStride{guest.tileConfig.pitch}; //!< The size of a single stride of pixel data
|
auto sizeStride{guest.tileConfig.pitch}; //!< The size of a single stride of pixel data
|
||||||
|
|
@ -5,9 +5,22 @@
|
|||||||
#include <common/trace.h>
|
#include <common/trace.h>
|
||||||
#include <kernel/types/KProcess.h>
|
#include <kernel/types/KProcess.h>
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
#include "copy.h"
|
#include "layout.h"
|
||||||
|
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
|
u32 GuestTexture::GetLayerSize() {
|
||||||
|
switch (tileConfig.mode) {
|
||||||
|
case texture::TileMode::Linear:
|
||||||
|
return layerStride = static_cast<u32>(format->GetSize(dimensions));
|
||||||
|
|
||||||
|
case texture::TileMode::Pitch:
|
||||||
|
return layerStride = dimensions.height * tileConfig.pitch;
|
||||||
|
|
||||||
|
case texture::TileMode::Block:
|
||||||
|
return layerStride = static_cast<u32>(texture::GetBlockLinearLayerSize(*this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TextureView::TextureView(std::shared_ptr<Texture> texture, vk::ImageViewType type, vk::ImageSubresourceRange range, texture::Format format, vk::ComponentMapping mapping) : texture(std::move(texture)), type(type), format(format), mapping(mapping), range(range) {}
|
TextureView::TextureView(std::shared_ptr<Texture> texture, vk::ImageViewType type, vk::ImageSubresourceRange range, texture::Format format, vk::ComponentMapping mapping) : texture(std::move(texture)), type(type), format(format), mapping(mapping), range(range) {}
|
||||||
|
|
||||||
vk::ImageView TextureView::GetView() {
|
vk::ImageView TextureView::GetView() {
|
||||||
@ -103,9 +116,9 @@ namespace skyline::gpu {
|
|||||||
}()};
|
}()};
|
||||||
|
|
||||||
if (guest->tileConfig.mode == texture::TileMode::Block)
|
if (guest->tileConfig.mode == texture::TileMode::Block)
|
||||||
CopyBlockLinearToLinear(*guest, pointer, bufferData);
|
texture::CopyBlockLinearToLinear(*guest, pointer, bufferData);
|
||||||
else if (guest->tileConfig.mode == texture::TileMode::Pitch)
|
else if (guest->tileConfig.mode == texture::TileMode::Pitch)
|
||||||
CopyPitchLinearToLinear(*guest, pointer, bufferData);
|
texture::CopyPitchLinearToLinear(*guest, pointer, bufferData);
|
||||||
else if (guest->tileConfig.mode == texture::TileMode::Linear)
|
else if (guest->tileConfig.mode == texture::TileMode::Linear)
|
||||||
std::memcpy(bufferData, pointer, size);
|
std::memcpy(bufferData, pointer, size);
|
||||||
|
|
||||||
@ -208,9 +221,9 @@ namespace skyline::gpu {
|
|||||||
auto guestOutput{guest->mappings[0].data()};
|
auto guestOutput{guest->mappings[0].data()};
|
||||||
|
|
||||||
if (guest->tileConfig.mode == texture::TileMode::Block)
|
if (guest->tileConfig.mode == texture::TileMode::Block)
|
||||||
CopyLinearToBlockLinear(*guest, hostBuffer, guestOutput);
|
texture::CopyLinearToBlockLinear(*guest, hostBuffer, guestOutput);
|
||||||
else if (guest->tileConfig.mode == texture::TileMode::Pitch)
|
else if (guest->tileConfig.mode == texture::TileMode::Pitch)
|
||||||
CopyLinearToPitchLinear(*guest, hostBuffer, guestOutput);
|
texture::CopyLinearToPitchLinear(*guest, hostBuffer, guestOutput);
|
||||||
else if (guest->tileConfig.mode == texture::TileMode::Linear)
|
else if (guest->tileConfig.mode == texture::TileMode::Linear)
|
||||||
std::memcpy(hostBuffer, guestOutput, format->GetSize(dimensions));
|
std::memcpy(hostBuffer, guestOutput, format->GetSize(dimensions));
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ namespace skyline::gpu {
|
|||||||
texture::TextureType type{};
|
texture::TextureType type{};
|
||||||
u16 baseArrayLayer{};
|
u16 baseArrayLayer{};
|
||||||
u16 layerCount{};
|
u16 layerCount{};
|
||||||
u32 layerStride{}; //!< An optional hint regarding the size of a single layer, it will be set to 0 when not available
|
u32 layerStride{}; //!< An optional hint regarding the size of a single layer, it will be set to 0 when not available, GetLayerSize() should be used to retrieve this value
|
||||||
|
|
||||||
GuestTexture() {}
|
GuestTexture() {}
|
||||||
|
|
||||||
@ -284,6 +284,12 @@ namespace skyline::gpu {
|
|||||||
baseArrayLayer(baseArrayLayer),
|
baseArrayLayer(baseArrayLayer),
|
||||||
layerCount(layerCount),
|
layerCount(layerCount),
|
||||||
layerStride(layerStride) {}
|
layerStride(layerStride) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note Requires `dimensions`, `format` and `tileConfig` to be filled in
|
||||||
|
* @return The size of a single layer with alignment in bytes
|
||||||
|
*/
|
||||||
|
u32 GetLayerSize();
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextureManager;
|
class TextureManager;
|
||||||
|
@ -343,7 +343,9 @@ namespace skyline::service::hosbinder {
|
|||||||
throw exception("Legacy 16Bx16 tiled surfaces are not supported");
|
throw exception("Legacy 16Bx16 tiled surfaces are not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::GuestTexture guestTexture(span<u8>(nvMapHandleObj->GetPointer() + surface.offset, surface.size), gpu::texture::Dimensions(surface.width, surface.height), format, tileConfig, gpu::texture::TextureType::e2D);
|
gpu::texture::Dimensions dimensions(surface.width, surface.height);
|
||||||
|
gpu::GuestTexture guestTexture(span<u8>{}, dimensions, format, tileConfig, gpu::texture::TextureType::e2D);
|
||||||
|
guestTexture.mappings[0] = span<u8>(nvMapHandleObj->GetPointer() + surface.offset, guestTexture.GetLayerSize());
|
||||||
buffer.texture = state.gpu->texture.FindOrCreate(guestTexture)->texture;
|
buffer.texture = state.gpu->texture.FindOrCreate(guestTexture)->texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user