aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/texture_cache')
-rw-r--r--src/video_core/texture_cache/image_base.h3
-rw-r--r--src/video_core/texture_cache/texture_cache.h86
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h6
3 files changed, 91 insertions, 4 deletions
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index cfb839d17e..04254fb417 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -53,6 +53,9 @@ enum class ImageFlagBits : u32 {
/// Over Switch 2 sparse blocks nobody wrote: all zeros, so the host image has no memory
/// (Unbacked) until the first write, which makes a backed image replace it.
UnwrittenSparse = 1 << 21,
+ /// A large array over Switch 2 sparse blocks: unbacked, each layer gets device memory when
+ /// something is written to it (Image::BindLayers) and only those layers are uploaded.
+ SparseLayers = 1 << 22,
};
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 168df50c8b..3de82d2eb4 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -7,6 +7,7 @@
#pragma once
#include <algorithm>
+#include <numeric>
#include <cstdlib>
#include <limits>
#include <map>
@@ -1306,6 +1307,10 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
TrackImage(image, image_id);
}
+ if (True(image.flags & ImageFlagBits::SparseLayers)) {
+ RefreshSparseLayers(image);
+ return;
+ }
if (!image.dirty_layers.empty() && UploadWrittenLayers(image)) {
runtime.InsertUploadMemoryBarrier();
return;
@@ -1354,6 +1359,9 @@ bool TextureCache<P>::UploadsByLayer(const ImageBase& image) const noexcept {
// Title 1's terrain arrays (8192x8192x68 ASTC, 1.45 GB each) get a few layers streamed in at a
// time; re-decoding all of them for each took a 2 GiB staging buffer and stalled the GPU.
if constexpr (requires { &Runtime::AccelerateImageUploadLayers; }) {
+ if (True(image.flags & ImageFlagBits::SparseLayers)) {
+ return true;
+ }
return True(image.flags & ImageFlagBits::AcceleratedUpload) &&
image.info.type == ImageType::e2D && image.info.resources.layers > 1 &&
image.guest_size_bytes >= LayerUploadMinSize &&
@@ -1401,6 +1409,71 @@ bool TextureCache<P>::UploadWrittenLayers(Image& image) {
if (layers.empty() || layers.size() == num_layers) {
return false;
}
+ UploadLayers(image, layers);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+template <class P>
+void TextureCache<P>::RefreshSparseLayers(Image& image) {
+ if constexpr (requires { &Image::BindLayers; }) {
+ // The layers written since the last upload, or, for a new image, every layer any of whose
+ // blocks has been written.
+ const u32 num_layers = static_cast<u32>(image.info.resources.layers);
+ const u64 stride = image.info.layer_stride;
+ std::vector<u32> layers;
+ for (u32 layer = 0; layer < num_layers; ++layer) {
+ const bool written =
+ image.dirty_layers.empty()
+ ? !Tegra::Nx2SparseUnwritten(image.gpu_addr + layer * stride, stride)
+ : ((image.dirty_layers[layer / 64] >> (layer % 64)) & 1) != 0;
+ if (written) {
+ layers.push_back(layer);
+ }
+ }
+ image.dirty_layers.clear();
+ const std::vector<u32> bound = layers.empty() ? std::vector<u32>{} : image.BindLayers(layers);
+ if (bound.empty()) {
+ runtime.TransitionImageLayout(image);
+ return;
+ }
+ UploadLayers(image, bound);
+ runtime.InsertUploadMemoryBarrier();
+ }
+}
+
+template <class P>
+void TextureCache<P>::UploadLayers(Image& image, std::span<const u32> layers) {
+ if constexpr (requires { &Runtime::AccelerateImageUploadLayers; }) {
+ if (False(image.flags & ImageFlagBits::AcceleratedUpload)) {
+ // Plain formats: each layer unswizzled on its own into its layer of the image.
+ ImageInfo layer_info = image.info;
+ layer_info.resources.layers = 1;
+ const size_t layer_size = CalculateUnswizzledSizeBytes(layer_info);
+ auto staging = runtime.UploadStagingBuffer(layer_size * layers.size());
+ std::vector<BufferImageCopy> copies;
+ for (size_t i = 0; i < layers.size(); ++i) {
+ const GPUVAddr layer_addr = image.gpu_addr + u64{layers[i]} * image.info.layer_stride;
+ Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::UnsafeRead>
+ swizzle_data(*gpu_memory, layer_addr, image.info.layer_stride,
+ &swizzle_data_buffer);
+ auto layer_copies =
+ UnswizzleImage(*gpu_memory, layer_addr, layer_info, swizzle_data,
+ staging.mapped_span.subspan(i * layer_size, layer_size));
+ for (BufferImageCopy copy : layer_copies) {
+ copy.buffer_offset += i * layer_size;
+ copy.image_subresource.base_layer = static_cast<s32>(layers[i]);
+ copies.push_back(copy);
+ }
+ }
+ image.UploadMemory(staging, copies);
+ ++images_uploaded;
+ uploaded_bytes += u64{image.info.layer_stride} * layers.size();
+ ++layer_uploads;
+ return;
+ }
// Each layer's mip chain from the first level the host keeps on (as UploadImageContents).
auto uploads = FullUploadSwizzles(image.info);
const size_t level_offset = uploads[image.host_level_offset].buffer_offset;
@@ -1420,9 +1493,6 @@ bool TextureCache<P>::UploadWrittenLayers(Image& image) {
++images_uploaded;
uploaded_bytes += tail * layers.size();
++layer_uploads;
- return true;
- } else {
- return false;
}
}
@@ -2524,7 +2594,8 @@ ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const Imag
if (const ImageViewId image_view_id = image.FindView(info); image_view_id) {
return image_view_id;
}
- if (True(image.flags & ImageFlagBits::Unbacked)) {
+ if (True(image.flags & ImageFlagBits::Unbacked) &&
+ False(image.flags & ImageFlagBits::SparseLayers)) {
// How the game uses an image too large to back (e.g. a sparse virtual texture).
LOG_INFO(HW_GPU, "View of unbacked image {:#x}: type {} format {} levels {}+{} layers {}+{}",
image.gpu_addr, static_cast<u32>(info.type), static_cast<u32>(info.format),
@@ -2924,6 +2995,13 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool
}
}
if (is_modification) {
+ if constexpr (requires { &Image::BindLayers; }) {
+ if (True(image.flags & ImageFlagBits::SparseLayers)) {
+ std::vector<u32> layers(static_cast<size_t>(image.info.resources.layers));
+ std::iota(layers.begin(), layers.end(), 0U);
+ image.BindLayers(layers);
+ }
+ }
if (True(image.flags & ImageFlagBits::UnwrittenSparse) &&
False(image.flags & ImageFlagBits::Remapped)) {
// The GPU renders into it: these writes are lost, later uses get a backed image.
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index f4cc89f2ba..11e95f98b3 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -167,6 +167,12 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
/// Uploads the image's dirty layers. False when it needs a full upload instead.
bool UploadWrittenLayers(Image& image);
+ /// Gives the written layers of a SparseLayers image memory and uploads them.
+ void RefreshSparseLayers(Image& image);
+
+ /// Uploads `layers` of the image from guest memory.
+ void UploadLayers(Image& image, std::span<const u32> layers);
+
struct PendingUnswizzle {
ImageId image_id;
VideoCommon::ImageInfo info;