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 6f8da3ba..19a99b6d 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h @@ -113,6 +113,7 @@ namespace skyline::gpu::interconnect { bool disabled{true}; //!< If this RT has been disabled and will be an unbound attachment instead IOVA iova{}; u32 widthBytes{}; //!< The width in bytes for linear textures + u32 layerStride{}; //!< The stride of a single layer in bytes bool is3d{}; //!< If the RT is 3D, this controls if the RT is 3D or layered GuestTexture guest{}; std::shared_ptr view{}; @@ -370,7 +371,7 @@ namespace skyline::gpu::interconnect { } void SetRenderTargetLayerStride(RenderTarget &renderTarget, u32 layerStrideLsr2) { - renderTarget.guest.layerStride = layerStrideLsr2 << 2; + renderTarget.layerStride = layerStrideLsr2 << 2; renderTarget.view.reset(); } @@ -397,8 +398,13 @@ namespace skyline::gpu::interconnect { else if (renderTarget.view) return &*renderTarget.view; + if (renderTarget.guest.baseArrayLayer > 0 || renderTarget.guest.layerCount > 1) + renderTarget.guest.layerStride = renderTarget.layerStride; // Games can supply a layer stride that may include intentional padding which can contain additional mip layers + else + renderTarget.guest.layerStride = 0; // We want to explicitly reset the stride to 0 for non-array textures + if (renderTarget.guest.mappings.empty()) { - size_t layerStride{renderTarget.guest.GetLayerSize()}; + size_t layerStride{renderTarget.guest.GetLayerStride()}; size_t size{layerStride * (renderTarget.guest.layerCount - renderTarget.guest.baseArrayLayer)}; auto mappings{channelCtx.asCtx->gmmu.TranslateRange(renderTarget.iova, size)}; renderTarget.guest.mappings.assign(mappings.begin(), mappings.end()); @@ -2323,7 +2329,7 @@ namespace skyline::gpu::interconnect { throw exception("Unsupported TIC Header Type: {}", static_cast(textureControl.headerType)); } - auto mappings{channelCtx.asCtx->gmmu.TranslateRange(textureControl.Iova(), guest.GetLayerSize() * (guest.layerCount - guest.baseArrayLayer))}; + auto mappings{channelCtx.asCtx->gmmu.TranslateRange(textureControl.Iova(), guest.GetLayerStride() * (guest.layerCount - guest.baseArrayLayer))}; guest.mappings.assign(mappings.begin(), mappings.end()); } else if (auto textureView{poolTexture.view.lock()}; textureView != nullptr) { // If the entry already exists and the view is still valid then we return it directly diff --git a/app/src/main/cpp/skyline/gpu/texture/texture.cpp b/app/src/main/cpp/skyline/gpu/texture/texture.cpp index 459bd2da..f7d22ead 100644 --- a/app/src/main/cpp/skyline/gpu/texture/texture.cpp +++ b/app/src/main/cpp/skyline/gpu/texture/texture.cpp @@ -10,7 +10,10 @@ #include "adreno_aliasing.h" namespace skyline::gpu { - u32 GuestTexture::GetLayerSize() { + u32 GuestTexture::GetLayerStride() { + if (layerStride) + return layerStride; + switch (tileConfig.mode) { case texture::TileMode::Linear: return layerStride = static_cast(format->GetSize(dimensions)); @@ -134,7 +137,7 @@ namespace skyline::gpu { throw exception("Guest and host dimensions being different is not supported currently"); auto pointer{mirror.data()}; - auto size{format->GetSize(dimensions) * layerCount}; + auto size{layerStride * layerCount}; WaitOnBacking(); @@ -158,12 +161,16 @@ namespace skyline::gpu { } }()}; - 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); + 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 += guest->GetLayerStride(); + bufferData += layerStride; + } if (stagingBuffer && cycle.lock() != pCycle) WaitOnFence(); @@ -263,12 +270,16 @@ namespace skyline::gpu { void Texture::CopyToGuest(u8 *hostBuffer) { auto guestOutput{mirror.data()}; - 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, format->GetSize(dimensions)); + 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 += guest->layerStride; + hostBuffer += layerStride; + } } Texture::TextureBufferCopy::TextureBufferCopy(std::shared_ptr texture, std::shared_ptr stagingBuffer) : texture(std::move(texture)), stagingBuffer(std::move(stagingBuffer)) {} @@ -289,6 +300,7 @@ namespace skyline::gpu { usage(usage), mipLevels(mipLevels), layerCount(layerCount), + layerStride(static_cast(format->GetSize(dimensions))), sampleCount(sampleCount) {} Texture::Texture(GPU &pGpu, GuestTexture pGuest) @@ -300,6 +312,7 @@ namespace skyline::gpu { 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))), sampleCount(vk::SampleCountFlagBits::e1), flags(gpu.traits.quirks.vkImageMutableFormatCostly ? vk::ImageCreateFlags{} : vk::ImageCreateFlagBits::eMutableFormat), usage(vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled) { @@ -485,7 +498,7 @@ namespace skyline::gpu { WaitOnFence(); if (tiling == vk::ImageTiling::eOptimal || !std::holds_alternative(backing)) { - auto size{format->GetSize(dimensions) * layerCount}; + auto size{layerStride * layerCount}; auto stagingBuffer{gpu.memory.AllocateStagingBuffer(size)}; auto lCycle{gpu.scheduler.Submit([&](vk::raii::CommandBuffer &commandBuffer) { @@ -519,7 +532,7 @@ namespace skyline::gpu { WaitOnFence(); if (tiling == vk::ImageTiling::eOptimal || !std::holds_alternative(backing)) { - auto size{format->GetSize(dimensions) * layerCount}; + auto size{layerStride * layerCount}; auto stagingBuffer{gpu.memory.AllocateStagingBuffer(size)}; CopyIntoStagingBuffer(commandBuffer, stagingBuffer); diff --git a/app/src/main/cpp/skyline/gpu/texture/texture.h b/app/src/main/cpp/skyline/gpu/texture/texture.h index 632c345a..a13e1326 100644 --- a/app/src/main/cpp/skyline/gpu/texture/texture.h +++ b/app/src/main/cpp/skyline/gpu/texture/texture.h @@ -238,15 +238,15 @@ namespace skyline::gpu { texture::Format format{}; texture::TileConfig tileConfig{}; texture::TextureType type{}; - u16 baseArrayLayer{}; - u16 layerCount{}; - 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 + u32 baseArrayLayer{}; + u32 layerCount{}; + 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 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, u16 baseArrayLayer = 0, u16 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) : mappings(mappings), dimensions(dimensions), format(format), @@ -257,7 +257,7 @@ namespace skyline::gpu { layerStride(layerStride), aspect(format->vkAspect) {} - GuestTexture(span mapping, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, texture::TextureType type, u16 baseArrayLayer = 0, u16 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) : mappings(1, mapping), dimensions(dimensions), format(format), @@ -269,10 +269,11 @@ namespace skyline::gpu { aspect(format->vkAspect) {} /** + * @note This should be used over accessing the `layerStride` member directly when desiring the actual layer stride for calculations as it will automatically handle it not being filled in * @note Requires `dimensions`, `format` and `tileConfig` to be filled in - * @return The size of a single layer with alignment in bytes + * @return The size of a single layer with layout alignment in bytes */ - u32 GetLayerSize(); + u32 GetLayerStride(); }; class TextureManager; @@ -417,6 +418,7 @@ namespace skyline::gpu { 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 vk::SampleCountFlagBits sampleCount; /** diff --git a/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp index eee43472..301ac24c 100644 --- a/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp +++ b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp @@ -345,7 +345,7 @@ namespace skyline::service::hosbinder { gpu::texture::Dimensions dimensions(surface.width, surface.height); gpu::GuestTexture guestTexture(span{}, dimensions, format, tileConfig, gpu::texture::TextureType::e2D); - guestTexture.mappings[0] = span(nvMapHandleObj->GetPointer() + surface.offset, guestTexture.GetLayerSize()); + guestTexture.mappings[0] = span(nvMapHandleObj->GetPointer() + surface.offset, guestTexture.GetLayerStride()); buffer.texture = state.gpu->texture.FindOrCreate(guestTexture)->texture; } diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_dma.cpp b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_dma.cpp index b1f380ab..08d99ccd 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_dma.cpp +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_dma.cpp @@ -86,7 +86,7 @@ namespace skyline::soc::gm20b::engine { gpu::texture::TileConfig{ .mode = gpu::texture::TileMode::Linear }, gpu::texture::TextureType::e2D}; - if (auto mappings{channelCtx.asCtx->gmmu.TranslateRange(*registers.offsetIn, srcTexture.GetLayerSize())}; mappings.size() == 1) { + if (auto mappings{channelCtx.asCtx->gmmu.TranslateRange(*registers.offsetIn, srcTexture.GetLayerStride())}; mappings.size() == 1) { srcTexture.mappings[0] = mappings[0]; } else { Logger::Warn("DMA for split textures is unimplemented!"); @@ -100,8 +100,8 @@ namespace skyline::soc::gm20b::engine { gpu::texture::TileConfig{ .mode = gpu::texture::TileMode::Block, .blockHeight = registers.dstSurface->blockSize.Height(), .blockDepth = 1 }, gpu::texture::TextureType::e2D}; - u64 dstLayerAddress{*registers.offsetOut + dstTexture.GetLayerSize() * registers.dstSurface->layer}; - if (auto mappings{channelCtx.asCtx->gmmu.TranslateRange(dstLayerAddress, dstTexture.GetLayerSize())}; mappings.size() == 1) { + u64 dstLayerAddress{*registers.offsetOut + dstTexture.GetLayerStride() * registers.dstSurface->layer}; + if (auto mappings{channelCtx.asCtx->gmmu.TranslateRange(dstLayerAddress, dstTexture.GetLayerStride())}; mappings.size() == 1) { dstTexture.mappings[0] = mappings[0]; } else { Logger::Warn("DMA for split textures is unimplemented!"); @@ -145,7 +145,7 @@ namespace skyline::soc::gm20b::engine { gpu::texture::TileConfig{ .mode = gpu::texture::TileMode::Block, .blockHeight = registers.srcSurface->blockSize.Height(), .blockDepth = 1 }, gpu::texture::TextureType::e2D}; - if (auto mappings{channelCtx.asCtx->gmmu.TranslateRange(*registers.offsetIn, srcTexture.GetLayerSize())}; mappings.size() == 1) { + if (auto mappings{channelCtx.asCtx->gmmu.TranslateRange(*registers.offsetIn, srcTexture.GetLayerStride())}; mappings.size() == 1) { srcTexture.mappings[0] = mappings[0]; } else { Logger::Warn("DMA for split textures is unimplemented!"); @@ -158,7 +158,7 @@ namespace skyline::soc::gm20b::engine { gpu::texture::TileConfig{ .mode = gpu::texture::TileMode::Linear }, gpu::texture::TextureType::e2D}; - if (auto mappings{channelCtx.asCtx->gmmu.TranslateRange(*registers.offsetOut, dstTexture.GetLayerSize())}; mappings.size() == 1) { + if (auto mappings{channelCtx.asCtx->gmmu.TranslateRange(*registers.offsetOut, dstTexture.GetLayerStride())}; mappings.size() == 1) { dstTexture.mappings[0] = mappings[0]; } else { Logger::Warn("DMA for split textures is unimplemented!");