diff --git a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h index 06017803..4f4f4ee6 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h @@ -2257,6 +2257,10 @@ namespace skyline::gpu::interconnect { guest.dimensions = texture::Dimensions(textureControl.widthMinusOne + 1, textureControl.heightMinusOne + 1, 1); u16 depth{static_cast(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; diff --git a/app/src/main/cpp/skyline/gpu/interconnect/types/tic.h b/app/src/main/cpp/skyline/gpu/interconnect/types/tic.h index d158ff5d..cdfe230b 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/types/tic.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/types/tic.h @@ -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; diff --git a/app/src/main/cpp/skyline/gpu/texture/layout.cpp b/app/src/main/cpp/skyline/gpu/texture/layout.cpp index 910e7a7a..14cd2cca 100644 --- a/app/src/main/cpp/skyline/gpu/texture/layout.cpp +++ b/app/src/main/cpp/skyline/gpu/texture/layout.cpp @@ -23,8 +23,69 @@ namespace skyline::gpu::texture { return robLineBytes * robHeight * surfaceHeightRobs * robDepth; } + template + constexpr Type CalculateBlockGobs(Type blockGobs, Type surfaceGobs) { + if (surfaceGobs > blockGobs) + return blockGobs; + return std::bit_ceil(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(util::DivideCeil(dimensions.width, formatBlockWidth) * formatBpb, GobWidth)}; + size_t gobsHeight{util::DivideCeil(util::DivideCeil(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 GetBlockLinearMipLayout(Dimensions dimensions, size_t formatBlockHeight, size_t formatBlockWidth, size_t formatBpb, size_t gobBlockHeight, size_t gobBlockDepth, size_t levelCount) { + std::vector mipLevels; + mipLevels.reserve(levelCount); + + size_t gobsWidth{util::DivideCeil(util::DivideCeil(dimensions.width, formatBlockWidth) * formatBpb, GobWidth)}; + size_t gobsHeight{util::DivideCeil(util::DivideCeil(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(dimensions.width, formatBlockWidth) * formatBpb * util::DivideCeil(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(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 void CopyBlockLinearInternal(Dimensions dimensions, diff --git a/app/src/main/cpp/skyline/gpu/texture/layout.h b/app/src/main/cpp/skyline/gpu/texture/layout.h index e4368816..1c26b7c2 100644 --- a/app/src/main/cpp/skyline/gpu/texture/layout.h +++ b/app/src/main/cpp/skyline/gpu/texture/layout.h @@ -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 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 */ diff --git a/app/src/main/cpp/skyline/gpu/texture/texture.cpp b/app/src/main/cpp/skyline/gpu/texture/texture.cpp index 95d6ed68..870b940e 100644 --- a/app/src/main/cpp/skyline/gpu/texture/texture.cpp +++ b/app/src/main/cpp/skyline/gpu/texture/texture.cpp @@ -22,7 +22,7 @@ namespace skyline::gpu { return layerStride = dimensions.height * tileConfig.pitch; case texture::TileMode::Block: - return layerStride = static_cast(texture::GetBlockLinearLayerSize(dimensions, format->blockHeight, format->blockWidth, format->bpb, tileConfig.blockHeight, tileConfig.blockDepth)); + return layerStride = static_cast(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 { if (tiling == vk::ImageTiling::eOptimal || !std::holds_alternative(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(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 bufferImageCopies; + std::vector 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 bufferImageCopies; + boost::container::small_vector 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(tiling)); } } @@ -291,7 +353,7 @@ namespace skyline::gpu { texture->CopyToGuest(stagingBuffer ? stagingBuffer->data() : std::get(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(format->GetSize(dimensions))), sampleCount(sampleCount) {} + u32 CalculateLevelStride(const std::vector &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(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(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(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(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, diff --git a/app/src/main/cpp/skyline/gpu/texture/texture.h b/app/src/main/cpp/skyline/gpu/texture/texture.h index a13e1326..11358e15 100644 --- a/app/src/main/cpp/skyline/gpu/texture/texture.h +++ b/app/src/main/cpp/skyline/gpu/texture/texture.h @@ -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 mapping, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, texture::TextureType type, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0) + GuestTexture(span 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 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 diff --git a/app/src/main/cpp/skyline/gpu/texture_manager.cpp b/app/src/main/cpp/skyline/gpu/texture_manager.cpp index 22009397..1addbb68 100644 --- a/app/src/main/cpp/skyline/gpu/texture_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/texture_manager.cpp @@ -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(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(guestTexture.type), vk::ImageSubresourceRange{ .aspectMask = guestTexture.aspect, - .levelCount = texture->mipLevels, + .baseMipLevel = guestTexture.viewMipBase, + .levelCount = guestTexture.viewMipCount, .layerCount = texture->layerCount, }, guestTexture.format, guestTexture.swizzle); }