texture_cache.h 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509
  1. // Copyright 2019 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include "video_core/dirty_flags.h"
  6. #include "video_core/texture_cache/samples_helper.h"
  7. #include "video_core/texture_cache/texture_cache_base.h"
  8. namespace VideoCommon {
  9. using Tegra::Texture::SwizzleSource;
  10. using Tegra::Texture::TextureType;
  11. using Tegra::Texture::TICEntry;
  12. using Tegra::Texture::TSCEntry;
  13. using VideoCore::Surface::GetFormatType;
  14. using VideoCore::Surface::IsCopyCompatible;
  15. using VideoCore::Surface::PixelFormat;
  16. using VideoCore::Surface::PixelFormatFromDepthFormat;
  17. using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
  18. using VideoCore::Surface::SurfaceType;
  19. using namespace Common::Literals;
  20. template <class P>
  21. TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_,
  22. Tegra::Engines::Maxwell3D& maxwell3d_,
  23. Tegra::Engines::KeplerCompute& kepler_compute_,
  24. Tegra::MemoryManager& gpu_memory_)
  25. : runtime{runtime_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
  26. kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_} {
  27. // Configure null sampler
  28. TSCEntry sampler_descriptor{};
  29. sampler_descriptor.min_filter.Assign(Tegra::Texture::TextureFilter::Linear);
  30. sampler_descriptor.mag_filter.Assign(Tegra::Texture::TextureFilter::Linear);
  31. sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear);
  32. sampler_descriptor.cubemap_anisotropy.Assign(1);
  33. // Make sure the first index is reserved for the null resources
  34. // This way the null resource becomes a compile time constant
  35. void(slot_image_views.insert(runtime, NullImageParams{}));
  36. void(slot_samplers.insert(runtime, sampler_descriptor));
  37. deletion_iterator = slot_images.begin();
  38. if constexpr (HAS_DEVICE_MEMORY_INFO) {
  39. const auto device_memory = runtime.GetDeviceLocalMemory();
  40. const u64 possible_expected_memory = (device_memory * 3) / 10;
  41. const u64 possible_critical_memory = (device_memory * 6) / 10;
  42. expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY);
  43. critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY);
  44. minimum_memory = 0;
  45. } else {
  46. // on OGL we can be more conservatives as the driver takes care.
  47. expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
  48. critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
  49. minimum_memory = expected_memory;
  50. }
  51. }
  52. template <class P>
  53. void TextureCache<P>::RunGarbageCollector() {
  54. const bool high_priority_mode = total_used_memory >= expected_memory;
  55. const bool aggressive_mode = total_used_memory >= critical_memory;
  56. const u64 ticks_to_destroy = high_priority_mode ? 60 : 100;
  57. int num_iterations = aggressive_mode ? 256 : (high_priority_mode ? 128 : 64);
  58. for (; num_iterations > 0; --num_iterations) {
  59. if (deletion_iterator == slot_images.end()) {
  60. deletion_iterator = slot_images.begin();
  61. if (deletion_iterator == slot_images.end()) {
  62. break;
  63. }
  64. }
  65. auto [image_id, image_tmp] = *deletion_iterator;
  66. Image* image = image_tmp; // fix clang error.
  67. const bool is_alias = True(image->flags & ImageFlagBits::Alias);
  68. const bool is_bad_overlap = True(image->flags & ImageFlagBits::BadOverlap);
  69. const bool must_download = image->IsSafeDownload();
  70. bool should_care = is_bad_overlap || is_alias || (high_priority_mode && !must_download);
  71. const u64 ticks_needed =
  72. is_bad_overlap
  73. ? ticks_to_destroy >> 4
  74. : ((should_care && aggressive_mode) ? ticks_to_destroy >> 1 : ticks_to_destroy);
  75. should_care |= aggressive_mode;
  76. if (should_care && image->frame_tick + ticks_needed < frame_tick) {
  77. if (is_bad_overlap) {
  78. const bool overlap_check = std::ranges::all_of(
  79. image->overlapping_images, [&, image](const ImageId& overlap_id) {
  80. auto& overlap = slot_images[overlap_id];
  81. return overlap.frame_tick >= image->frame_tick;
  82. });
  83. if (!overlap_check) {
  84. ++deletion_iterator;
  85. continue;
  86. }
  87. }
  88. if (!is_bad_overlap && must_download) {
  89. const bool alias_check = std::ranges::none_of(
  90. image->aliased_images, [&, image](const AliasedImage& alias) {
  91. auto& alias_image = slot_images[alias.id];
  92. return (alias_image.frame_tick < image->frame_tick) ||
  93. (alias_image.modification_tick < image->modification_tick);
  94. });
  95. if (alias_check) {
  96. auto map = runtime.DownloadStagingBuffer(image->unswizzled_size_bytes);
  97. const auto copies = FullDownloadCopies(image->info);
  98. image->DownloadMemory(map, copies);
  99. runtime.Finish();
  100. SwizzleImage(gpu_memory, image->gpu_addr, image->info, copies, map.mapped_span);
  101. }
  102. }
  103. if (True(image->flags & ImageFlagBits::Tracked)) {
  104. UntrackImage(*image, image_id);
  105. }
  106. UnregisterImage(image_id);
  107. DeleteImage(image_id);
  108. if (is_bad_overlap) {
  109. ++num_iterations;
  110. }
  111. }
  112. ++deletion_iterator;
  113. }
  114. }
  115. template <class P>
  116. void TextureCache<P>::TickFrame() {
  117. if (Settings::values.use_caches_gc.GetValue() && total_used_memory > minimum_memory) {
  118. RunGarbageCollector();
  119. }
  120. sentenced_images.Tick();
  121. sentenced_framebuffers.Tick();
  122. sentenced_image_view.Tick();
  123. ++frame_tick;
  124. }
  125. template <class P>
  126. const typename P::ImageView& TextureCache<P>::GetImageView(ImageViewId id) const noexcept {
  127. return slot_image_views[id];
  128. }
  129. template <class P>
  130. typename P::ImageView& TextureCache<P>::GetImageView(ImageViewId id) noexcept {
  131. return slot_image_views[id];
  132. }
  133. template <class P>
  134. void TextureCache<P>::MarkModification(ImageId id) noexcept {
  135. MarkModification(slot_images[id]);
  136. }
  137. template <class P>
  138. void TextureCache<P>::FillGraphicsImageViews(std::span<const u32> indices,
  139. std::span<ImageViewId> image_view_ids) {
  140. FillImageViews(graphics_image_table, graphics_image_view_ids, indices, image_view_ids);
  141. }
  142. template <class P>
  143. void TextureCache<P>::FillComputeImageViews(std::span<const u32> indices,
  144. std::span<ImageViewId> image_view_ids) {
  145. FillImageViews(compute_image_table, compute_image_view_ids, indices, image_view_ids);
  146. }
  147. template <class P>
  148. typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
  149. if (index > graphics_sampler_table.Limit()) {
  150. LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
  151. return &slot_samplers[NULL_SAMPLER_ID];
  152. }
  153. const auto [descriptor, is_new] = graphics_sampler_table.Read(index);
  154. SamplerId& id = graphics_sampler_ids[index];
  155. if (is_new) {
  156. id = FindSampler(descriptor);
  157. }
  158. return &slot_samplers[id];
  159. }
  160. template <class P>
  161. typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
  162. if (index > compute_sampler_table.Limit()) {
  163. LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
  164. return &slot_samplers[NULL_SAMPLER_ID];
  165. }
  166. const auto [descriptor, is_new] = compute_sampler_table.Read(index);
  167. SamplerId& id = compute_sampler_ids[index];
  168. if (is_new) {
  169. id = FindSampler(descriptor);
  170. }
  171. return &slot_samplers[id];
  172. }
  173. template <class P>
  174. void TextureCache<P>::SynchronizeGraphicsDescriptors() {
  175. using SamplerIndex = Tegra::Engines::Maxwell3D::Regs::SamplerIndex;
  176. const bool linked_tsc = maxwell3d.regs.sampler_index == SamplerIndex::ViaHeaderIndex;
  177. const u32 tic_limit = maxwell3d.regs.tic.limit;
  178. const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d.regs.tsc.limit;
  179. if (graphics_sampler_table.Synchornize(maxwell3d.regs.tsc.Address(), tsc_limit)) {
  180. graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
  181. }
  182. if (graphics_image_table.Synchornize(maxwell3d.regs.tic.Address(), tic_limit)) {
  183. graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
  184. }
  185. }
  186. template <class P>
  187. void TextureCache<P>::SynchronizeComputeDescriptors() {
  188. const bool linked_tsc = kepler_compute.launch_description.linked_tsc;
  189. const u32 tic_limit = kepler_compute.regs.tic.limit;
  190. const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute.regs.tsc.limit;
  191. const GPUVAddr tsc_gpu_addr = kepler_compute.regs.tsc.Address();
  192. if (compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) {
  193. compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
  194. }
  195. if (compute_image_table.Synchornize(kepler_compute.regs.tic.Address(), tic_limit)) {
  196. compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
  197. }
  198. }
  199. template <class P>
  200. void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
  201. using namespace VideoCommon::Dirty;
  202. auto& flags = maxwell3d.dirty.flags;
  203. if (!flags[Dirty::RenderTargets]) {
  204. for (size_t index = 0; index < NUM_RT; ++index) {
  205. ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
  206. PrepareImageView(color_buffer_id, true, is_clear && IsFullClear(color_buffer_id));
  207. }
  208. const ImageViewId depth_buffer_id = render_targets.depth_buffer_id;
  209. PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id));
  210. return;
  211. }
  212. flags[Dirty::RenderTargets] = false;
  213. // Render target control is used on all render targets, so force look ups when this one is up
  214. const bool force = flags[Dirty::RenderTargetControl];
  215. flags[Dirty::RenderTargetControl] = false;
  216. for (size_t index = 0; index < NUM_RT; ++index) {
  217. ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
  218. if (flags[Dirty::ColorBuffer0 + index] || force) {
  219. flags[Dirty::ColorBuffer0 + index] = false;
  220. BindRenderTarget(&color_buffer_id, FindColorBuffer(index, is_clear));
  221. }
  222. PrepareImageView(color_buffer_id, true, is_clear && IsFullClear(color_buffer_id));
  223. }
  224. if (flags[Dirty::ZetaBuffer] || force) {
  225. flags[Dirty::ZetaBuffer] = false;
  226. BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer(is_clear));
  227. }
  228. const ImageViewId depth_buffer_id = render_targets.depth_buffer_id;
  229. PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id));
  230. for (size_t index = 0; index < NUM_RT; ++index) {
  231. render_targets.draw_buffers[index] = static_cast<u8>(maxwell3d.regs.rt_control.Map(index));
  232. }
  233. render_targets.size = Extent2D{
  234. maxwell3d.regs.render_area.width,
  235. maxwell3d.regs.render_area.height,
  236. };
  237. }
  238. template <class P>
  239. typename P::Framebuffer* TextureCache<P>::GetFramebuffer() {
  240. return &slot_framebuffers[GetFramebufferId(render_targets)];
  241. }
  242. template <class P>
  243. void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table,
  244. std::span<ImageViewId> cached_image_view_ids,
  245. std::span<const u32> indices,
  246. std::span<ImageViewId> image_view_ids) {
  247. ASSERT(indices.size() <= image_view_ids.size());
  248. do {
  249. has_deleted_images = false;
  250. std::ranges::transform(indices, image_view_ids.begin(), [&](u32 index) {
  251. return VisitImageView(table, cached_image_view_ids, index);
  252. });
  253. } while (has_deleted_images);
  254. }
  255. template <class P>
  256. ImageViewId TextureCache<P>::VisitImageView(DescriptorTable<TICEntry>& table,
  257. std::span<ImageViewId> cached_image_view_ids,
  258. u32 index) {
  259. if (index > table.Limit()) {
  260. LOG_DEBUG(HW_GPU, "Invalid image view index={}", index);
  261. return NULL_IMAGE_VIEW_ID;
  262. }
  263. const auto [descriptor, is_new] = table.Read(index);
  264. ImageViewId& image_view_id = cached_image_view_ids[index];
  265. if (is_new) {
  266. image_view_id = FindImageView(descriptor);
  267. }
  268. if (image_view_id != NULL_IMAGE_VIEW_ID) {
  269. PrepareImageView(image_view_id, false, false);
  270. }
  271. return image_view_id;
  272. }
  273. template <class P>
  274. FramebufferId TextureCache<P>::GetFramebufferId(const RenderTargets& key) {
  275. const auto [pair, is_new] = framebuffers.try_emplace(key);
  276. FramebufferId& framebuffer_id = pair->second;
  277. if (!is_new) {
  278. return framebuffer_id;
  279. }
  280. std::array<ImageView*, NUM_RT> color_buffers;
  281. std::ranges::transform(key.color_buffer_ids, color_buffers.begin(),
  282. [this](ImageViewId id) { return id ? &slot_image_views[id] : nullptr; });
  283. ImageView* const depth_buffer =
  284. key.depth_buffer_id ? &slot_image_views[key.depth_buffer_id] : nullptr;
  285. framebuffer_id = slot_framebuffers.insert(runtime, color_buffers, depth_buffer, key);
  286. return framebuffer_id;
  287. }
  288. template <class P>
  289. void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
  290. ForEachImageInRegion(cpu_addr, size, [this](ImageId image_id, Image& image) {
  291. if (True(image.flags & ImageFlagBits::CpuModified)) {
  292. return;
  293. }
  294. image.flags |= ImageFlagBits::CpuModified;
  295. if (True(image.flags & ImageFlagBits::Tracked)) {
  296. UntrackImage(image, image_id);
  297. }
  298. });
  299. }
  300. template <class P>
  301. void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
  302. std::vector<ImageId> images;
  303. ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) {
  304. if (!image.IsSafeDownload()) {
  305. return;
  306. }
  307. image.flags &= ~ImageFlagBits::GpuModified;
  308. images.push_back(image_id);
  309. });
  310. if (images.empty()) {
  311. return;
  312. }
  313. std::ranges::sort(images, [this](ImageId lhs, ImageId rhs) {
  314. return slot_images[lhs].modification_tick < slot_images[rhs].modification_tick;
  315. });
  316. for (const ImageId image_id : images) {
  317. Image& image = slot_images[image_id];
  318. auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
  319. const auto copies = FullDownloadCopies(image.info);
  320. image.DownloadMemory(map, copies);
  321. runtime.Finish();
  322. SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span);
  323. }
  324. }
  325. template <class P>
  326. void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
  327. std::vector<ImageId> deleted_images;
  328. ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); });
  329. for (const ImageId id : deleted_images) {
  330. Image& image = slot_images[id];
  331. if (True(image.flags & ImageFlagBits::Tracked)) {
  332. UntrackImage(image, id);
  333. }
  334. UnregisterImage(id);
  335. DeleteImage(id);
  336. }
  337. }
  338. template <class P>
  339. void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) {
  340. std::vector<ImageId> deleted_images;
  341. ForEachImageInRegionGPU(gpu_addr, size,
  342. [&](ImageId id, Image&) { deleted_images.push_back(id); });
  343. for (const ImageId id : deleted_images) {
  344. Image& image = slot_images[id];
  345. if (True(image.flags & ImageFlagBits::Remapped)) {
  346. continue;
  347. }
  348. image.flags |= ImageFlagBits::Remapped;
  349. if (True(image.flags & ImageFlagBits::Tracked)) {
  350. UntrackImage(image, id);
  351. }
  352. }
  353. }
  354. template <class P>
  355. void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
  356. const Tegra::Engines::Fermi2D::Surface& src,
  357. const Tegra::Engines::Fermi2D::Config& copy) {
  358. const BlitImages images = GetBlitImages(dst, src);
  359. const ImageId dst_id = images.dst_id;
  360. const ImageId src_id = images.src_id;
  361. PrepareImage(src_id, false, false);
  362. PrepareImage(dst_id, true, false);
  363. ImageBase& dst_image = slot_images[dst_id];
  364. const ImageBase& src_image = slot_images[src_id];
  365. // TODO: Deduplicate
  366. const std::optional src_base = src_image.TryFindBase(src.Address());
  367. const SubresourceRange src_range{.base = src_base.value(), .extent = {1, 1}};
  368. const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range);
  369. const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info);
  370. const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples);
  371. const Region2D src_region{
  372. Offset2D{.x = copy.src_x0 >> src_samples_x, .y = copy.src_y0 >> src_samples_y},
  373. Offset2D{.x = copy.src_x1 >> src_samples_x, .y = copy.src_y1 >> src_samples_y},
  374. };
  375. const std::optional dst_base = dst_image.TryFindBase(dst.Address());
  376. const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}};
  377. const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
  378. const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
  379. const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples);
  380. const Region2D dst_region{
  381. Offset2D{.x = copy.dst_x0 >> dst_samples_x, .y = copy.dst_y0 >> dst_samples_y},
  382. Offset2D{.x = copy.dst_x1 >> dst_samples_x, .y = copy.dst_y1 >> dst_samples_y},
  383. };
  384. // Always call this after src_framebuffer_id was queried, as the address might be invalidated.
  385. Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id];
  386. if constexpr (FRAMEBUFFER_BLITS) {
  387. // OpenGL blits from framebuffers, not images
  388. Framebuffer* const src_framebuffer = &slot_framebuffers[src_framebuffer_id];
  389. runtime.BlitFramebuffer(dst_framebuffer, src_framebuffer, dst_region, src_region,
  390. copy.filter, copy.operation);
  391. } else {
  392. // Vulkan can blit images, but it lacks format reinterpretations
  393. // Provide a framebuffer in case it's necessary
  394. ImageView& dst_view = slot_image_views[dst_view_id];
  395. ImageView& src_view = slot_image_views[src_view_id];
  396. runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter,
  397. copy.operation);
  398. }
  399. }
  400. template <class P>
  401. typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) {
  402. // TODO: Properly implement this
  403. const auto it = page_table.find(cpu_addr >> PAGE_BITS);
  404. if (it == page_table.end()) {
  405. return nullptr;
  406. }
  407. const auto& image_map_ids = it->second;
  408. for (const ImageMapId map_id : image_map_ids) {
  409. const ImageMapView& map = slot_map_views[map_id];
  410. const ImageBase& image = slot_images[map.image_id];
  411. if (image.cpu_addr != cpu_addr) {
  412. continue;
  413. }
  414. if (image.image_view_ids.empty()) {
  415. continue;
  416. }
  417. return &slot_image_views[image.image_view_ids.at(0)];
  418. }
  419. return nullptr;
  420. }
  421. template <class P>
  422. bool TextureCache<P>::HasUncommittedFlushes() const noexcept {
  423. return !uncommitted_downloads.empty();
  424. }
  425. template <class P>
  426. bool TextureCache<P>::ShouldWaitAsyncFlushes() const noexcept {
  427. return !committed_downloads.empty() && !committed_downloads.front().empty();
  428. }
  429. template <class P>
  430. void TextureCache<P>::CommitAsyncFlushes() {
  431. // This is intentionally passing the value by copy
  432. committed_downloads.push(uncommitted_downloads);
  433. uncommitted_downloads.clear();
  434. }
  435. template <class P>
  436. void TextureCache<P>::PopAsyncFlushes() {
  437. if (committed_downloads.empty()) {
  438. return;
  439. }
  440. const std::span<const ImageId> download_ids = committed_downloads.front();
  441. if (download_ids.empty()) {
  442. committed_downloads.pop();
  443. return;
  444. }
  445. size_t total_size_bytes = 0;
  446. for (const ImageId image_id : download_ids) {
  447. total_size_bytes += slot_images[image_id].unswizzled_size_bytes;
  448. }
  449. auto download_map = runtime.DownloadStagingBuffer(total_size_bytes);
  450. const size_t original_offset = download_map.offset;
  451. for (const ImageId image_id : download_ids) {
  452. Image& image = slot_images[image_id];
  453. const auto copies = FullDownloadCopies(image.info);
  454. image.DownloadMemory(download_map, copies);
  455. download_map.offset += image.unswizzled_size_bytes;
  456. }
  457. // Wait for downloads to finish
  458. runtime.Finish();
  459. download_map.offset = original_offset;
  460. std::span<u8> download_span = download_map.mapped_span;
  461. for (const ImageId image_id : download_ids) {
  462. const ImageBase& image = slot_images[image_id];
  463. const auto copies = FullDownloadCopies(image.info);
  464. SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, download_span);
  465. download_map.offset += image.unswizzled_size_bytes;
  466. download_span = download_span.subspan(image.unswizzled_size_bytes);
  467. }
  468. committed_downloads.pop();
  469. }
  470. template <class P>
  471. bool TextureCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
  472. bool is_modified = false;
  473. ForEachImageInRegion(addr, size, [&is_modified](ImageId, ImageBase& image) {
  474. if (False(image.flags & ImageFlagBits::GpuModified)) {
  475. return false;
  476. }
  477. is_modified = true;
  478. return true;
  479. });
  480. return is_modified;
  481. }
  482. template <class P>
  483. void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
  484. if (False(image.flags & ImageFlagBits::CpuModified)) {
  485. // Only upload modified images
  486. return;
  487. }
  488. image.flags &= ~ImageFlagBits::CpuModified;
  489. TrackImage(image, image_id);
  490. if (image.info.num_samples > 1) {
  491. LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
  492. return;
  493. }
  494. auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image));
  495. UploadImageContents(image, staging);
  496. runtime.InsertUploadMemoryBarrier();
  497. }
  498. template <class P>
  499. template <typename StagingBuffer>
  500. void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging) {
  501. const std::span<u8> mapped_span = staging.mapped_span;
  502. const GPUVAddr gpu_addr = image.gpu_addr;
  503. if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
  504. gpu_memory.ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes());
  505. const auto uploads = FullUploadSwizzles(image.info);
  506. runtime.AccelerateImageUpload(image, staging, uploads);
  507. } else if (True(image.flags & ImageFlagBits::Converted)) {
  508. std::vector<u8> unswizzled_data(image.unswizzled_size_bytes);
  509. auto copies = UnswizzleImage(gpu_memory, gpu_addr, image.info, unswizzled_data);
  510. ConvertImage(unswizzled_data, image.info, mapped_span, copies);
  511. image.UploadMemory(staging, copies);
  512. } else {
  513. const auto copies = UnswizzleImage(gpu_memory, gpu_addr, image.info, mapped_span);
  514. image.UploadMemory(staging, copies);
  515. }
  516. }
  517. template <class P>
  518. ImageViewId TextureCache<P>::FindImageView(const TICEntry& config) {
  519. if (!IsValidEntry(gpu_memory, config)) {
  520. return NULL_IMAGE_VIEW_ID;
  521. }
  522. const auto [pair, is_new] = image_views.try_emplace(config);
  523. ImageViewId& image_view_id = pair->second;
  524. if (is_new) {
  525. image_view_id = CreateImageView(config);
  526. }
  527. return image_view_id;
  528. }
  529. template <class P>
  530. ImageViewId TextureCache<P>::CreateImageView(const TICEntry& config) {
  531. const ImageInfo info(config);
  532. if (info.type == ImageType::Buffer) {
  533. const ImageViewInfo view_info(config, 0);
  534. return slot_image_views.insert(runtime, info, view_info, config.Address());
  535. }
  536. const u32 layer_offset = config.BaseLayer() * info.layer_stride;
  537. const GPUVAddr image_gpu_addr = config.Address() - layer_offset;
  538. const ImageId image_id = FindOrInsertImage(info, image_gpu_addr);
  539. if (!image_id) {
  540. return NULL_IMAGE_VIEW_ID;
  541. }
  542. ImageBase& image = slot_images[image_id];
  543. const SubresourceBase base = image.TryFindBase(config.Address()).value();
  544. ASSERT(base.level == 0);
  545. const ImageViewInfo view_info(config, base.layer);
  546. const ImageViewId image_view_id = FindOrEmplaceImageView(image_id, view_info);
  547. ImageViewBase& image_view = slot_image_views[image_view_id];
  548. image_view.flags |= ImageViewFlagBits::Strong;
  549. image.flags |= ImageFlagBits::Strong;
  550. return image_view_id;
  551. }
  552. template <class P>
  553. ImageId TextureCache<P>::FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
  554. RelaxedOptions options) {
  555. if (const ImageId image_id = FindImage(info, gpu_addr, options); image_id) {
  556. return image_id;
  557. }
  558. return InsertImage(info, gpu_addr, options);
  559. }
  560. template <class P>
  561. ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
  562. RelaxedOptions options) {
  563. std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
  564. if (!cpu_addr) {
  565. cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info));
  566. if (!cpu_addr) {
  567. return ImageId{};
  568. }
  569. }
  570. const bool broken_views = runtime.HasBrokenTextureViewFormats();
  571. const bool native_bgr = runtime.HasNativeBgr();
  572. ImageId image_id;
  573. const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
  574. if (True(existing_image.flags & ImageFlagBits::Remapped)) {
  575. return false;
  576. }
  577. if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) {
  578. const bool strict_size = False(options & RelaxedOptions::Size) &&
  579. True(existing_image.flags & ImageFlagBits::Strong);
  580. const ImageInfo& existing = existing_image.info;
  581. if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&
  582. existing.pitch == info.pitch &&
  583. IsPitchLinearSameSize(existing, info, strict_size) &&
  584. IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
  585. image_id = existing_image_id;
  586. return true;
  587. }
  588. } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
  589. native_bgr)) {
  590. image_id = existing_image_id;
  591. return true;
  592. }
  593. return false;
  594. };
  595. ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda);
  596. return image_id;
  597. }
  598. template <class P>
  599. ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
  600. RelaxedOptions options) {
  601. std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
  602. if (!cpu_addr) {
  603. const auto size = CalculateGuestSizeInBytes(info);
  604. cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, size);
  605. if (!cpu_addr) {
  606. const VAddr fake_addr = ~(1ULL << 40ULL) + virtual_invalid_space;
  607. virtual_invalid_space += Common::AlignUp(size, 32);
  608. cpu_addr = std::optional<VAddr>(fake_addr);
  609. }
  610. }
  611. ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr=0x{:x}", gpu_addr);
  612. const ImageId image_id = JoinImages(info, gpu_addr, *cpu_addr);
  613. const Image& image = slot_images[image_id];
  614. // Using "image.gpu_addr" instead of "gpu_addr" is important because it might be different
  615. const auto [it, is_new] = image_allocs_table.try_emplace(image.gpu_addr);
  616. if (is_new) {
  617. it->second = slot_image_allocs.insert();
  618. }
  619. slot_image_allocs[it->second].images.push_back(image_id);
  620. return image_id;
  621. }
  622. template <class P>
  623. ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr) {
  624. ImageInfo new_info = info;
  625. const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
  626. const bool broken_views = runtime.HasBrokenTextureViewFormats();
  627. const bool native_bgr = runtime.HasNativeBgr();
  628. std::vector<ImageId> overlap_ids;
  629. std::unordered_set<ImageId> overlaps_found;
  630. std::vector<ImageId> left_aliased_ids;
  631. std::vector<ImageId> right_aliased_ids;
  632. std::unordered_set<ImageId> ignore_textures;
  633. std::vector<ImageId> bad_overlap_ids;
  634. const auto region_check = [&](ImageId overlap_id, ImageBase& overlap) {
  635. if (True(overlap.flags & ImageFlagBits::Remapped)) {
  636. ignore_textures.insert(overlap_id);
  637. return;
  638. }
  639. if (info.type == ImageType::Linear) {
  640. if (info.pitch == overlap.info.pitch && gpu_addr == overlap.gpu_addr) {
  641. // Alias linear images with the same pitch
  642. left_aliased_ids.push_back(overlap_id);
  643. }
  644. return;
  645. }
  646. overlaps_found.insert(overlap_id);
  647. static constexpr bool strict_size = true;
  648. const std::optional<OverlapResult> solution = ResolveOverlap(
  649. new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr);
  650. if (solution) {
  651. gpu_addr = solution->gpu_addr;
  652. cpu_addr = solution->cpu_addr;
  653. new_info.resources = solution->resources;
  654. overlap_ids.push_back(overlap_id);
  655. return;
  656. }
  657. static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;
  658. const ImageBase new_image_base(new_info, gpu_addr, cpu_addr);
  659. if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {
  660. left_aliased_ids.push_back(overlap_id);
  661. overlap.flags |= ImageFlagBits::Alias;
  662. } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
  663. broken_views, native_bgr)) {
  664. right_aliased_ids.push_back(overlap_id);
  665. overlap.flags |= ImageFlagBits::Alias;
  666. } else {
  667. bad_overlap_ids.push_back(overlap_id);
  668. overlap.flags |= ImageFlagBits::BadOverlap;
  669. }
  670. };
  671. ForEachImageInRegion(cpu_addr, size_bytes, region_check);
  672. const auto region_check_gpu = [&](ImageId overlap_id, ImageBase& overlap) {
  673. if (!overlaps_found.contains(overlap_id)) {
  674. if (True(overlap.flags & ImageFlagBits::Remapped)) {
  675. ignore_textures.insert(overlap_id);
  676. }
  677. if (overlap.gpu_addr == gpu_addr && overlap.guest_size_bytes == size_bytes) {
  678. ignore_textures.insert(overlap_id);
  679. }
  680. }
  681. };
  682. ForEachSparseImageInRegion(gpu_addr, size_bytes, region_check_gpu);
  683. const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);
  684. Image& new_image = slot_images[new_image_id];
  685. if (!gpu_memory.IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) {
  686. new_image.flags |= ImageFlagBits::Sparse;
  687. }
  688. for (const ImageId overlap_id : ignore_textures) {
  689. Image& overlap = slot_images[overlap_id];
  690. if (True(overlap.flags & ImageFlagBits::GpuModified)) {
  691. UNIMPLEMENTED();
  692. }
  693. if (True(overlap.flags & ImageFlagBits::Tracked)) {
  694. UntrackImage(overlap, overlap_id);
  695. }
  696. UnregisterImage(overlap_id);
  697. DeleteImage(overlap_id);
  698. }
  699. // TODO: Only upload what we need
  700. RefreshContents(new_image, new_image_id);
  701. for (const ImageId overlap_id : overlap_ids) {
  702. Image& overlap = slot_images[overlap_id];
  703. if (overlap.info.num_samples != new_image.info.num_samples) {
  704. LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
  705. } else {
  706. const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value();
  707. const auto copies = MakeShrinkImageCopies(new_info, overlap.info, base);
  708. runtime.CopyImage(new_image, overlap, copies);
  709. }
  710. if (True(overlap.flags & ImageFlagBits::Tracked)) {
  711. UntrackImage(overlap, overlap_id);
  712. }
  713. UnregisterImage(overlap_id);
  714. DeleteImage(overlap_id);
  715. }
  716. ImageBase& new_image_base = new_image;
  717. for (const ImageId aliased_id : right_aliased_ids) {
  718. ImageBase& aliased = slot_images[aliased_id];
  719. AddImageAlias(new_image_base, aliased, new_image_id, aliased_id);
  720. new_image.flags |= ImageFlagBits::Alias;
  721. }
  722. for (const ImageId aliased_id : left_aliased_ids) {
  723. ImageBase& aliased = slot_images[aliased_id];
  724. AddImageAlias(aliased, new_image_base, aliased_id, new_image_id);
  725. new_image.flags |= ImageFlagBits::Alias;
  726. }
  727. for (const ImageId aliased_id : bad_overlap_ids) {
  728. ImageBase& aliased = slot_images[aliased_id];
  729. aliased.overlapping_images.push_back(new_image_id);
  730. new_image.overlapping_images.push_back(aliased_id);
  731. new_image.flags |= ImageFlagBits::BadOverlap;
  732. }
  733. RegisterImage(new_image_id);
  734. return new_image_id;
  735. }
  736. template <class P>
  737. typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
  738. const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src) {
  739. static constexpr auto FIND_OPTIONS = RelaxedOptions::Format | RelaxedOptions::Samples;
  740. const GPUVAddr dst_addr = dst.Address();
  741. const GPUVAddr src_addr = src.Address();
  742. ImageInfo dst_info(dst);
  743. ImageInfo src_info(src);
  744. ImageId dst_id;
  745. ImageId src_id;
  746. do {
  747. has_deleted_images = false;
  748. dst_id = FindImage(dst_info, dst_addr, FIND_OPTIONS);
  749. src_id = FindImage(src_info, src_addr, FIND_OPTIONS);
  750. const ImageBase* const dst_image = dst_id ? &slot_images[dst_id] : nullptr;
  751. const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr;
  752. DeduceBlitImages(dst_info, src_info, dst_image, src_image);
  753. if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) {
  754. continue;
  755. }
  756. if (!dst_id) {
  757. dst_id = InsertImage(dst_info, dst_addr, RelaxedOptions{});
  758. }
  759. if (!src_id) {
  760. src_id = InsertImage(src_info, src_addr, RelaxedOptions{});
  761. }
  762. } while (has_deleted_images);
  763. return BlitImages{
  764. .dst_id = dst_id,
  765. .src_id = src_id,
  766. .dst_format = dst_info.format,
  767. .src_format = src_info.format,
  768. };
  769. }
  770. template <class P>
  771. SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
  772. if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) {
  773. return NULL_SAMPLER_ID;
  774. }
  775. const auto [pair, is_new] = samplers.try_emplace(config);
  776. if (is_new) {
  777. pair->second = slot_samplers.insert(runtime, config);
  778. }
  779. return pair->second;
  780. }
  781. template <class P>
  782. ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) {
  783. const auto& regs = maxwell3d.regs;
  784. if (index >= regs.rt_control.count) {
  785. return ImageViewId{};
  786. }
  787. const auto& rt = regs.rt[index];
  788. const GPUVAddr gpu_addr = rt.Address();
  789. if (gpu_addr == 0) {
  790. return ImageViewId{};
  791. }
  792. if (rt.format == Tegra::RenderTargetFormat::NONE) {
  793. return ImageViewId{};
  794. }
  795. const ImageInfo info(regs, index);
  796. return FindRenderTargetView(info, gpu_addr, is_clear);
  797. }
  798. template <class P>
  799. ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) {
  800. const auto& regs = maxwell3d.regs;
  801. if (!regs.zeta_enable) {
  802. return ImageViewId{};
  803. }
  804. const GPUVAddr gpu_addr = regs.zeta.Address();
  805. if (gpu_addr == 0) {
  806. return ImageViewId{};
  807. }
  808. const ImageInfo info(regs);
  809. return FindRenderTargetView(info, gpu_addr, is_clear);
  810. }
  811. template <class P>
  812. ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr,
  813. bool is_clear) {
  814. const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{};
  815. const ImageId image_id = FindOrInsertImage(info, gpu_addr, options);
  816. if (!image_id) {
  817. return NULL_IMAGE_VIEW_ID;
  818. }
  819. Image& image = slot_images[image_id];
  820. const ImageViewType view_type = RenderTargetImageViewType(info);
  821. SubresourceBase base;
  822. if (image.info.type == ImageType::Linear) {
  823. base = SubresourceBase{.level = 0, .layer = 0};
  824. } else {
  825. base = image.TryFindBase(gpu_addr).value();
  826. }
  827. const s32 layers = image.info.type == ImageType::e3D ? info.size.depth : info.resources.layers;
  828. const SubresourceRange range{
  829. .base = base,
  830. .extent = {.levels = 1, .layers = layers},
  831. };
  832. return FindOrEmplaceImageView(image_id, ImageViewInfo(view_type, info.format, range));
  833. }
  834. template <class P>
  835. template <typename Func>
  836. void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func) {
  837. using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type;
  838. static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
  839. boost::container::small_vector<ImageId, 32> images;
  840. boost::container::small_vector<ImageMapId, 32> maps;
  841. ForEachCPUPage(cpu_addr, size, [this, &images, &maps, cpu_addr, size, func](u64 page) {
  842. const auto it = page_table.find(page);
  843. if (it == page_table.end()) {
  844. if constexpr (BOOL_BREAK) {
  845. return false;
  846. } else {
  847. return;
  848. }
  849. }
  850. for (const ImageMapId map_id : it->second) {
  851. ImageMapView& map = slot_map_views[map_id];
  852. if (map.picked) {
  853. continue;
  854. }
  855. if (!map.Overlaps(cpu_addr, size)) {
  856. continue;
  857. }
  858. map.picked = true;
  859. maps.push_back(map_id);
  860. Image& image = slot_images[map.image_id];
  861. if (True(image.flags & ImageFlagBits::Picked)) {
  862. continue;
  863. }
  864. image.flags |= ImageFlagBits::Picked;
  865. images.push_back(map.image_id);
  866. if constexpr (BOOL_BREAK) {
  867. if (func(map.image_id, image)) {
  868. return true;
  869. }
  870. } else {
  871. func(map.image_id, image);
  872. }
  873. }
  874. if constexpr (BOOL_BREAK) {
  875. return false;
  876. }
  877. });
  878. for (const ImageId image_id : images) {
  879. slot_images[image_id].flags &= ~ImageFlagBits::Picked;
  880. }
  881. for (const ImageMapId map_id : maps) {
  882. slot_map_views[map_id].picked = false;
  883. }
  884. }
  885. template <class P>
  886. template <typename Func>
  887. void TextureCache<P>::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func) {
  888. using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type;
  889. static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
  890. boost::container::small_vector<ImageId, 8> images;
  891. ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) {
  892. const auto it = gpu_page_table.find(page);
  893. if (it == gpu_page_table.end()) {
  894. if constexpr (BOOL_BREAK) {
  895. return false;
  896. } else {
  897. return;
  898. }
  899. }
  900. for (const ImageId image_id : it->second) {
  901. Image& image = slot_images[image_id];
  902. if (True(image.flags & ImageFlagBits::Picked)) {
  903. continue;
  904. }
  905. if (!image.OverlapsGPU(gpu_addr, size)) {
  906. continue;
  907. }
  908. image.flags |= ImageFlagBits::Picked;
  909. images.push_back(image_id);
  910. if constexpr (BOOL_BREAK) {
  911. if (func(image_id, image)) {
  912. return true;
  913. }
  914. } else {
  915. func(image_id, image);
  916. }
  917. }
  918. if constexpr (BOOL_BREAK) {
  919. return false;
  920. }
  921. });
  922. for (const ImageId image_id : images) {
  923. slot_images[image_id].flags &= ~ImageFlagBits::Picked;
  924. }
  925. }
  926. template <class P>
  927. template <typename Func>
  928. void TextureCache<P>::ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func) {
  929. using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type;
  930. static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
  931. boost::container::small_vector<ImageId, 8> images;
  932. ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) {
  933. const auto it = sparse_page_table.find(page);
  934. if (it == sparse_page_table.end()) {
  935. if constexpr (BOOL_BREAK) {
  936. return false;
  937. } else {
  938. return;
  939. }
  940. }
  941. for (const ImageId image_id : it->second) {
  942. Image& image = slot_images[image_id];
  943. if (True(image.flags & ImageFlagBits::Picked)) {
  944. continue;
  945. }
  946. if (!image.OverlapsGPU(gpu_addr, size)) {
  947. continue;
  948. }
  949. image.flags |= ImageFlagBits::Picked;
  950. images.push_back(image_id);
  951. if constexpr (BOOL_BREAK) {
  952. if (func(image_id, image)) {
  953. return true;
  954. }
  955. } else {
  956. func(image_id, image);
  957. }
  958. }
  959. if constexpr (BOOL_BREAK) {
  960. return false;
  961. }
  962. });
  963. for (const ImageId image_id : images) {
  964. slot_images[image_id].flags &= ~ImageFlagBits::Picked;
  965. }
  966. }
  967. template <class P>
  968. template <typename Func>
  969. void TextureCache<P>::ForEachSparseSegment(ImageBase& image, Func&& func) {
  970. using FuncReturn = typename std::invoke_result<Func, GPUVAddr, VAddr, size_t>::type;
  971. static constexpr bool RETURNS_BOOL = std::is_same_v<FuncReturn, bool>;
  972. const auto segments = gpu_memory.GetSubmappedRange(image.gpu_addr, image.guest_size_bytes);
  973. for (auto& segment : segments) {
  974. const auto gpu_addr = segment.first;
  975. const auto size = segment.second;
  976. std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
  977. ASSERT(cpu_addr);
  978. if constexpr (RETURNS_BOOL) {
  979. if (func(gpu_addr, *cpu_addr, size)) {
  980. break;
  981. }
  982. } else {
  983. func(gpu_addr, *cpu_addr, size);
  984. }
  985. }
  986. }
  987. template <class P>
  988. ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info) {
  989. Image& image = slot_images[image_id];
  990. if (const ImageViewId image_view_id = image.FindView(info); image_view_id) {
  991. return image_view_id;
  992. }
  993. const ImageViewId image_view_id = slot_image_views.insert(runtime, info, image_id, image);
  994. image.InsertView(info, image_view_id);
  995. return image_view_id;
  996. }
  997. template <class P>
  998. void TextureCache<P>::RegisterImage(ImageId image_id) {
  999. ImageBase& image = slot_images[image_id];
  1000. ASSERT_MSG(False(image.flags & ImageFlagBits::Registered),
  1001. "Trying to register an already registered image");
  1002. image.flags |= ImageFlagBits::Registered;
  1003. u64 tentative_size = std::max(image.guest_size_bytes, image.unswizzled_size_bytes);
  1004. if ((IsPixelFormatASTC(image.info.format) &&
  1005. True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
  1006. True(image.flags & ImageFlagBits::Converted)) {
  1007. tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
  1008. }
  1009. total_used_memory += Common::AlignUp(tentative_size, 1024);
  1010. ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
  1011. [this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); });
  1012. if (False(image.flags & ImageFlagBits::Sparse)) {
  1013. auto map_id =
  1014. slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id);
  1015. ForEachCPUPage(image.cpu_addr, image.guest_size_bytes,
  1016. [this, map_id](u64 page) { page_table[page].push_back(map_id); });
  1017. image.map_view_id = map_id;
  1018. return;
  1019. }
  1020. std::vector<ImageViewId> sparse_maps{};
  1021. ForEachSparseSegment(
  1022. image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) {
  1023. auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id);
  1024. ForEachCPUPage(cpu_addr, size,
  1025. [this, map_id](u64 page) { page_table[page].push_back(map_id); });
  1026. sparse_maps.push_back(map_id);
  1027. });
  1028. sparse_views.emplace(image_id, std::move(sparse_maps));
  1029. ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
  1030. [this, image_id](u64 page) { sparse_page_table[page].push_back(image_id); });
  1031. }
  1032. template <class P>
  1033. void TextureCache<P>::UnregisterImage(ImageId image_id) {
  1034. Image& image = slot_images[image_id];
  1035. ASSERT_MSG(True(image.flags & ImageFlagBits::Registered),
  1036. "Trying to unregister an already registered image");
  1037. image.flags &= ~ImageFlagBits::Registered;
  1038. image.flags &= ~ImageFlagBits::BadOverlap;
  1039. u64 tentative_size = std::max(image.guest_size_bytes, image.unswizzled_size_bytes);
  1040. if ((IsPixelFormatASTC(image.info.format) &&
  1041. True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
  1042. True(image.flags & ImageFlagBits::Converted)) {
  1043. tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
  1044. }
  1045. total_used_memory -= Common::AlignUp(tentative_size, 1024);
  1046. const auto& clear_page_table =
  1047. [this, image_id](
  1048. u64 page,
  1049. std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) {
  1050. const auto page_it = selected_page_table.find(page);
  1051. if (page_it == selected_page_table.end()) {
  1052. UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
  1053. return;
  1054. }
  1055. std::vector<ImageId>& image_ids = page_it->second;
  1056. const auto vector_it = std::ranges::find(image_ids, image_id);
  1057. if (vector_it == image_ids.end()) {
  1058. UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}",
  1059. page << PAGE_BITS);
  1060. return;
  1061. }
  1062. image_ids.erase(vector_it);
  1063. };
  1064. ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
  1065. [this, &clear_page_table](u64 page) { clear_page_table(page, gpu_page_table); });
  1066. if (False(image.flags & ImageFlagBits::Sparse)) {
  1067. const auto map_id = image.map_view_id;
  1068. ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) {
  1069. const auto page_it = page_table.find(page);
  1070. if (page_it == page_table.end()) {
  1071. UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
  1072. return;
  1073. }
  1074. std::vector<ImageMapId>& image_map_ids = page_it->second;
  1075. const auto vector_it = std::ranges::find(image_map_ids, map_id);
  1076. if (vector_it == image_map_ids.end()) {
  1077. UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}",
  1078. page << PAGE_BITS);
  1079. return;
  1080. }
  1081. image_map_ids.erase(vector_it);
  1082. });
  1083. slot_map_views.erase(map_id);
  1084. return;
  1085. }
  1086. ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) {
  1087. clear_page_table(page, sparse_page_table);
  1088. });
  1089. auto it = sparse_views.find(image_id);
  1090. ASSERT(it != sparse_views.end());
  1091. auto& sparse_maps = it->second;
  1092. for (auto& map_view_id : sparse_maps) {
  1093. const auto& map_range = slot_map_views[map_view_id];
  1094. const VAddr cpu_addr = map_range.cpu_addr;
  1095. const std::size_t size = map_range.size;
  1096. ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) {
  1097. const auto page_it = page_table.find(page);
  1098. if (page_it == page_table.end()) {
  1099. UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
  1100. return;
  1101. }
  1102. std::vector<ImageMapId>& image_map_ids = page_it->second;
  1103. auto vector_it = image_map_ids.begin();
  1104. while (vector_it != image_map_ids.end()) {
  1105. ImageMapView& map = slot_map_views[*vector_it];
  1106. if (map.image_id != image_id) {
  1107. vector_it++;
  1108. continue;
  1109. }
  1110. if (!map.picked) {
  1111. map.picked = true;
  1112. }
  1113. vector_it = image_map_ids.erase(vector_it);
  1114. }
  1115. });
  1116. slot_map_views.erase(map_view_id);
  1117. }
  1118. sparse_views.erase(it);
  1119. }
  1120. template <class P>
  1121. void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
  1122. ASSERT(False(image.flags & ImageFlagBits::Tracked));
  1123. image.flags |= ImageFlagBits::Tracked;
  1124. if (False(image.flags & ImageFlagBits::Sparse)) {
  1125. rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1);
  1126. return;
  1127. }
  1128. if (True(image.flags & ImageFlagBits::Registered)) {
  1129. auto it = sparse_views.find(image_id);
  1130. ASSERT(it != sparse_views.end());
  1131. auto& sparse_maps = it->second;
  1132. for (auto& map_view_id : sparse_maps) {
  1133. const auto& map = slot_map_views[map_view_id];
  1134. const VAddr cpu_addr = map.cpu_addr;
  1135. const std::size_t size = map.size;
  1136. rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1);
  1137. }
  1138. return;
  1139. }
  1140. ForEachSparseSegment(image,
  1141. [this]([[maybe_unused]] GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) {
  1142. rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1);
  1143. });
  1144. }
  1145. template <class P>
  1146. void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
  1147. ASSERT(True(image.flags & ImageFlagBits::Tracked));
  1148. image.flags &= ~ImageFlagBits::Tracked;
  1149. if (False(image.flags & ImageFlagBits::Sparse)) {
  1150. rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1);
  1151. return;
  1152. }
  1153. ASSERT(True(image.flags & ImageFlagBits::Registered));
  1154. auto it = sparse_views.find(image_id);
  1155. ASSERT(it != sparse_views.end());
  1156. auto& sparse_maps = it->second;
  1157. for (auto& map_view_id : sparse_maps) {
  1158. const auto& map = slot_map_views[map_view_id];
  1159. const VAddr cpu_addr = map.cpu_addr;
  1160. const std::size_t size = map.size;
  1161. rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
  1162. }
  1163. }
  1164. template <class P>
  1165. void TextureCache<P>::DeleteImage(ImageId image_id) {
  1166. ImageBase& image = slot_images[image_id];
  1167. const GPUVAddr gpu_addr = image.gpu_addr;
  1168. const auto alloc_it = image_allocs_table.find(gpu_addr);
  1169. if (alloc_it == image_allocs_table.end()) {
  1170. UNREACHABLE_MSG("Trying to delete an image alloc that does not exist in address 0x{:x}",
  1171. gpu_addr);
  1172. return;
  1173. }
  1174. const ImageAllocId alloc_id = alloc_it->second;
  1175. std::vector<ImageId>& alloc_images = slot_image_allocs[alloc_id].images;
  1176. const auto alloc_image_it = std::ranges::find(alloc_images, image_id);
  1177. if (alloc_image_it == alloc_images.end()) {
  1178. UNREACHABLE_MSG("Trying to delete an image that does not exist");
  1179. return;
  1180. }
  1181. ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked");
  1182. ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered");
  1183. // Mark render targets as dirty
  1184. auto& dirty = maxwell3d.dirty.flags;
  1185. dirty[Dirty::RenderTargets] = true;
  1186. dirty[Dirty::ZetaBuffer] = true;
  1187. for (size_t rt = 0; rt < NUM_RT; ++rt) {
  1188. dirty[Dirty::ColorBuffer0 + rt] = true;
  1189. }
  1190. const std::span<const ImageViewId> image_view_ids = image.image_view_ids;
  1191. for (const ImageViewId image_view_id : image_view_ids) {
  1192. std::ranges::replace(render_targets.color_buffer_ids, image_view_id, ImageViewId{});
  1193. if (render_targets.depth_buffer_id == image_view_id) {
  1194. render_targets.depth_buffer_id = ImageViewId{};
  1195. }
  1196. }
  1197. RemoveImageViewReferences(image_view_ids);
  1198. RemoveFramebuffers(image_view_ids);
  1199. for (const AliasedImage& alias : image.aliased_images) {
  1200. ImageBase& other_image = slot_images[alias.id];
  1201. [[maybe_unused]] const size_t num_removed_aliases =
  1202. std::erase_if(other_image.aliased_images, [image_id](const AliasedImage& other_alias) {
  1203. return other_alias.id == image_id;
  1204. });
  1205. other_image.CheckAliasState();
  1206. ASSERT_MSG(num_removed_aliases == 1, "Invalid number of removed aliases: {}",
  1207. num_removed_aliases);
  1208. }
  1209. for (const ImageId overlap_id : image.overlapping_images) {
  1210. ImageBase& other_image = slot_images[overlap_id];
  1211. [[maybe_unused]] const size_t num_removed_overlaps = std::erase_if(
  1212. other_image.overlapping_images,
  1213. [image_id](const ImageId other_overlap_id) { return other_overlap_id == image_id; });
  1214. other_image.CheckBadOverlapState();
  1215. ASSERT_MSG(num_removed_overlaps == 1, "Invalid number of removed overlapps: {}",
  1216. num_removed_overlaps);
  1217. }
  1218. for (const ImageViewId image_view_id : image_view_ids) {
  1219. sentenced_image_view.Push(std::move(slot_image_views[image_view_id]));
  1220. slot_image_views.erase(image_view_id);
  1221. }
  1222. sentenced_images.Push(std::move(slot_images[image_id]));
  1223. slot_images.erase(image_id);
  1224. alloc_images.erase(alloc_image_it);
  1225. if (alloc_images.empty()) {
  1226. image_allocs_table.erase(alloc_it);
  1227. }
  1228. if constexpr (ENABLE_VALIDATION) {
  1229. std::ranges::fill(graphics_image_view_ids, CORRUPT_ID);
  1230. std::ranges::fill(compute_image_view_ids, CORRUPT_ID);
  1231. }
  1232. graphics_image_table.Invalidate();
  1233. compute_image_table.Invalidate();
  1234. has_deleted_images = true;
  1235. }
  1236. template <class P>
  1237. void TextureCache<P>::RemoveImageViewReferences(std::span<const ImageViewId> removed_views) {
  1238. auto it = image_views.begin();
  1239. while (it != image_views.end()) {
  1240. const auto found = std::ranges::find(removed_views, it->second);
  1241. if (found != removed_views.end()) {
  1242. it = image_views.erase(it);
  1243. } else {
  1244. ++it;
  1245. }
  1246. }
  1247. }
  1248. template <class P>
  1249. void TextureCache<P>::RemoveFramebuffers(std::span<const ImageViewId> removed_views) {
  1250. auto it = framebuffers.begin();
  1251. while (it != framebuffers.end()) {
  1252. if (it->first.Contains(removed_views)) {
  1253. it = framebuffers.erase(it);
  1254. } else {
  1255. ++it;
  1256. }
  1257. }
  1258. }
  1259. template <class P>
  1260. void TextureCache<P>::MarkModification(ImageBase& image) noexcept {
  1261. image.flags |= ImageFlagBits::GpuModified;
  1262. image.modification_tick = ++modification_tick;
  1263. }
  1264. template <class P>
  1265. void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
  1266. boost::container::small_vector<const AliasedImage*, 1> aliased_images;
  1267. ImageBase& image = slot_images[image_id];
  1268. u64 most_recent_tick = image.modification_tick;
  1269. for (const AliasedImage& aliased : image.aliased_images) {
  1270. ImageBase& aliased_image = slot_images[aliased.id];
  1271. if (image.modification_tick < aliased_image.modification_tick) {
  1272. most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick);
  1273. aliased_images.push_back(&aliased);
  1274. }
  1275. }
  1276. if (aliased_images.empty()) {
  1277. return;
  1278. }
  1279. image.modification_tick = most_recent_tick;
  1280. std::ranges::sort(aliased_images, [this](const AliasedImage* lhs, const AliasedImage* rhs) {
  1281. const ImageBase& lhs_image = slot_images[lhs->id];
  1282. const ImageBase& rhs_image = slot_images[rhs->id];
  1283. return lhs_image.modification_tick < rhs_image.modification_tick;
  1284. });
  1285. for (const AliasedImage* const aliased : aliased_images) {
  1286. CopyImage(image_id, aliased->id, aliased->copies);
  1287. }
  1288. }
  1289. template <class P>
  1290. void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool invalidate) {
  1291. Image& image = slot_images[image_id];
  1292. if (invalidate) {
  1293. image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified);
  1294. if (False(image.flags & ImageFlagBits::Tracked)) {
  1295. TrackImage(image, image_id);
  1296. }
  1297. } else {
  1298. RefreshContents(image, image_id);
  1299. SynchronizeAliases(image_id);
  1300. }
  1301. if (is_modification) {
  1302. MarkModification(image);
  1303. }
  1304. image.frame_tick = frame_tick;
  1305. }
  1306. template <class P>
  1307. void TextureCache<P>::PrepareImageView(ImageViewId image_view_id, bool is_modification,
  1308. bool invalidate) {
  1309. if (!image_view_id) {
  1310. return;
  1311. }
  1312. const ImageViewBase& image_view = slot_image_views[image_view_id];
  1313. if (image_view.IsBuffer()) {
  1314. return;
  1315. }
  1316. PrepareImage(image_view.image_id, is_modification, invalidate);
  1317. }
  1318. template <class P>
  1319. void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies) {
  1320. Image& dst = slot_images[dst_id];
  1321. Image& src = slot_images[src_id];
  1322. const auto dst_format_type = GetFormatType(dst.info.format);
  1323. const auto src_format_type = GetFormatType(src.info.format);
  1324. if (src_format_type == dst_format_type) {
  1325. if constexpr (HAS_EMULATED_COPIES) {
  1326. if (!runtime.CanImageBeCopied(dst, src)) {
  1327. return runtime.EmulateCopyImage(dst, src, copies);
  1328. }
  1329. }
  1330. return runtime.CopyImage(dst, src, copies);
  1331. }
  1332. UNIMPLEMENTED_IF(dst.info.type != ImageType::e2D);
  1333. UNIMPLEMENTED_IF(src.info.type != ImageType::e2D);
  1334. for (const ImageCopy& copy : copies) {
  1335. UNIMPLEMENTED_IF(copy.dst_subresource.num_layers != 1);
  1336. UNIMPLEMENTED_IF(copy.src_subresource.num_layers != 1);
  1337. UNIMPLEMENTED_IF(copy.src_offset != Offset3D{});
  1338. UNIMPLEMENTED_IF(copy.dst_offset != Offset3D{});
  1339. const SubresourceBase dst_base{
  1340. .level = copy.dst_subresource.base_level,
  1341. .layer = copy.dst_subresource.base_layer,
  1342. };
  1343. const SubresourceBase src_base{
  1344. .level = copy.src_subresource.base_level,
  1345. .layer = copy.src_subresource.base_layer,
  1346. };
  1347. const SubresourceExtent dst_extent{.levels = 1, .layers = 1};
  1348. const SubresourceExtent src_extent{.levels = 1, .layers = 1};
  1349. const SubresourceRange dst_range{.base = dst_base, .extent = dst_extent};
  1350. const SubresourceRange src_range{.base = src_base, .extent = src_extent};
  1351. const ImageViewInfo dst_view_info(ImageViewType::e2D, dst.info.format, dst_range);
  1352. const ImageViewInfo src_view_info(ImageViewType::e2D, src.info.format, src_range);
  1353. const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
  1354. Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id];
  1355. const ImageViewId src_view_id = FindOrEmplaceImageView(src_id, src_view_info);
  1356. ImageView& dst_view = slot_image_views[dst_view_id];
  1357. ImageView& src_view = slot_image_views[src_view_id];
  1358. [[maybe_unused]] const Extent3D expected_size{
  1359. .width = std::min(dst_view.size.width, src_view.size.width),
  1360. .height = std::min(dst_view.size.height, src_view.size.height),
  1361. .depth = std::min(dst_view.size.depth, src_view.size.depth),
  1362. };
  1363. UNIMPLEMENTED_IF(copy.extent != expected_size);
  1364. runtime.ConvertImage(dst_framebuffer, dst_view, src_view);
  1365. }
  1366. }
  1367. template <class P>
  1368. void TextureCache<P>::BindRenderTarget(ImageViewId* old_id, ImageViewId new_id) {
  1369. if (*old_id == new_id) {
  1370. return;
  1371. }
  1372. if (*old_id) {
  1373. const ImageViewBase& old_view = slot_image_views[*old_id];
  1374. if (True(old_view.flags & ImageViewFlagBits::PreemtiveDownload)) {
  1375. uncommitted_downloads.push_back(old_view.image_id);
  1376. }
  1377. }
  1378. *old_id = new_id;
  1379. }
  1380. template <class P>
  1381. std::pair<FramebufferId, ImageViewId> TextureCache<P>::RenderTargetFromImage(
  1382. ImageId image_id, const ImageViewInfo& view_info) {
  1383. const ImageViewId view_id = FindOrEmplaceImageView(image_id, view_info);
  1384. const ImageBase& image = slot_images[image_id];
  1385. const bool is_color = GetFormatType(image.info.format) == SurfaceType::ColorTexture;
  1386. const ImageViewId color_view_id = is_color ? view_id : ImageViewId{};
  1387. const ImageViewId depth_view_id = is_color ? ImageViewId{} : view_id;
  1388. const Extent3D extent = MipSize(image.info.size, view_info.range.base.level);
  1389. const u32 num_samples = image.info.num_samples;
  1390. const auto [samples_x, samples_y] = SamplesLog2(num_samples);
  1391. const FramebufferId framebuffer_id = GetFramebufferId(RenderTargets{
  1392. .color_buffer_ids = {color_view_id},
  1393. .depth_buffer_id = depth_view_id,
  1394. .size = {extent.width >> samples_x, extent.height >> samples_y},
  1395. });
  1396. return {framebuffer_id, view_id};
  1397. }
  1398. template <class P>
  1399. bool TextureCache<P>::IsFullClear(ImageViewId id) {
  1400. if (!id) {
  1401. return true;
  1402. }
  1403. const ImageViewBase& image_view = slot_image_views[id];
  1404. const ImageBase& image = slot_images[image_view.image_id];
  1405. const Extent3D size = image_view.size;
  1406. const auto& regs = maxwell3d.regs;
  1407. const auto& scissor = regs.scissor_test[0];
  1408. if (image.info.resources.levels > 1 || image.info.resources.layers > 1) {
  1409. // Images with multiple resources can't be cleared in a single call
  1410. return false;
  1411. }
  1412. if (regs.clear_flags.scissor == 0) {
  1413. // If scissor testing is disabled, the clear is always full
  1414. return true;
  1415. }
  1416. // Make sure the clear covers all texels in the subresource
  1417. return scissor.min_x == 0 && scissor.min_y == 0 && scissor.max_x >= size.width &&
  1418. scissor.max_y >= size.height;
  1419. }
  1420. } // namespace VideoCommon