1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <chrono>
#include <deque>
#include <memory>
#include <optional>
#include <span>
#include "video_core/texture_cache/texture_cache_base.h"
#include "shader_recompiler/shader_info.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/renderer_vulkan/vk_fsr3_upscaler.h"
#ifdef EGLANTINE_STREAMLINE
#include "video_core/renderer_vulkan/vk_streamline_dlss.h"
#endif
#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
#include "video_core/texture_cache/image_view_base.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Settings {
struct ResolutionScalingInfo;
}
namespace Vulkan {
using Common::SlotVector;
using VideoCommon::ImageId;
using VideoCommon::NUM_RT;
using VideoCommon::Region2D;
using VideoCore::Surface::PixelFormat;
class BlitImageHelper;
class DescriptorPool;
class Device;
class Image;
class ImageView;
class Framebuffer;
class RenderPassCache;
class StagingBufferPool;
class Scheduler;
class TextureCacheRuntime {
public:
explicit TextureCacheRuntime(const Device& device_, Scheduler& scheduler_,
MemoryAllocator& memory_allocator_,
StagingBufferPool& staging_buffer_pool_,
BlitImageHelper& blit_image_helper_,
RenderPassCache& render_pass_cache_,
DescriptorPool& descriptor_pool,
ComputePassDescriptorQueue& compute_pass_descriptor_queue);
void Finish();
StagingBufferRef UploadStagingBuffer(size_t size, bool deferred = false);
StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
void FreeDeferredStagingBuffer(StagingBufferRef& ref);
void TickFrame();
void FlushDeferredClear();
u64 GetDeviceLocalMemory() const;
u64 GetDeviceMemoryUsage() const;
/// GPU progress: the texture cache frees a deleted image once the GPU is past its last use.
[[nodiscard]] u64 CurrentTick() const noexcept;
[[nodiscard]] bool IsFree(u64 tick) const noexcept;
u64 GetDeviceMemoryBudget() const;
u64 GetDeviceMemorySlack() const;
bool CanReportMemoryUsage() const;
/// Images that did not fit the device memory budget so far (see MemoryAllocator).
u32 MemoryPressureEvents() const noexcept {
return memory_allocator.MemoryPressureEvents();
}
void LogMemoryStatistics() const;
/// True when ASTC is re-compressed to BC3 and opaque images may use BC1 instead.
bool UseOpaqueAstcFormat() const;
bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const;
[[nodiscard]] VkImage AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci);
void ReleaseMsaaScratchImage(VkImage image);
void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
/// Stands in for a DLSS evaluation: temporal upscale of input_color (render_size on the host)
/// into dst, with FSR 3.1 when given the depth, else with the temporal stand-in. False when dst
/// cannot take it.
bool TemporalUpscale(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& input_color,
ImageView& motion_vectors, ImageView* depth,
std::array<u32, 2> render_size, std::array<f32, 2> jitter,
std::array<f32, 2> motion_scale);
/// True when TemporalUpscale would use the depth (FSR 3.1 not forced off and not failed).
[[nodiscard]] bool WantsTemporalUpscaleDepth() const;
void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
void CopyImageMSAA(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
bool ShouldReinterpret(Image& dst, Image& src);
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view);
bool CanAccelerateImageUpload(Image&) const noexcept {
return false;
}
bool CanUploadMSAA() const noexcept {
return true;
}
void AccelerateImageUpload(Image&, const StagingBufferRef&,
std::span<const VideoCommon::SwizzleParameters>,
u32 z_start, u32 z_count);
/// Decodes only `layers` of an ASTC array; the staging buffer holds those layers in order.
void AccelerateImageUploadLayers(Image&, const StagingBufferRef&,
std::span<const VideoCommon::SwizzleParameters>,
std::span<const u32> layers);
void InsertUploadMemoryBarrier() {}
void TransitionImageLayout(Image& image);
bool HasBrokenTextureViewFormats() const noexcept {
// No known Vulkan driver has broken image views
return false;
}
bool HasNativeBgr() const noexcept {
// All known Vulkan drivers can natively handle BGR textures
return true;
}
[[nodiscard]] VkBuffer GetTemporaryBuffer(size_t needed_size);
struct ResolveShadow {
vk::Image image;
vk::ImageView view;
VkFormat format = VK_FORMAT_UNDEFINED;
VkExtent2D extent{};
u32 layers = 0;
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
bool up_to_date = false;
};
[[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
VkExtent2D extent, u32 layers,
VkImageAspectFlags aspect_mask);
[[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const;
void InvalidateResolveShadow(VkImage msaa_image);
void MarkResolveShadowUpToDate(VkImage msaa_image);
void EraseResolveShadow(VkImage msaa_image);
std::span<const VkFormat> ViewFormats(PixelFormat format) {
return view_formats[static_cast<std::size_t>(format)];
}
void BarrierFeedbackLoop();
bool IsFormatDitherable(VideoCore::Surface::PixelFormat format);
bool IsFormatScalable(VideoCore::Surface::PixelFormat format);
VkFormat GetSupportedFormat(VkFormat requested_format, VkFormatFeatureFlags required_features) const;
const Device& device;
Scheduler& scheduler;
MemoryAllocator& memory_allocator;
StagingBufferPool& staging_buffer_pool;
BlitImageHelper& blit_image_helper;
RenderPassCache& render_pass_cache;
DescriptorPool& descriptor_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
/// FSR 3.1 in place of a DLSS evaluation (TemporalUpscale), created on first use.
bool UpscaleWithFsr3(Framebuffer* dst_framebuffer, ImageView& input_color,
ImageView& motion_vectors, ImageView& depth,
std::array<u32, 2> render_size, std::array<f32, 2> jitter,
std::array<f32, 2> motion_scale);
std::unique_ptr<Fsr3Upscaler> fsr3_upscaler;
bool fsr3_unsupported{};
std::optional<std::chrono::steady_clock::time_point> fsr3_last_evaluation;
/// Draws an upscaler's RGBA16F output into the DLSS output and returns it to GENERAL.
void DrawUpscaledOutput(Framebuffer* dst_framebuffer, VkImageView view, VkImage image,
VkSampler sampler);
#ifdef EGLANTINE_STREAMLINE
/// NVIDIA's DLSS (through NVIDIA Streamline) in place of the game's,
/// before FSR 3.1, when the setting is on. Returns false to fall back to FSR 3.1.
bool UpscaleWithDlss(Framebuffer* dst_framebuffer, ImageView& input_color,
ImageView& motion_vectors, ImageView& depth,
std::array<u32, 2> render_size, std::array<f32, 2> jitter,
std::array<f32, 2> motion_scale, bool reset);
std::unique_ptr<StreamlineDlssUpscaler> dlss_upscaler;
bool dlss_unsupported{};
#endif
/// TemporalUpscale's history: its previous result (images[current]) and the image the next
/// result is stored to.
struct TemporalHistory {
std::array<vk::Image, 2> images;
std::array<vk::ImageView, 2> views;
size_t current{};
VkFormat format{};
VkExtent2D extent{};
} temporal_history;
std::optional<ASTCDecoderPass> astc_decoder_pass;
std::optional<BcEncoderPass> bc_encoder_pass;
/// ASTC decoded on the GPU and re-compressed to BC1/BC3 there (accelerate_astc=Gpu with
/// astc_recompression=Bc1/Bc3): decode into a temporary RGBA8 image, encode, copy.
void TranscodeAstcToBcn(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
/// Temporary decode images, reusable once the GPU is past `tick`.
struct PendingTranscode {
u64 tick;
vk::Image image;
std::vector<vk::ImageView> views;
u32 width;
u32 height;
u32 levels;
u32 layers;
u32 unused_frames;
};
std::deque<PendingTranscode> pending_transcodes;
/// Decode images the GPU is done with, for the next texture of the same shape.
std::vector<PendingTranscode> free_transcodes;
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
const Settings::ResolutionScalingInfo& resolution;
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
static constexpr size_t indexing_slots = 8 * sizeof(size_t);
std::array<vk::Buffer, indexing_slots> buffers{};
struct MsaaScratchKey {
VkFormat format;
VkImageType type;
u32 width;
u32 height;
u32 depth;
u32 levels;
u32 layers;
VkImageUsageFlags usage;
VkImageCreateFlags flags;
bool operator==(const MsaaScratchKey&) const noexcept = default;
};
struct MsaaScratchImage {
MsaaScratchKey key;
vk::Image image;
u64 tick;
u32 unused_frames;
};
std::vector<MsaaScratchImage> msaa_scratch_images;
::Common::unordered_map<VkImage, ResolveShadow> resolve_shadows;
std::vector<std::pair<u64, ResolveShadow>> pending_resolve_shadows;
};
class Framebuffer {
public:
explicit Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
ImageView* depth_buffer, const VideoCommon::RenderTargets& key);
explicit Framebuffer(TextureCacheRuntime& runtime, ImageView* color_buffer,
ImageView* depth_buffer, VkExtent2D extent, bool is_rescaled);
~Framebuffer();
Framebuffer(const Framebuffer&) = delete;
Framebuffer& operator=(const Framebuffer&) = delete;
Framebuffer(Framebuffer&&) = default;
Framebuffer& operator=(Framebuffer&&) = default;
void CreateFramebuffer(TextureCacheRuntime& runtime,
std::span<ImageView*, NUM_RT> color_buffers, ImageView* depth_buffer,
bool is_rescaled = false);
[[nodiscard]] VkFramebuffer Handle() const noexcept {
return *framebuffer;
}
[[nodiscard]] VkRenderPass RenderPass() const noexcept {
return renderpass;
}
[[nodiscard]] const RenderPassKey& RenderPassKeyBase() const noexcept {
return render_pass_key;
}
[[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear,
u32 color_discard_mask,
bool depth_stencil_discard) const;
[[nodiscard]] VkExtent2D RenderArea() const noexcept {
return render_area;
}
[[nodiscard]] VkSampleCountFlagBits Samples() const noexcept {
return samples;
}
[[nodiscard]] u32 NumColorBuffers() const noexcept {
return num_color_buffers;
}
[[nodiscard]] u32 NumImages() const noexcept {
return num_images;
}
[[nodiscard]] const std::array<VkImage, 9>& Images() const noexcept {
return images;
}
[[nodiscard]] const std::array<VkImageSubresourceRange, 9>& ImageRanges() const noexcept {
return image_ranges;
}
[[nodiscard]] bool HasAspectColorBit(size_t index) const noexcept {
return (image_ranges.at(rt_map[index]).aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
}
[[nodiscard]] bool HasAspectDepthBit() const noexcept {
return has_depth;
}
[[nodiscard]] bool HasAspectStencilBit() const noexcept {
return has_stencil;
}
[[nodiscard]] bool IsRescaled() const noexcept {
return is_rescaled;
}
[[nodiscard]] bool DiscardsMsaaColor() const noexcept {
return discard_msaa_color;
}
[[nodiscard]] bool DiscardsMsaaDepthStencil() const noexcept {
return discard_msaa_depth_stencil;
}
/// Records that a render pass has begun, so its resolve attachments will hold valid contents
/// once it ends.
void MarkResolveShadowsUpToDate() const;
private:
static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8;
vk::Framebuffer framebuffer;
VkRenderPass renderpass{};
VkExtent2D render_area{};
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
u32 num_color_buffers = 0;
u32 num_images = 0;
std::array<VkImage, 9> images{};
std::array<VkImageSubresourceRange, 9> image_ranges{};
std::array<size_t, NUM_RT> rt_map{};
bool has_depth{};
bool has_stencil{};
bool is_rescaled{};
std::array<VkImage, 9> resolve_shadow_images{};
u32 num_resolve_shadows = 0;
TextureCacheRuntime* runtime_ptr{nullptr};
RenderPassKey render_pass_key{};
RenderPassCache* render_pass_cache{nullptr};
bool discard_msaa_color{};
bool discard_msaa_depth_stencil{};
mutable std::array<u32, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_keys{};
mutable std::array<VkRenderPass, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_render_passes{};
mutable u32 num_memoized_variants{};
};
class Image : public VideoCommon::ImageBase {
public:
explicit Image(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, GPUVAddr gpu_addr,
VAddr cpu_addr);
explicit Image(const VideoCommon::NullImageParams&);
~Image();
Image(const Image&) = delete;
Image& operator=(const Image&) = delete;
Image(Image&&) = default;
Image& operator=(Image&&) = default;
void UploadMemory(VkBuffer buffer, VkDeviceSize offset,
std::span<const VideoCommon::BufferImageCopy> copies);
void UploadMemory(const StagingBufferRef& map,
std::span<const VideoCommon::BufferImageCopy> copies);
void DownloadMemory(VkBuffer buffer, size_t offset,
std::span<const VideoCommon::BufferImageCopy> copies);
void DownloadMemory(std::span<VkBuffer> buffers, std::span<size_t> offsets,
std::span<const VideoCommon::BufferImageCopy> copies);
void DownloadMemory(const StagingBufferRef& map,
std::span<const VideoCommon::BufferImageCopy> copies);
void AllocateComputeUnswizzleImage();
[[nodiscard]] VkImage Handle() const noexcept {
return *(this->*current_image);
}
[[nodiscard]] VkImageAspectFlags AspectMask() const noexcept {
return aspect_mask;
}
[[nodiscard]] VkImageUsageFlags UsageFlags() const noexcept {
return (this->*current_image).UsageFlags();
}
/// Where the image's memory lies, for the collector (TextureCache::RunGarbageCollector).
[[nodiscard]] vk::Image::MemoryBlock MemoryBlock() const noexcept {
return original_image.GetMemoryBlock();
}
/// Returns true when the image is already initialized and mark it as initialized
[[nodiscard]] bool ExchangeInitialization() noexcept {
return std::exchange(initialized, true);
}
VkImageView StorageImageView(s32 level) noexcept;
/// A storage view of one layer of a host level (decoding only some layers of an array).
VkImageView StorageImageLayerView(s32 level, u32 layer);
/// SparseLayers images: gives `layers` device memory where they have none yet. Returns the
/// layers that have memory afterwards (a cap per image can leave some out).
std::vector<u32> BindLayers(std::span<const u32> layers);
/// SparseTiles images: gives the host sparse blocks under `rects` (and the mip tail, for
/// levels in it) device memory where they have none yet. False if that failed.
bool BindTiles(std::span<const VideoCommon::LevelRect> rects);
/// SparseTiles images: the host sparse block, in texels, and the first level of the mip tail.
u32 SparseTileWidth() const noexcept {
return sparse_layers.tile_width;
}
u32 SparseTileHeight() const noexcept {
return sparse_layers.tile_height;
}
s32 SparseTailLevel() const noexcept {
return static_cast<s32>(sparse_layers.tail_level);
}
/// SparseTiles images whose blocks without memory of their own share one zeroed block.
bool SparseTilesShareZeroBlock() const noexcept {
return sparse_layers.zero_block != VK_NULL_HANDLE;
}
/// SparseTiles images sharing a zeroed block, before the GPU renders into them: every block
/// gets memory of its own, all cleared (the caller loads the data again). False on failure.
bool BackTilesWhole();
/// Device memory BindLayers has bound to the image's layers (an unbacked image has no other).
u64 SparseBoundBytes() const noexcept {
return sparse_layers.bound_bytes;
}
bool IsRescaled() const noexcept;
bool ScaleUp(bool ignore = false);
bool ScaleDown(bool ignore = false);
u64 allocation_tick;
friend class BlockLinearUnswizzle3DPass;
private:
bool BlitScaleHelper(bool scale_up);
bool NeedsScaleHelper() const;
Scheduler* scheduler{};
TextureCacheRuntime* runtime{};
/// Memory bound to the layers of a SparseLayers image. Declared before the images, so it is
/// released after them.
struct SparseLayerMemory {
SparseLayerMemory() = default;
SparseLayerMemory(const SparseLayerMemory&) = delete;
SparseLayerMemory& operator=(const SparseLayerMemory&) = delete;
SparseLayerMemory(SparseLayerMemory&& other) noexcept {
*this = std::move(other);
}
SparseLayerMemory& operator=(SparseLayerMemory&& other) noexcept {
Release();
allocator = std::exchange(other.allocator, nullptr);
requirements = other.requirements;
sparse = other.sparse;
width = other.width;
height = other.height;
levels = other.levels;
tile_width = other.tile_width;
tile_height = other.tile_height;
layer_bytes = other.layer_bytes;
layer_memory = std::move(other.layer_memory);
other.layer_memory.clear();
level_tiles = std::move(other.level_tiles);
other.level_tiles.clear();
tail_level = other.tail_level;
zero_block = std::exchange(other.zero_block, VK_NULL_HANDLE);
tail_memory = std::exchange(other.tail_memory, VK_NULL_HANDLE);
bound_bytes = other.bound_bytes;
warned = other.warned;
return *this;
}
~SparseLayerMemory() {
Release();
}
void Release() noexcept {
if (allocator == nullptr) {
return;
}
for (const VmaAllocation allocation : layer_memory) {
allocator->FreeSparseMemory(allocation);
}
for (const auto& tiles : level_tiles) {
for (const VmaAllocation allocation : tiles) {
allocator->FreeSparseMemory(allocation);
}
}
allocator->FreeSparseMemory(tail_memory);
allocator->FreeSparseMemory(zero_block);
zero_block = VK_NULL_HANDLE;
layer_memory.clear();
level_tiles.clear();
tail_memory = VK_NULL_HANDLE;
}
const MemoryAllocator* allocator{};
VkMemoryRequirements requirements{};
VkSparseImageMemoryRequirements sparse{};
u32 width{}; ///< Host level 0
u32 height{};
u32 levels{}; ///< Host levels
u32 tile_width{}; ///< Sparse block, in texels
u32 tile_height{};
VkDeviceSize layer_bytes{}; ///< One layer's levels before the mip tail, and its own tail
std::vector<VmaAllocation> layer_memory; ///< Per layer, null until bound
/// SparseTiles: per level before the mip tail, per sparse block (rows), null until bound
std::vector<std::vector<VmaAllocation>> level_tiles;
u32 tail_level{}; ///< SparseTiles: first level of the mip tail
/// SparseTiles: the zeroed block every block without memory of its own is bound to, where
/// the driver can't sample unbound memory
VmaAllocation zero_block{};
VmaAllocation tail_memory{}; ///< The mip tail all layers share, if one
VkDeviceSize bound_bytes{};
bool warned{};
};
SparseLayerMemory sparse_layers;
/// Reads the image's sparse requirements; false when it can't be bound by layer.
bool InitSparseLayers();
/// Reads the image's sparse requirements; false when it can't be bound by sparse block.
bool InitSparseTiles();
/// Binds one zeroed block to every block of a SparseTiles image (and memory to its mip tail).
bool BindZeroBlock();
/// Records a clear of the whole image to zero.
void ClearTiles();
vk::Image original_image;
vk::Image scaled_image;
vk::Buffer compute_unswizzle_buffer;
VkDeviceSize compute_unswizzle_buffer_size = 0;
bool has_compute_unswizzle_buffer = false;
void AllocateComputeUnswizzleBuffer(u32 max_slices);
// Use a pointer to field because it is relative, so that the object can be
// moved without breaking the reference.
vk::Image Image::*current_image{};
std::vector<vk::ImageView> storage_image_views;
std::vector<vk::ImageView> storage_layer_views; ///< By host level * layers + layer
VkImageAspectFlags aspect_mask = 0;
bool initialized = false;
std::optional<Framebuffer> scale_framebuffer;
std::optional<Framebuffer> normal_framebuffer;
std::unique_ptr<ImageView> scale_view;
std::unique_ptr<ImageView> normal_view;
};
class ImageView : public VideoCommon::ImageViewBase {
public:
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
const SlotVector<Image>&);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
const VideoCommon::ImageViewInfo&, GPUVAddr);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&);
~ImageView();
ImageView(const ImageView&) = delete;
ImageView& operator=(const ImageView&) = delete;
ImageView(ImageView&&) = default;
ImageView& operator=(ImageView&&) = default;
[[nodiscard]] VkImageView DepthView();
[[nodiscard]] VkImageView StencilView();
[[nodiscard]] VkImageView ColorView();
[[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type,
Shader::ImageFormat image_format);
[[nodiscard]] bool IsRescaled() const noexcept;
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
return *image_views[static_cast<size_t>(texture_type)];
}
[[nodiscard]] VkImage ImageHandle() const noexcept {
return image_handle;
}
[[nodiscard]] VkImageView RenderTarget() const noexcept {
return render_target;
}
[[nodiscard]] VkSampleCountFlagBits Samples() const noexcept {
return samples;
}
[[nodiscard]] bool SupportsDepthComparison() const noexcept {
return supports_depth_comparison;
}
[[nodiscard]] bool RequiresBorderColorFormat() const noexcept {
return requires_border_color_format;
}
[[nodiscard]] bool SupportsMinmaxFilter() const noexcept {
return supports_minmax_filter;
}
[[nodiscard]] const VkComponentMapping& Swizzle() const noexcept {
return swizzle_mapping;
}
[[nodiscard]] bool HasIdentitySwizzle() const noexcept {
return has_identity_swizzle;
}
[[nodiscard]] GPUVAddr GpuAddr() const noexcept {
return gpu_addr;
}
[[nodiscard]] u32 BufferSize() const noexcept {
return buffer_size;
}
private:
struct StorageViews {
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> signeds;
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> unsigneds;
};
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask,
std::optional<Shader::TextureType> texture_type = std::nullopt);
const Device* device = nullptr;
const SlotVector<Image>* slot_images = nullptr;
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views;
std::optional<StorageViews> storage_views;
vk::ImageView typeless_storage_view;
vk::ImageView depth_view;
vk::ImageView stencil_view;
vk::ImageView color_view;
vk::Image null_image;
VkImage image_handle = VK_NULL_HANDLE;
VkImageView render_target = VK_NULL_HANDLE;
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
u32 buffer_size = 0;
VkComponentMapping swizzle_mapping{};
bool supports_depth_comparison = false;
bool requires_border_color_format = false;
bool supports_minmax_filter = false;
bool has_identity_swizzle = true;
};
class ImageAlloc : public VideoCommon::ImageAllocBase {};
class CustomBorderColorBudget {
public:
CustomBorderColorBudget() = default;
~CustomBorderColorBudget();
CustomBorderColorBudget(const CustomBorderColorBudget&) = delete;
CustomBorderColorBudget& operator=(const CustomBorderColorBudget&) = delete;
CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept;
CustomBorderColorBudget& operator=(CustomBorderColorBudget&& rhs) noexcept;
bool TryAcquire(const Device& device, size_t count);
private:
void Release() noexcept;
const Device* device_ptr = nullptr;
size_t held = 0;
};
class Sampler {
public:
explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
[[nodiscard]] VkSampler Handle() const noexcept {
return *variants.front().sampler;
}
[[nodiscard]] VkSampler HandleFor(const ImageView& image_view, bool is_depth);
private:
struct VariantKey {
bool reduce_anisotropy;
bool force_nearest;
bool drop_reduction;
bool drop_custom_border;
bool srgb_border;
std::array<VkComponentSwizzle, 4> swizzle;
bool operator==(const VariantKey&) const noexcept = default;
[[nodiscard]] bool HasSwizzle() const noexcept {
return swizzle != std::array<VkComponentSwizzle, 4>{};
}
};
struct Variant {
VariantKey key;
vk::Sampler sampler;
};
static constexpr size_t MAX_VARIANTS = 32;
[[nodiscard]] VariantKey MakeKey(const ImageView& image_view, bool is_depth) const noexcept;
[[nodiscard]] VkSampler Find(const VariantKey& key) const noexcept;
VkSampler Emplace(VariantKey key);
CustomBorderColorBudget custom_border_color_budget;
std::vector<Variant> variants;
const Device* device_ptr{nullptr};
VkSamplerCreateInfo base_ci{};
VkSamplerReductionModeEXT reduction_mode{VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT};
std::array<float, 4> border_color{};
std::array<float, 4> srgb_border_color{};
f32 default_anisotropy{1.0f};
bool has_added_anisotropy{};
bool has_linear_filtering{};
bool has_depth_comparison{};
bool has_minmax_reduction{};
bool has_custom_border_colors{};
bool has_srgb_border_color{};
bool needs_swizzle_mapping{};
};
struct TextureCacheParams {
static constexpr bool ENABLE_VALIDATION = true;
static constexpr bool FRAMEBUFFER_BLITS = false;
static constexpr bool HAS_EMULATED_COPIES = false;
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
static constexpr bool HAS_MSAA_DOWNLOADS = true;
using Runtime = Vulkan::TextureCacheRuntime;
using Image = Vulkan::Image;
using ImageAlloc = Vulkan::ImageAlloc;
using ImageView = Vulkan::ImageView;
using Sampler = Vulkan::Sampler;
using Framebuffer = Vulkan::Framebuffer;
using AsyncBuffer = Vulkan::StagingBufferRef;
using BufferType = VkBuffer;
};
using TextureCache = VideoCommon::TextureCache<TextureCacheParams>;
} // namespace Vulkan
|