Implement Mipmapped Texture Support

Support for mipmapped textures was not implemented which is fairly crucial to proper rendering of games as the only level that would load is the first level (highest resolution), that might result in a lot more memory bandwidth being utilized. Mipmapping also has associated benefits regarding aliasing as it has a minor anti-aliasing effect on distant textures. 

This commit entirely implements mipmapping support but it does not extend to full support for views into specific mipmap levels due to the texture manager implemention being incomplete.
This commit is contained in:
PixelyIon 2022-05-28 20:27:17 +05:30
parent da7e6a7df7
commit 7d4e0a7844
7 changed files with 253 additions and 68 deletions

View File

@ -2257,6 +2257,10 @@ namespace skyline::gpu::interconnect {
guest.dimensions = texture::Dimensions(textureControl.widthMinusOne + 1, textureControl.heightMinusOne + 1, 1);
u16 depth{static_cast<u16>(textureControl.depthMinusOne + 1)};
guest.mipLevelCount = textureControl.mipMaxLevels + 1;
guest.viewMipBase = textureControl.viewConfig.mipMinLevel;
guest.viewMipCount = textureControl.viewConfig.mipMaxLevel - textureControl.viewConfig.mipMinLevel + 1;
using TicType = TextureImageControl::TextureType;
using TexType = texture::TextureType;
switch (textureControl.textureType) {
@ -2271,8 +2275,11 @@ namespace skyline::gpu::interconnect {
case TicType::e1DBuffer:
throw exception("1D Buffers are not supported");
case TicType::e2D:
case TicType::e2DNoMipmap:
guest.mipLevelCount = 1;
guest.viewMipBase = 0;
guest.viewMipCount = 1;
case TicType::e2D:
guest.type = TexType::e2D;
guest.layerCount = 1;
break;

View File

@ -316,8 +316,8 @@ namespace skyline::gpu::interconnect {
union ViewConfig {
u32 colorKeyValue;
struct {
u32 viewMipMinLevel : 4;
u32 viewMipMaxLevel : 4;
u32 mipMinLevel : 4;
u32 mipMaxLevel : 4;
MsaaMode msaaMode : 4;
u32 minLodClamp : 12;
u32 _pad2_ : 8;

View File

@ -23,8 +23,69 @@ namespace skyline::gpu::texture {
return robLineBytes * robHeight * surfaceHeightRobs * robDepth;
}
template<typename Type>
constexpr Type CalculateBlockGobs(Type blockGobs, Type surfaceGobs) {
if (surfaceGobs > blockGobs)
return blockGobs;
return std::bit_ceil<Type>(surfaceGobs);
}
size_t GetBlockLinearLayerSize(Dimensions dimensions, size_t formatBlockHeight, size_t formatBlockWidth, size_t formatBpb, size_t gobBlockHeight, size_t gobBlockDepth, size_t levelCount, bool isMultiLayer) {
// Calculate the size of the surface in GOBs on every axis
size_t gobsWidth{util::DivideCeil<size_t>(util::DivideCeil<size_t>(dimensions.width, formatBlockWidth) * formatBpb, GobWidth)};
size_t gobsHeight{util::DivideCeil<size_t>(util::DivideCeil<size_t>(dimensions.height, formatBlockHeight), GobHeight)};
size_t gobsDepth{dimensions.depth};
size_t totalSize{}, layerAlignment{GobWidth * GobHeight * gobBlockHeight * gobBlockDepth};
for (size_t i{}; i < levelCount; i++) {
// Iterate over every level, adding the size of the current level to the total size
totalSize += (GobWidth * gobsWidth) * (GobHeight * util::AlignUp(gobsHeight, gobBlockHeight)) * util::AlignUp(gobsDepth, gobBlockDepth);
// Successively divide every dimension by 2 until the final level is reached
gobsWidth = std::max(gobsWidth / 2, 1UL);
gobsHeight = std::max(gobsHeight / 2, 1UL);
gobsDepth = std::max(gobsDepth / 2, 1UL);
gobBlockHeight = CalculateBlockGobs(gobBlockHeight, gobsHeight);
gobBlockDepth = CalculateBlockGobs(gobBlockDepth, gobsDepth);
}
return isMultiLayer ? util::AlignUp(totalSize, layerAlignment) : totalSize;
}
std::vector<MipLevelLayout> GetBlockLinearMipLayout(Dimensions dimensions, size_t formatBlockHeight, size_t formatBlockWidth, size_t formatBpb, size_t gobBlockHeight, size_t gobBlockDepth, size_t levelCount) {
std::vector<MipLevelLayout> mipLevels;
mipLevels.reserve(levelCount);
size_t gobsWidth{util::DivideCeil<size_t>(util::DivideCeil<size_t>(dimensions.width, formatBlockWidth) * formatBpb, GobWidth)};
size_t gobsHeight{util::DivideCeil<size_t>(util::DivideCeil<size_t>(dimensions.height, formatBlockHeight), GobHeight)};
// Note: We don't need a separate gobsDepth variable here, since a GOB is always a single slice deep and the value would be the same as the depth dimension
for (size_t i{}; i < levelCount; i++) {
mipLevels.emplace_back(
dimensions,
util::DivideCeil<size_t>(dimensions.width, formatBlockWidth) * formatBpb * util::DivideCeil<size_t>(dimensions.height, formatBlockHeight) * dimensions.depth,
(GobWidth * gobsWidth) * (GobHeight * util::AlignUp(gobsHeight, gobBlockHeight)) * util::AlignUp(dimensions.depth, gobBlockDepth),
gobBlockHeight, gobBlockDepth
);
gobsWidth = std::max(gobsWidth / 2, 1UL);
gobsHeight = std::max(gobsHeight / 2, 1UL);
dimensions.width = std::max(dimensions.width / 2, 1U);
dimensions.height = std::max(dimensions.height / 2, 1U);
dimensions.depth = std::max(dimensions.depth / 2, 1U);
gobBlockHeight = CalculateBlockGobs(gobBlockHeight, gobsHeight);
gobBlockDepth = CalculateBlockGobs(gobBlockDepth, static_cast<size_t>(dimensions.depth));
}
return mipLevels;
}
/**
* @brief Copies pixel data between a linear and blocklinear texture
* @tparam BlockLinearToLinear Whether to copy from a blocklinear texture to a linear texture or a linear texture to a blocklinear texture
*/
template<bool BlockLinearToLinear>
void CopyBlockLinearInternal(Dimensions dimensions,

View File

@ -13,6 +13,23 @@ namespace skyline::gpu::texture {
size_t formatBlockWidth, size_t formatBlockHeight, size_t formatBpb,
size_t gobBlockHeight, size_t gobBlockDepth);
/**
* @param isMultiLayer If the texture has more than one layer, a multi-layer texture requires alignment to a block at layer end
* @return The size of a layer of the specified block-linear surface in bytes
*/
size_t GetBlockLinearLayerSize(Dimensions dimensions,
size_t formatBlockHeight, size_t formatBlockWidth, size_t formatBpb,
size_t gobBlockHeight, size_t gobBlockDepth,
size_t levelCount, bool isMultiLayer);
/**
* @return A vector of metadata about every mipmapped level of the supplied block-linear surface
*/
std::vector<MipLevelLayout> GetBlockLinearMipLayout(Dimensions dimensions,
size_t formatBlockHeight, size_t formatBlockWidth, size_t formatBpb,
size_t gobBlockHeight, size_t gobBlockDepth,
size_t levelCount);
/**
* @brief Copies the contents of a blocklinear texture to a linear output buffer
*/

View File

@ -22,7 +22,7 @@ namespace skyline::gpu {
return layerStride = dimensions.height * tileConfig.pitch;
case texture::TileMode::Block:
return layerStride = static_cast<u32>(texture::GetBlockLinearLayerSize(dimensions, format->blockHeight, format->blockWidth, format->bpb, tileConfig.blockHeight, tileConfig.blockDepth));
return layerStride = static_cast<u32>(texture::GetBlockLinearLayerSize(dimensions, format->blockHeight, format->blockWidth, format->bpb, tileConfig.blockHeight, tileConfig.blockDepth, mipLevelCount, layerCount > 1));
}
}
@ -137,7 +137,6 @@ namespace skyline::gpu {
throw exception("Guest and host dimensions being different is not supported currently");
auto pointer{mirror.data()};
auto size{layerStride * layerCount};
WaitOnBacking();
@ -145,7 +144,7 @@ namespace skyline::gpu {
auto stagingBuffer{[&]() -> std::shared_ptr<memory::StagingBuffer> {
if (tiling == vk::ImageTiling::eOptimal || !std::holds_alternative<memory::Image>(backing)) {
// We need a staging buffer for all optimal copies (since we aren't aware of the host optimal layout) and linear textures which we cannot map on the CPU since we do not have access to their backing VkDeviceMemory
auto stagingBuffer{gpu.memory.AllocateStagingBuffer(size)};
auto stagingBuffer{gpu.memory.AllocateStagingBuffer(surfaceSize)};
bufferData = stagingBuffer->data();
return stagingBuffer;
} else if (tiling == vk::ImageTiling::eLinear) {
@ -162,15 +161,38 @@ namespace skyline::gpu {
}()};
auto guestLayerStride{guest->GetLayerStride()};
for (size_t layer{}; layer < layerCount; ++layer) {
if (guest->tileConfig.mode == texture::TileMode::Block)
texture::CopyBlockLinearToLinear(*guest, pointer, bufferData);
else if (guest->tileConfig.mode == texture::TileMode::Pitch)
texture::CopyPitchLinearToLinear(*guest, pointer, bufferData);
else if (guest->tileConfig.mode == texture::TileMode::Linear)
std::memcpy(bufferData, pointer, size);
pointer += guestLayerStride;
bufferData += layerStride;
if (levelCount == 1) {
for (size_t layer{}; layer < layerCount; layer++) {
if (guest->tileConfig.mode == texture::TileMode::Block)
texture::CopyBlockLinearToLinear(*guest, pointer, bufferData);
else if (guest->tileConfig.mode == texture::TileMode::Pitch)
texture::CopyPitchLinearToLinear(*guest, pointer, bufferData);
else if (guest->tileConfig.mode == texture::TileMode::Linear)
std::memcpy(bufferData, pointer, surfaceSize);
pointer += guestLayerStride;
bufferData += layerStride;
}
} else if (levelCount > 1 && guest->tileConfig.mode == texture::TileMode::Block) {
// We need to generate a buffer that has all layers for a given mip level while Tegra X1 layout holds all mip levels for a given layer
for (size_t layer{}; layer < layerCount; layer++) {
auto inputLevel{pointer}, outputLevel{bufferData};
for (size_t level{}; level < levelCount; ++level) {
const auto &mipLayout{mipLayouts[level]};
texture::CopyBlockLinearToLinear(
mipLayout.dimensions,
guest->format->blockWidth, guest->format->blockHeight, guest->format->bpb,
mipLayout.blockHeight, mipLayout.blockDepth,
inputLevel, outputLevel + (layer * mipLayout.linearSize) // Offset into the current layer relative to the start of the current mip level
);
inputLevel += mipLayout.blockLinearSize; // Skip over the current mip level as we've deswizzled it
outputLevel += layerCount * mipLayout.linearSize; // We need to offset the output buffer by the size of the previous mip level
}
pointer += guestLayerStride; // We need to offset the input buffer by the size of the previous guest layer, this can differ from inputLevel's value due to layer end padding or guest RT layer stride
}
} else if (levelCount != 0) {
throw exception("Mipmapped textures with tiling mode '{}' aren't supported", static_cast<int>(tiling));
}
if (stagingBuffer && cycle.lock() != pCycle)
@ -192,21 +214,29 @@ namespace skyline::gpu {
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange = {
.aspectMask = format->vkAspect,
.levelCount = mipLevels,
.levelCount = levelCount,
.layerCount = layerCount,
},
});
boost::container::static_vector<const vk::BufferImageCopy, 3> bufferImageCopies;
std::vector<vk::BufferImageCopy> bufferImageCopies;
auto pushBufferImageCopyWithAspect{[&](vk::ImageAspectFlagBits aspect) {
bufferImageCopies.emplace_back(
vk::BufferImageCopy{
.imageExtent = dimensions,
.imageSubresource = {
.aspectMask = aspect,
.layerCount = layerCount,
},
});
vk::DeviceSize bufferOffset{};
u32 mipLevel{};
for (auto &level : mipLayouts) {
bufferImageCopies.emplace_back(
vk::BufferImageCopy{
.bufferOffset = bufferOffset,
.imageSubresource = {
.aspectMask = aspect,
.mipLevel = mipLevel++,
.layerCount = layerCount,
},
.imageExtent = level.dimensions,
}
);
bufferOffset += level.linearSize * layerCount;
}
}};
if (format->vkAspect & vk::ImageAspectFlagBits::eColor)
@ -231,21 +261,29 @@ namespace skyline::gpu {
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange = {
.aspectMask = format->vkAspect,
.levelCount = mipLevels,
.levelCount = levelCount,
.layerCount = layerCount,
},
});
boost::container::static_vector<const vk::BufferImageCopy, 3> bufferImageCopies;
boost::container::small_vector<vk::BufferImageCopy, 10> bufferImageCopies;
auto pushBufferImageCopyWithAspect{[&](vk::ImageAspectFlagBits aspect) {
bufferImageCopies.emplace_back(
vk::BufferImageCopy{
.imageExtent = dimensions,
.imageSubresource = {
.aspectMask = aspect,
.layerCount = layerCount,
},
});
vk::DeviceSize bufferOffset{};
u32 mipLevel{};
for (auto &level : mipLayouts) {
bufferImageCopies.emplace_back(
vk::BufferImageCopy{
.bufferOffset = bufferOffset,
.imageSubresource = {
.aspectMask = aspect,
.mipLevel = mipLevel++,
.layerCount = layerCount,
},
.imageExtent = level.dimensions,
}
);
bufferOffset += level.linearSize * levelCount;
}
}};
if (format->vkAspect & vk::ImageAspectFlagBits::eColor)
@ -272,15 +310,39 @@ namespace skyline::gpu {
auto guestOutput{mirror.data()};
auto guestLayerStride{guest->GetLayerStride()};
for (size_t layer{}; layer < layerCount; ++layer) {
if (guest->tileConfig.mode == texture::TileMode::Block)
texture::CopyLinearToBlockLinear(*guest, hostBuffer, guestOutput);
else if (guest->tileConfig.mode == texture::TileMode::Pitch)
texture::CopyLinearToPitchLinear(*guest, hostBuffer, guestOutput);
else if (guest->tileConfig.mode == texture::TileMode::Linear)
std::memcpy(hostBuffer, guestOutput, layerStride);
guestOutput += guestLayerStride;
hostBuffer += layerStride;
if (levelCount == 1) {
for (size_t layer{}; layer < layerCount; layer++) {
if (guest->tileConfig.mode == texture::TileMode::Block)
texture::CopyLinearToBlockLinear(*guest, hostBuffer, guestOutput);
else if (guest->tileConfig.mode == texture::TileMode::Pitch)
texture::CopyLinearToPitchLinear(*guest, hostBuffer, guestOutput);
else if (guest->tileConfig.mode == texture::TileMode::Linear)
std::memcpy(hostBuffer, guestOutput, layerStride);
guestOutput += guestLayerStride;
hostBuffer += layerStride;
}
} else if (levelCount > 1 && guest->tileConfig.mode == texture::TileMode::Block) {
// We need to copy into the Tegra X1 layout holds all mip levels for a given layer while the input buffer has all layers for a given mip level
// Note: See SynchronizeHostImpl for additional comments
for (size_t layer{}; layer < layerCount; layer++) {
auto outputLevel{guestOutput}, inputLevel{hostBuffer};
for (size_t level{}; level < levelCount; ++level) {
const auto &mipLayout{mipLayouts[level]};
texture::CopyLinearToBlockLinear(
mipLayout.dimensions,
guest->format->blockWidth, guest->format->blockHeight, guest->format->bpb,
mipLayout.blockHeight, mipLayout.blockDepth,
outputLevel, inputLevel + (layer * mipLayout.linearSize)
);
outputLevel += mipLayout.blockLinearSize;
inputLevel += layerCount * mipLayout.linearSize;
}
guestOutput += guestLayerStride;
}
} else if (levelCount != 0) {
throw exception("Mipmapped textures with tiling mode '{}' aren't supported", static_cast<int>(tiling));
}
}
@ -291,7 +353,7 @@ namespace skyline::gpu {
texture->CopyToGuest(stagingBuffer ? stagingBuffer->data() : std::get<memory::Image>(texture->backing).data());
}
Texture::Texture(GPU &gpu, BackingType &&backing, texture::Dimensions dimensions, texture::Format format, vk::ImageLayout layout, vk::ImageTiling tiling, vk::ImageCreateFlags flags, vk::ImageUsageFlags usage, u32 mipLevels, u32 layerCount, vk::SampleCountFlagBits sampleCount)
Texture::Texture(GPU &gpu, BackingType &&backing, texture::Dimensions dimensions, texture::Format format, vk::ImageLayout layout, vk::ImageTiling tiling, vk::ImageCreateFlags flags, vk::ImageUsageFlags usage, u32 levelCount, u32 layerCount, vk::SampleCountFlagBits sampleCount)
: gpu(gpu),
backing(std::move(backing)),
dimensions(dimensions),
@ -300,11 +362,17 @@ namespace skyline::gpu {
tiling(tiling),
flags(flags),
usage(usage),
mipLevels(mipLevels),
levelCount(levelCount),
layerCount(layerCount),
layerStride(static_cast<u32>(format->GetSize(dimensions))),
sampleCount(sampleCount) {}
u32 CalculateLevelStride(const std::vector<texture::MipLevelLayout> &mipLayouts) {
u32 surfaceSize{};
for (const auto &level : mipLayouts)
surfaceSize += level.linearSize;
return surfaceSize;
}
Texture::Texture(GPU &pGpu, GuestTexture pGuest)
: gpu(pGpu),
guest(std::move(pGuest)),
@ -312,9 +380,18 @@ namespace skyline::gpu {
format(guest->format),
layout(vk::ImageLayout::eUndefined),
tiling(vk::ImageTiling::eOptimal), // Force Optimal due to not adhering to host subresource layout during Linear synchronization
mipLevels(1),
layerCount(guest->layerCount),
layerStride(static_cast<u32>(format->GetSize(dimensions))),
levelCount(guest->mipLevelCount),
mipLayouts(
texture::GetBlockLinearMipLayout(
guest->dimensions,
guest->format->blockHeight, guest->format->blockWidth, guest->format->bpb,
guest->tileConfig.blockHeight, guest->tileConfig.blockDepth,
guest->mipLevelCount
)
),
surfaceSize(CalculateLevelStride(mipLayouts) * layerCount),
sampleCount(vk::SampleCountFlagBits::e1),
flags(gpu.traits.quirks.vkImageMutableFormatCostly ? vk::ImageCreateFlags{} : vk::ImageCreateFlagBits::eMutableFormat),
usage(vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled) {
@ -346,10 +423,10 @@ namespace skyline::gpu {
vk::ImageCreateInfo imageCreateInfo{
.flags = flags,
.imageType = imageType,
.format = *guest->format,
.extent = guest->dimensions,
.mipLevels = 1,
.arrayLayers = guest->layerCount,
.format = *format,
.extent = dimensions,
.mipLevels = levelCount,
.arrayLayers = layerCount,
.samples = vk::SampleCountFlagBits::e1,
.tiling = tiling,
.usage = usage,
@ -429,7 +506,7 @@ namespace skyline::gpu {
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange = {
.aspectMask = format->vkAspect,
.levelCount = mipLevels,
.levelCount = levelCount,
.layerCount = layerCount,
},
});
@ -500,8 +577,7 @@ namespace skyline::gpu {
WaitOnFence();
if (tiling == vk::ImageTiling::eOptimal || !std::holds_alternative<memory::Image>(backing)) {
auto size{layerStride * layerCount};
auto stagingBuffer{gpu.memory.AllocateStagingBuffer(size)};
auto stagingBuffer{gpu.memory.AllocateStagingBuffer(surfaceSize)};
auto lCycle{gpu.scheduler.Submit([&](vk::raii::CommandBuffer &commandBuffer) {
CopyIntoStagingBuffer(commandBuffer, stagingBuffer);
@ -534,8 +610,7 @@ namespace skyline::gpu {
WaitOnFence();
if (tiling == vk::ImageTiling::eOptimal || !std::holds_alternative<memory::Image>(backing)) {
auto size{layerStride * layerCount};
auto stagingBuffer{gpu.memory.AllocateStagingBuffer(size)};
auto stagingBuffer{gpu.memory.AllocateStagingBuffer(surfaceSize)};
CopyIntoStagingBuffer(commandBuffer, stagingBuffer);
pCycle->AttachObject(std::make_shared<TextureBufferCopy>(shared_from_this(), stagingBuffer));
@ -616,7 +691,7 @@ namespace skyline::gpu {
.baseArrayLayer = subresource.baseArrayLayer,
.layerCount = subresource.layerCount == VK_REMAINING_ARRAY_LAYERS ? layerCount - subresource.baseArrayLayer : subresource.layerCount,
};
for (; subresourceLayers.mipLevel < (subresource.levelCount == VK_REMAINING_MIP_LEVELS ? mipLevels - subresource.baseMipLevel : subresource.levelCount); subresourceLayers.mipLevel++)
for (; subresourceLayers.mipLevel < (subresource.levelCount == VK_REMAINING_MIP_LEVELS ? levelCount - subresource.baseMipLevel : subresource.levelCount); subresourceLayers.mipLevel++)
commandBuffer.copyImage(sourceBacking, vk::ImageLayout::eTransferSrcOptimal, destinationBacking, vk::ImageLayout::eTransferDstOptimal, vk::ImageCopy{
.srcSubresource = subresourceLayers,
.dstSubresource = subresourceLayers,

View File

@ -222,6 +222,18 @@ namespace skyline::gpu {
e2DArray = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
eCubeArray = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY,
};
/**
* @brief A description of a single mipmapped level of a block-linear surface
*/
struct MipLevelLayout {
Dimensions dimensions; //!< The dimensions of the mipmapped level, these are exact dimensions and not aligned to a GOB
size_t linearSize; //!< The size of a linear image with this mipmapped level in bytes
size_t blockLinearSize; //!< The size of a blocklinear image with this mipmapped level in bytes
size_t blockHeight, blockDepth; //!< The block height and block depth set for the level
constexpr MipLevelLayout(Dimensions dimensions, size_t linearSize, size_t blockLinearSize, size_t blockHeight, size_t blockDepth) : dimensions{dimensions}, linearSize{linearSize}, blockLinearSize{blockLinearSize}, blockHeight{blockHeight}, blockDepth{blockDepth} {}
};
}
class Texture;
@ -239,14 +251,17 @@ namespace skyline::gpu {
texture::TileConfig tileConfig{};
texture::TextureType type{};
u32 baseArrayLayer{};
u32 layerCount{};
u32 layerCount{1};
u32 layerStride{}; //!< An optional hint regarding the size of a single layer, it **should** be set to 0 when not available and should never be a non-0 value that doesn't reflect the correct layer stride
u32 mipLevelCount{1}; //!< The total amount of mip levels in the parent image, if one exists
u32 viewMipBase{}; //!< The minimum mip level of the view, this is the smallest mip level that can be accessed via this view
u32 viewMipCount{1}; //!< The amount of mip levels starting from the base that can be accessed via this view
vk::ComponentMapping swizzle{}; //!< Component swizzle derived from format requirements and the guest supplied swizzle
vk::ImageAspectFlags aspect{};
GuestTexture() {}
GuestTexture(Mappings mappings, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, texture::TextureType type, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0)
GuestTexture(Mappings mappings, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, texture::TextureType type, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0, u32 mipLevelCount = 1, u32 viewMipBase = 0, u32 viewMipCount = 1)
: mappings(mappings),
dimensions(dimensions),
format(format),
@ -255,9 +270,12 @@ namespace skyline::gpu {
baseArrayLayer(baseArrayLayer),
layerCount(layerCount),
layerStride(layerStride),
mipLevelCount(mipLevelCount),
viewMipBase(viewMipBase),
viewMipCount(viewMipCount),
aspect(format->vkAspect) {}
GuestTexture(span<u8> mapping, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, texture::TextureType type, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0)
GuestTexture(span<u8> mapping, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, texture::TextureType type, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0, u32 mipLevelCount = 1, u32 viewMipBase = 0, u32 viewMipCount = 1)
: mappings(1, mapping),
dimensions(dimensions),
format(format),
@ -266,6 +284,9 @@ namespace skyline::gpu {
baseArrayLayer(baseArrayLayer),
layerCount(layerCount),
layerStride(layerStride),
mipLevelCount(mipLevelCount),
viewMipBase(viewMipBase),
viewMipCount(viewMipCount),
aspect(format->vkAspect) {}
/**
@ -416,16 +437,18 @@ namespace skyline::gpu {
vk::ImageTiling tiling;
vk::ImageCreateFlags flags;
vk::ImageUsageFlags usage;
u32 mipLevels;
u32 layerCount; //!< The amount of array layers in the image, utilized for efficient binding (Not to be confused with the depth or faces in a cubemap)
u32 layerStride; //!< The stride of a single array layer given linear tiling
u32 layerCount; //!< The amount of array layers in the image
u32 layerStride; //!< The stride of a single array layer given linear tiling, this does **not** consider mipmapping
u32 levelCount;
std::vector<texture::MipLevelLayout> mipLayouts; //!< The layout of each mip level in the guest texture
u32 surfaceSize; //!< The size of the entire surface given linear tiling, this contains all mip levels and layers
vk::SampleCountFlagBits sampleCount;
/**
* @brief Creates a texture object wrapping the supplied backing with the supplied attributes
* @param layout The initial layout of the texture, it **must** be eUndefined or ePreinitialized
*/
Texture(GPU &gpu, BackingType &&backing, texture::Dimensions dimensions, texture::Format format, vk::ImageLayout layout, vk::ImageTiling tiling, vk::ImageCreateFlags flags, vk::ImageUsageFlags usage, u32 mipLevels = 1, u32 layerCount = 1, vk::SampleCountFlagBits sampleCount = vk::SampleCountFlagBits::e1);
Texture(GPU &gpu, BackingType &&backing, texture::Dimensions dimensions, texture::Format format, vk::ImageLayout layout, vk::ImageTiling tiling, vk::ImageCreateFlags flags, vk::ImageUsageFlags usage, u32 levelCount = 1, u32 layerCount = 1, vk::SampleCountFlagBits sampleCount = vk::SampleCountFlagBits::e1);
/**
* @brief Creates a texture object wrapping the guest texture with a backing that can represent the guest texture data

View File

@ -45,11 +45,12 @@ namespace skyline::gpu {
if (firstHostMapping == hostMappings.begin() && firstHostMapping->begin() == guestMapping.begin() && mappingMatch && lastHostMapping == hostMappings.end() && lastGuestMapping.end() == std::prev(lastHostMapping)->end()) {
// We've gotten a perfect 1:1 match for *all* mappings from the start to end, we just need to check for compatibility aside from this
auto &matchGuestTexture{*hostMapping->texture->guest};
if (matchGuestTexture.format->IsCompatible(*guestTexture.format) && matchGuestTexture.dimensions == guestTexture.dimensions && matchGuestTexture.tileConfig == guestTexture.tileConfig) {
if (matchGuestTexture.format->IsCompatible(*guestTexture.format) && (matchGuestTexture.dimensions == guestTexture.dimensions || matchGuestTexture.viewMipBase > 0) && matchGuestTexture.tileConfig == guestTexture.tileConfig) {
auto &texture{hostMapping->texture};
return texture->GetView(static_cast<vk::ImageViewType>(guestTexture.type), vk::ImageSubresourceRange{
.aspectMask = guestTexture.aspect,
.levelCount = texture->mipLevels,
.baseMipLevel = guestTexture.viewMipBase,
.levelCount = guestTexture.viewMipCount,
.layerCount = texture->layerCount,
}, guestTexture.format, guestTexture.swizzle);
}
@ -81,7 +82,8 @@ namespace skyline::gpu {
return texture->GetView(static_cast<vk::ImageViewType>(guestTexture.type), vk::ImageSubresourceRange{
.aspectMask = guestTexture.aspect,
.levelCount = texture->mipLevels,
.baseMipLevel = guestTexture.viewMipBase,
.levelCount = guestTexture.viewMipCount,
.layerCount = texture->layerCount,
}, guestTexture.format, guestTexture.swizzle);
}