buffer_cache.h 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755
  1. // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-3.0-or-later
  3. #pragma once
  4. #include <algorithm>
  5. #include <memory>
  6. #include <numeric>
  7. #include "video_core/buffer_cache/buffer_cache_base.h"
  8. namespace VideoCommon {
  9. using Core::Memory::YUZU_PAGESIZE;
  10. template <class P>
  11. BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
  12. Core::Memory::Memory& cpu_memory_, Runtime& runtime_)
  13. : runtime{runtime_}, rasterizer{rasterizer_}, cpu_memory{cpu_memory_}, memory_tracker{
  14. rasterizer} {
  15. // Ensure the first slot is used for the null buffer
  16. void(slot_buffers.insert(runtime, NullBufferParams{}));
  17. common_ranges.clear();
  18. inline_buffer_id = NULL_BUFFER_ID;
  19. active_async_buffers = !Settings::IsGPULevelHigh();
  20. if (!runtime.CanReportMemoryUsage()) {
  21. minimum_memory = DEFAULT_EXPECTED_MEMORY;
  22. critical_memory = DEFAULT_CRITICAL_MEMORY;
  23. return;
  24. }
  25. const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
  26. const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
  27. const s64 min_spacing_critical = device_memory - 1_GiB;
  28. const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
  29. const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
  30. const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
  31. minimum_memory = static_cast<u64>(
  32. std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
  33. DEFAULT_EXPECTED_MEMORY));
  34. critical_memory = static_cast<u64>(
  35. std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
  36. DEFAULT_CRITICAL_MEMORY));
  37. }
  38. template <class P>
  39. void BufferCache<P>::RunGarbageCollector() {
  40. const bool aggressive_gc = total_used_memory >= critical_memory;
  41. const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
  42. int num_iterations = aggressive_gc ? 64 : 32;
  43. const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
  44. if (num_iterations == 0) {
  45. return true;
  46. }
  47. --num_iterations;
  48. auto& buffer = slot_buffers[buffer_id];
  49. DownloadBufferMemory(buffer);
  50. DeleteBuffer(buffer_id);
  51. return false;
  52. };
  53. lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
  54. }
  55. template <class P>
  56. void BufferCache<P>::TickFrame() {
  57. // Calculate hits and shots and move hit bits to the right
  58. const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end());
  59. const u32 shots = std::reduce(uniform_cache_shots.begin(), uniform_cache_shots.end());
  60. std::copy_n(uniform_cache_hits.begin(), uniform_cache_hits.size() - 1,
  61. uniform_cache_hits.begin() + 1);
  62. std::copy_n(uniform_cache_shots.begin(), uniform_cache_shots.size() - 1,
  63. uniform_cache_shots.begin() + 1);
  64. uniform_cache_hits[0] = 0;
  65. uniform_cache_shots[0] = 0;
  66. active_async_buffers = !Settings::IsGPULevelHigh();
  67. const bool skip_preferred = hits * 256 < shots * 251;
  68. uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
  69. // If we can obtain the memory info, use it instead of the estimate.
  70. if (runtime.CanReportMemoryUsage()) {
  71. total_used_memory = runtime.GetDeviceMemoryUsage();
  72. }
  73. if (total_used_memory >= minimum_memory) {
  74. RunGarbageCollector();
  75. }
  76. ++frame_tick;
  77. delayed_destruction_ring.Tick();
  78. if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
  79. for (auto& buffer : async_buffers_death_ring) {
  80. runtime.FreeDeferredStagingBuffer(buffer);
  81. }
  82. async_buffers_death_ring.clear();
  83. }
  84. }
  85. template <class P>
  86. void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) {
  87. memory_tracker.MarkRegionAsCpuModified(cpu_addr, size);
  88. if (memory_tracker.IsRegionGpuModified(cpu_addr, size)) {
  89. const IntervalType subtract_interval{cpu_addr, cpu_addr + size};
  90. ClearDownload(subtract_interval);
  91. common_ranges.subtract(subtract_interval);
  92. }
  93. }
  94. template <class P>
  95. void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) {
  96. memory_tracker.CachedCpuWrite(cpu_addr, size);
  97. const IntervalType add_interval{Common::AlignDown(cpu_addr, YUZU_PAGESIZE),
  98. Common::AlignUp(cpu_addr + size, YUZU_PAGESIZE)};
  99. cached_ranges.add(add_interval);
  100. }
  101. template <class P>
  102. void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
  103. WaitOnAsyncFlushes(cpu_addr, size);
  104. ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) {
  105. DownloadBufferMemory(buffer, cpu_addr, size);
  106. });
  107. }
  108. template <class P>
  109. void BufferCache<P>::WaitOnAsyncFlushes(VAddr cpu_addr, u64 size) {
  110. bool must_wait = false;
  111. ForEachInOverlapCounter(async_downloads, cpu_addr, size,
  112. [&](VAddr, VAddr, int) { must_wait = true; });
  113. bool must_release = false;
  114. ForEachInRangeSet(pending_ranges, cpu_addr, size, [&](VAddr, VAddr) { must_release = true; });
  115. if (must_release) {
  116. std::function<void()> tmp([]() {});
  117. rasterizer.SignalFence(std::move(tmp));
  118. }
  119. if (must_wait || must_release) {
  120. rasterizer.ReleaseFences();
  121. }
  122. }
  123. template <class P>
  124. void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
  125. RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024);
  126. uncommitted_ranges.subtract(subtract_interval);
  127. pending_ranges.subtract(subtract_interval);
  128. for (auto& interval_set : committed_ranges) {
  129. interval_set.subtract(subtract_interval);
  130. }
  131. }
  132. template <class P>
  133. bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
  134. const std::optional<VAddr> cpu_src_address = gpu_memory->GpuToCpuAddress(src_address);
  135. const std::optional<VAddr> cpu_dest_address = gpu_memory->GpuToCpuAddress(dest_address);
  136. if (!cpu_src_address || !cpu_dest_address) {
  137. return false;
  138. }
  139. const bool source_dirty = IsRegionRegistered(*cpu_src_address, amount);
  140. const bool dest_dirty = IsRegionRegistered(*cpu_dest_address, amount);
  141. if (!source_dirty && !dest_dirty) {
  142. return false;
  143. }
  144. const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
  145. WaitOnAsyncFlushes(*cpu_src_address, static_cast<u32>(amount));
  146. ClearDownload(subtract_interval);
  147. BufferId buffer_a;
  148. BufferId buffer_b;
  149. do {
  150. has_deleted_buffers = false;
  151. buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount));
  152. buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount));
  153. } while (has_deleted_buffers);
  154. auto& src_buffer = slot_buffers[buffer_a];
  155. auto& dest_buffer = slot_buffers[buffer_b];
  156. SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount));
  157. SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount));
  158. std::array copies{BufferCopy{
  159. .src_offset = src_buffer.Offset(*cpu_src_address),
  160. .dst_offset = dest_buffer.Offset(*cpu_dest_address),
  161. .size = amount,
  162. }};
  163. boost::container::small_vector<IntervalType, 4> tmp_intervals;
  164. auto mirror = [&](VAddr base_address, VAddr base_address_end) {
  165. const u64 size = base_address_end - base_address;
  166. const VAddr diff = base_address - *cpu_src_address;
  167. const VAddr new_base_address = *cpu_dest_address + diff;
  168. const IntervalType add_interval{new_base_address, new_base_address + size};
  169. tmp_intervals.push_back(add_interval);
  170. uncommitted_ranges.add(add_interval);
  171. pending_ranges.add(add_interval);
  172. };
  173. ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror);
  174. // This subtraction in this order is important for overlapping copies.
  175. common_ranges.subtract(subtract_interval);
  176. const bool has_new_downloads = tmp_intervals.size() != 0;
  177. for (const IntervalType& add_interval : tmp_intervals) {
  178. common_ranges.add(add_interval);
  179. }
  180. runtime.CopyBuffer(dest_buffer, src_buffer, copies);
  181. if (has_new_downloads) {
  182. memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
  183. }
  184. std::vector<u8> tmp_buffer(amount);
  185. cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
  186. cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
  187. return true;
  188. }
  189. template <class P>
  190. bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
  191. const std::optional<VAddr> cpu_dst_address = gpu_memory->GpuToCpuAddress(dst_address);
  192. if (!cpu_dst_address) {
  193. return false;
  194. }
  195. const bool dest_dirty = IsRegionRegistered(*cpu_dst_address, amount);
  196. if (!dest_dirty) {
  197. return false;
  198. }
  199. const size_t size = amount * sizeof(u32);
  200. const IntervalType subtract_interval{*cpu_dst_address, *cpu_dst_address + size};
  201. ClearDownload(subtract_interval);
  202. common_ranges.subtract(subtract_interval);
  203. const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
  204. auto& dest_buffer = slot_buffers[buffer];
  205. const u32 offset = dest_buffer.Offset(*cpu_dst_address);
  206. runtime.ClearBuffer(dest_buffer, offset, size, value);
  207. return true;
  208. }
  209. template <class P>
  210. std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_addr, u32 size,
  211. ObtainBufferSynchronize sync_info,
  212. ObtainBufferOperation post_op) {
  213. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
  214. if (!cpu_addr) {
  215. return {&slot_buffers[NULL_BUFFER_ID], 0};
  216. }
  217. const BufferId buffer_id = FindBuffer(*cpu_addr, size);
  218. Buffer& buffer = slot_buffers[buffer_id];
  219. // synchronize op
  220. switch (sync_info) {
  221. case ObtainBufferSynchronize::FullSynchronize:
  222. SynchronizeBuffer(buffer, *cpu_addr, size);
  223. break;
  224. default:
  225. break;
  226. }
  227. switch (post_op) {
  228. case ObtainBufferOperation::MarkAsWritten:
  229. MarkWrittenBuffer(buffer_id, *cpu_addr, size);
  230. break;
  231. case ObtainBufferOperation::DiscardWrite: {
  232. IntervalType interval{*cpu_addr, size};
  233. ClearDownload(interval);
  234. break;
  235. }
  236. default:
  237. break;
  238. }
  239. return {&buffer, buffer.Offset(*cpu_addr)};
  240. }
  241. template <class P>
  242. void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
  243. u32 size) {
  244. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
  245. const Binding binding{
  246. .cpu_addr = *cpu_addr,
  247. .size = size,
  248. .buffer_id = BufferId{},
  249. };
  250. uniform_buffers[stage][index] = binding;
  251. }
  252. template <class P>
  253. void BufferCache<P>::DisableGraphicsUniformBuffer(size_t stage, u32 index) {
  254. uniform_buffers[stage][index] = NULL_BINDING;
  255. }
  256. template <class P>
  257. void BufferCache<P>::UpdateGraphicsBuffers(bool is_indexed) {
  258. MICROPROFILE_SCOPE(GPU_PrepareBuffers);
  259. do {
  260. has_deleted_buffers = false;
  261. DoUpdateGraphicsBuffers(is_indexed);
  262. } while (has_deleted_buffers);
  263. }
  264. template <class P>
  265. void BufferCache<P>::UpdateComputeBuffers() {
  266. MICROPROFILE_SCOPE(GPU_PrepareBuffers);
  267. do {
  268. has_deleted_buffers = false;
  269. DoUpdateComputeBuffers();
  270. } while (has_deleted_buffers);
  271. }
  272. template <class P>
  273. void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
  274. MICROPROFILE_SCOPE(GPU_BindUploadBuffers);
  275. if (is_indexed) {
  276. BindHostIndexBuffer();
  277. } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
  278. const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
  279. if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
  280. draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
  281. runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
  282. draw_state.vertex_buffer.count);
  283. }
  284. }
  285. BindHostVertexBuffers();
  286. BindHostTransformFeedbackBuffers();
  287. if (current_draw_indirect) {
  288. BindHostDrawIndirectBuffers();
  289. }
  290. }
  291. template <class P>
  292. void BufferCache<P>::BindHostStageBuffers(size_t stage) {
  293. MICROPROFILE_SCOPE(GPU_BindUploadBuffers);
  294. BindHostGraphicsUniformBuffers(stage);
  295. BindHostGraphicsStorageBuffers(stage);
  296. BindHostGraphicsTextureBuffers(stage);
  297. }
  298. template <class P>
  299. void BufferCache<P>::BindHostComputeBuffers() {
  300. MICROPROFILE_SCOPE(GPU_BindUploadBuffers);
  301. BindHostComputeUniformBuffers();
  302. BindHostComputeStorageBuffers();
  303. BindHostComputeTextureBuffers();
  304. }
  305. template <class P>
  306. void BufferCache<P>::SetUniformBuffersState(const std::array<u32, NUM_STAGES>& mask,
  307. const UniformBufferSizes* sizes) {
  308. if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
  309. if (enabled_uniform_buffer_masks != mask) {
  310. if constexpr (IS_OPENGL) {
  311. fast_bound_uniform_buffers.fill(0);
  312. }
  313. dirty_uniform_buffers.fill(~u32{0});
  314. uniform_buffer_binding_sizes.fill({});
  315. }
  316. }
  317. enabled_uniform_buffer_masks = mask;
  318. uniform_buffer_sizes = sizes;
  319. }
  320. template <class P>
  321. void BufferCache<P>::SetComputeUniformBufferState(u32 mask,
  322. const ComputeUniformBufferSizes* sizes) {
  323. enabled_compute_uniform_buffer_mask = mask;
  324. compute_uniform_buffer_sizes = sizes;
  325. }
  326. template <class P>
  327. void BufferCache<P>::UnbindGraphicsStorageBuffers(size_t stage) {
  328. enabled_storage_buffers[stage] = 0;
  329. written_storage_buffers[stage] = 0;
  330. }
  331. template <class P>
  332. void BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
  333. u32 cbuf_offset, bool is_written) {
  334. enabled_storage_buffers[stage] |= 1U << ssbo_index;
  335. written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index;
  336. const auto& cbufs = maxwell3d->state.shader_stages[stage];
  337. const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
  338. storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
  339. }
  340. template <class P>
  341. void BufferCache<P>::UnbindGraphicsTextureBuffers(size_t stage) {
  342. enabled_texture_buffers[stage] = 0;
  343. written_texture_buffers[stage] = 0;
  344. image_texture_buffers[stage] = 0;
  345. }
  346. template <class P>
  347. void BufferCache<P>::BindGraphicsTextureBuffer(size_t stage, size_t tbo_index, GPUVAddr gpu_addr,
  348. u32 size, PixelFormat format, bool is_written,
  349. bool is_image) {
  350. enabled_texture_buffers[stage] |= 1U << tbo_index;
  351. written_texture_buffers[stage] |= (is_written ? 1U : 0U) << tbo_index;
  352. if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
  353. image_texture_buffers[stage] |= (is_image ? 1U : 0U) << tbo_index;
  354. }
  355. texture_buffers[stage][tbo_index] = GetTextureBufferBinding(gpu_addr, size, format);
  356. }
  357. template <class P>
  358. void BufferCache<P>::UnbindComputeStorageBuffers() {
  359. enabled_compute_storage_buffers = 0;
  360. written_compute_storage_buffers = 0;
  361. image_compute_texture_buffers = 0;
  362. }
  363. template <class P>
  364. void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
  365. bool is_written) {
  366. enabled_compute_storage_buffers |= 1U << ssbo_index;
  367. written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index;
  368. const auto& launch_desc = kepler_compute->launch_description;
  369. ASSERT(((launch_desc.const_buffer_enable_mask >> cbuf_index) & 1) != 0);
  370. const auto& cbufs = launch_desc.const_buffer_config;
  371. const GPUVAddr ssbo_addr = cbufs[cbuf_index].Address() + cbuf_offset;
  372. compute_storage_buffers[ssbo_index] = StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
  373. }
  374. template <class P>
  375. void BufferCache<P>::UnbindComputeTextureBuffers() {
  376. enabled_compute_texture_buffers = 0;
  377. written_compute_texture_buffers = 0;
  378. image_compute_texture_buffers = 0;
  379. }
  380. template <class P>
  381. void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size,
  382. PixelFormat format, bool is_written, bool is_image) {
  383. enabled_compute_texture_buffers |= 1U << tbo_index;
  384. written_compute_texture_buffers |= (is_written ? 1U : 0U) << tbo_index;
  385. if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
  386. image_compute_texture_buffers |= (is_image ? 1U : 0U) << tbo_index;
  387. }
  388. compute_texture_buffers[tbo_index] = GetTextureBufferBinding(gpu_addr, size, format);
  389. }
  390. template <class P>
  391. void BufferCache<P>::FlushCachedWrites() {
  392. cached_write_buffer_ids.clear();
  393. memory_tracker.FlushCachedWrites();
  394. cached_ranges.clear();
  395. }
  396. template <class P>
  397. bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
  398. return !uncommitted_ranges.empty() || !committed_ranges.empty();
  399. }
  400. template <class P>
  401. void BufferCache<P>::AccumulateFlushes() {
  402. if (uncommitted_ranges.empty()) {
  403. return;
  404. }
  405. committed_ranges.emplace_back(std::move(uncommitted_ranges));
  406. }
  407. template <class P>
  408. bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
  409. if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
  410. return (!async_buffers.empty() && async_buffers.front().has_value());
  411. } else {
  412. return false;
  413. }
  414. }
  415. template <class P>
  416. void BufferCache<P>::CommitAsyncFlushesHigh() {
  417. AccumulateFlushes();
  418. if (committed_ranges.empty()) {
  419. if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
  420. if (active_async_buffers) {
  421. async_buffers.emplace_back(std::optional<Async_Buffer>{});
  422. }
  423. }
  424. return;
  425. }
  426. MICROPROFILE_SCOPE(GPU_DownloadMemory);
  427. pending_ranges.clear();
  428. auto it = committed_ranges.begin();
  429. while (it != committed_ranges.end()) {
  430. auto& current_intervals = *it;
  431. auto next_it = std::next(it);
  432. while (next_it != committed_ranges.end()) {
  433. for (auto& interval : *next_it) {
  434. current_intervals.subtract(interval);
  435. }
  436. next_it++;
  437. }
  438. it++;
  439. }
  440. boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
  441. u64 total_size_bytes = 0;
  442. u64 largest_copy = 0;
  443. for (const IntervalSet& intervals : committed_ranges) {
  444. for (auto& interval : intervals) {
  445. const std::size_t size = interval.upper() - interval.lower();
  446. const VAddr cpu_addr = interval.lower();
  447. ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
  448. const VAddr buffer_start = buffer.CpuAddr();
  449. const VAddr buffer_end = buffer_start + buffer.SizeBytes();
  450. const VAddr new_start = std::max(buffer_start, cpu_addr);
  451. const VAddr new_end = std::min(buffer_end, cpu_addr + size);
  452. memory_tracker.ForEachDownloadRange(
  453. new_start, new_end - new_start, false, [&](u64 cpu_addr_out, u64 range_size) {
  454. const VAddr buffer_addr = buffer.CpuAddr();
  455. const auto add_download = [&](VAddr start, VAddr end) {
  456. const u64 new_offset = start - buffer_addr;
  457. const u64 new_size = end - start;
  458. downloads.push_back({
  459. BufferCopy{
  460. .src_offset = new_offset,
  461. .dst_offset = total_size_bytes,
  462. .size = new_size,
  463. },
  464. buffer_id,
  465. });
  466. // Align up to avoid cache conflicts
  467. constexpr u64 align = 64ULL;
  468. constexpr u64 mask = ~(align - 1ULL);
  469. total_size_bytes += (new_size + align - 1) & mask;
  470. largest_copy = std::max(largest_copy, new_size);
  471. };
  472. ForEachInRangeSet(common_ranges, cpu_addr_out, range_size, add_download);
  473. });
  474. });
  475. }
  476. }
  477. committed_ranges.clear();
  478. if (downloads.empty()) {
  479. if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
  480. if (active_async_buffers) {
  481. async_buffers.emplace_back(std::optional<Async_Buffer>{});
  482. }
  483. }
  484. return;
  485. }
  486. if (active_async_buffers) {
  487. if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
  488. auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
  489. boost::container::small_vector<BufferCopy, 4> normalized_copies;
  490. IntervalSet new_async_range{};
  491. runtime.PreCopyBarrier();
  492. for (auto& [copy, buffer_id] : downloads) {
  493. copy.dst_offset += download_staging.offset;
  494. const std::array copies{copy};
  495. BufferCopy second_copy{copy};
  496. Buffer& buffer = slot_buffers[buffer_id];
  497. second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
  498. VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
  499. const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
  500. async_downloads += std::make_pair(base_interval, 1);
  501. runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
  502. normalized_copies.push_back(second_copy);
  503. }
  504. runtime.PostCopyBarrier();
  505. pending_downloads.emplace_back(std::move(normalized_copies));
  506. async_buffers.emplace_back(download_staging);
  507. } else {
  508. committed_ranges.clear();
  509. uncommitted_ranges.clear();
  510. }
  511. } else {
  512. if constexpr (USE_MEMORY_MAPS) {
  513. auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
  514. runtime.PreCopyBarrier();
  515. for (auto& [copy, buffer_id] : downloads) {
  516. // Have in mind the staging buffer offset for the copy
  517. copy.dst_offset += download_staging.offset;
  518. const std::array copies{copy};
  519. runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, false);
  520. }
  521. runtime.PostCopyBarrier();
  522. runtime.Finish();
  523. for (const auto& [copy, buffer_id] : downloads) {
  524. const Buffer& buffer = slot_buffers[buffer_id];
  525. const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
  526. // Undo the modified offset
  527. const u64 dst_offset = copy.dst_offset - download_staging.offset;
  528. const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
  529. cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
  530. }
  531. } else {
  532. const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
  533. for (const auto& [copy, buffer_id] : downloads) {
  534. Buffer& buffer = slot_buffers[buffer_id];
  535. buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
  536. const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
  537. cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
  538. }
  539. }
  540. }
  541. }
  542. template <class P>
  543. void BufferCache<P>::CommitAsyncFlushes() {
  544. CommitAsyncFlushesHigh();
  545. }
  546. template <class P>
  547. void BufferCache<P>::PopAsyncFlushes() {
  548. MICROPROFILE_SCOPE(GPU_DownloadMemory);
  549. PopAsyncBuffers();
  550. }
  551. template <class P>
  552. void BufferCache<P>::PopAsyncBuffers() {
  553. if (async_buffers.empty()) {
  554. return;
  555. }
  556. if (!async_buffers.front().has_value()) {
  557. async_buffers.pop_front();
  558. return;
  559. }
  560. if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
  561. auto& downloads = pending_downloads.front();
  562. auto& async_buffer = async_buffers.front();
  563. u8* base = async_buffer->mapped_span.data();
  564. const size_t base_offset = async_buffer->offset;
  565. for (const auto& copy : downloads) {
  566. const VAddr cpu_addr = static_cast<VAddr>(copy.src_offset);
  567. const u64 dst_offset = copy.dst_offset - base_offset;
  568. const u8* read_mapped_memory = base + dst_offset;
  569. ForEachInOverlapCounter(
  570. async_downloads, cpu_addr, copy.size, [&](VAddr start, VAddr end, int count) {
  571. cpu_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - cpu_addr],
  572. end - start);
  573. if (count == 1) {
  574. const IntervalType base_interval{start, end};
  575. common_ranges.subtract(base_interval);
  576. }
  577. });
  578. const IntervalType subtract_interval{cpu_addr, cpu_addr + copy.size};
  579. RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1);
  580. }
  581. async_buffers_death_ring.emplace_back(*async_buffer);
  582. async_buffers.pop_front();
  583. pending_downloads.pop_front();
  584. }
  585. }
  586. template <class P>
  587. bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
  588. bool is_dirty = false;
  589. ForEachInRangeSet(common_ranges, addr, size, [&](VAddr, VAddr) { is_dirty = true; });
  590. return is_dirty;
  591. }
  592. template <class P>
  593. bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) {
  594. const VAddr end_addr = addr + size;
  595. const u64 page_end = Common::DivCeil(end_addr, CACHING_PAGESIZE);
  596. for (u64 page = addr >> CACHING_PAGEBITS; page < page_end;) {
  597. const BufferId buffer_id = page_table[page];
  598. if (!buffer_id) {
  599. ++page;
  600. continue;
  601. }
  602. Buffer& buffer = slot_buffers[buffer_id];
  603. const VAddr buf_start_addr = buffer.CpuAddr();
  604. const VAddr buf_end_addr = buf_start_addr + buffer.SizeBytes();
  605. if (buf_start_addr < end_addr && addr < buf_end_addr) {
  606. return true;
  607. }
  608. page = Common::DivCeil(end_addr, CACHING_PAGESIZE);
  609. }
  610. return false;
  611. }
  612. template <class P>
  613. bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
  614. return memory_tracker.IsRegionCpuModified(addr, size);
  615. }
  616. template <class P>
  617. void BufferCache<P>::BindHostIndexBuffer() {
  618. Buffer& buffer = slot_buffers[index_buffer.buffer_id];
  619. TouchBuffer(buffer, index_buffer.buffer_id);
  620. const u32 offset = buffer.Offset(index_buffer.cpu_addr);
  621. const u32 size = index_buffer.size;
  622. const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
  623. if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
  624. if constexpr (USE_MEMORY_MAPS) {
  625. auto upload_staging = runtime.UploadStagingBuffer(size);
  626. std::array<BufferCopy, 1> copies{
  627. {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
  628. std::memcpy(upload_staging.mapped_span.data(),
  629. draw_state.inline_index_draw_indexes.data(), size);
  630. runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
  631. } else {
  632. buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes);
  633. }
  634. } else {
  635. SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
  636. }
  637. if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
  638. const u32 new_offset =
  639. offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
  640. runtime.BindIndexBuffer(buffer, new_offset, size);
  641. } else {
  642. runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
  643. draw_state.index_buffer.first, draw_state.index_buffer.count,
  644. buffer, offset, size);
  645. }
  646. }
  647. template <class P>
  648. void BufferCache<P>::BindHostVertexBuffers() {
  649. auto& flags = maxwell3d->dirty.flags;
  650. for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
  651. const Binding& binding = vertex_buffers[index];
  652. Buffer& buffer = slot_buffers[binding.buffer_id];
  653. TouchBuffer(buffer, binding.buffer_id);
  654. SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
  655. if (!flags[Dirty::VertexBuffer0 + index]) {
  656. continue;
  657. }
  658. flags[Dirty::VertexBuffer0 + index] = false;
  659. const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
  660. const u32 offset = buffer.Offset(binding.cpu_addr);
  661. runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride);
  662. }
  663. }
  664. template <class P>
  665. void BufferCache<P>::BindHostDrawIndirectBuffers() {
  666. const auto bind_buffer = [this](const Binding& binding) {
  667. Buffer& buffer = slot_buffers[binding.buffer_id];
  668. TouchBuffer(buffer, binding.buffer_id);
  669. SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
  670. };
  671. if (current_draw_indirect->include_count) {
  672. bind_buffer(count_buffer_binding);
  673. }
  674. bind_buffer(indirect_buffer_binding);
  675. }
  676. template <class P>
  677. void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) {
  678. u32 dirty = ~0U;
  679. if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
  680. dirty = std::exchange(dirty_uniform_buffers[stage], 0);
  681. }
  682. u32 binding_index = 0;
  683. ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) {
  684. const bool needs_bind = ((dirty >> index) & 1) != 0;
  685. BindHostGraphicsUniformBuffer(stage, index, binding_index, needs_bind);
  686. if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
  687. ++binding_index;
  688. }
  689. });
  690. }
  691. template <class P>
  692. void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index,
  693. bool needs_bind) {
  694. const Binding& binding = uniform_buffers[stage][index];
  695. const VAddr cpu_addr = binding.cpu_addr;
  696. const u32 size = std::min(binding.size, (*uniform_buffer_sizes)[stage][index]);
  697. Buffer& buffer = slot_buffers[binding.buffer_id];
  698. TouchBuffer(buffer, binding.buffer_id);
  699. const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
  700. size <= uniform_buffer_skip_cache_size &&
  701. !memory_tracker.IsRegionGpuModified(cpu_addr, size);
  702. if (use_fast_buffer) {
  703. if constexpr (IS_OPENGL) {
  704. if (runtime.HasFastBufferSubData()) {
  705. // Fast path for Nvidia
  706. const bool should_fast_bind =
  707. !HasFastUniformBufferBound(stage, binding_index) ||
  708. uniform_buffer_binding_sizes[stage][binding_index] != size;
  709. if (should_fast_bind) {
  710. // We only have to bind when the currently bound buffer is not the fast version
  711. fast_bound_uniform_buffers[stage] |= 1U << binding_index;
  712. uniform_buffer_binding_sizes[stage][binding_index] = size;
  713. runtime.BindFastUniformBuffer(stage, binding_index, size);
  714. }
  715. const auto span = ImmediateBufferWithData(cpu_addr, size);
  716. runtime.PushFastUniformBuffer(stage, binding_index, span);
  717. return;
  718. }
  719. }
  720. if constexpr (IS_OPENGL) {
  721. fast_bound_uniform_buffers[stage] |= 1U << binding_index;
  722. uniform_buffer_binding_sizes[stage][binding_index] = size;
  723. }
  724. // Stream buffer path to avoid stalling on non-Nvidia drivers or Vulkan
  725. const std::span<u8> span = runtime.BindMappedUniformBuffer(stage, binding_index, size);
  726. cpu_memory.ReadBlockUnsafe(cpu_addr, span.data(), size);
  727. return;
  728. }
  729. // Classic cached path
  730. const bool sync_cached = SynchronizeBuffer(buffer, cpu_addr, size);
  731. if (sync_cached) {
  732. ++uniform_cache_hits[0];
  733. }
  734. ++uniform_cache_shots[0];
  735. // Skip binding if it's not needed and if the bound buffer is not the fast version
  736. // This exists to avoid instances where the fast buffer is bound and a GPU write happens
  737. needs_bind |= HasFastUniformBufferBound(stage, binding_index);
  738. if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
  739. needs_bind |= uniform_buffer_binding_sizes[stage][binding_index] != size;
  740. }
  741. if (!needs_bind) {
  742. return;
  743. }
  744. const u32 offset = buffer.Offset(cpu_addr);
  745. if constexpr (IS_OPENGL) {
  746. // Fast buffer will be unbound
  747. fast_bound_uniform_buffers[stage] &= ~(1U << binding_index);
  748. // Mark the index as dirty if offset doesn't match
  749. const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset();
  750. dirty_uniform_buffers[stage] |= (is_copy_bind ? 1U : 0U) << index;
  751. }
  752. if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
  753. uniform_buffer_binding_sizes[stage][binding_index] = size;
  754. }
  755. if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
  756. runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size);
  757. } else {
  758. runtime.BindUniformBuffer(buffer, offset, size);
  759. }
  760. }
  761. template <class P>
  762. void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
  763. u32 binding_index = 0;
  764. ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) {
  765. const Binding& binding = storage_buffers[stage][index];
  766. Buffer& buffer = slot_buffers[binding.buffer_id];
  767. TouchBuffer(buffer, binding.buffer_id);
  768. const u32 size = binding.size;
  769. SynchronizeBuffer(buffer, binding.cpu_addr, size);
  770. const u32 offset = buffer.Offset(binding.cpu_addr);
  771. const bool is_written = ((written_storage_buffers[stage] >> index) & 1) != 0;
  772. if constexpr (NEEDS_BIND_STORAGE_INDEX) {
  773. runtime.BindStorageBuffer(stage, binding_index, buffer, offset, size, is_written);
  774. ++binding_index;
  775. } else {
  776. runtime.BindStorageBuffer(buffer, offset, size, is_written);
  777. }
  778. });
  779. }
  780. template <class P>
  781. void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
  782. ForEachEnabledBit(enabled_texture_buffers[stage], [&](u32 index) {
  783. const TextureBufferBinding& binding = texture_buffers[stage][index];
  784. Buffer& buffer = slot_buffers[binding.buffer_id];
  785. const u32 size = binding.size;
  786. SynchronizeBuffer(buffer, binding.cpu_addr, size);
  787. const u32 offset = buffer.Offset(binding.cpu_addr);
  788. const PixelFormat format = binding.format;
  789. if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
  790. if (((image_texture_buffers[stage] >> index) & 1) != 0) {
  791. runtime.BindImageBuffer(buffer, offset, size, format);
  792. } else {
  793. runtime.BindTextureBuffer(buffer, offset, size, format);
  794. }
  795. } else {
  796. runtime.BindTextureBuffer(buffer, offset, size, format);
  797. }
  798. });
  799. }
  800. template <class P>
  801. void BufferCache<P>::BindHostTransformFeedbackBuffers() {
  802. if (maxwell3d->regs.transform_feedback_enabled == 0) {
  803. return;
  804. }
  805. for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
  806. const Binding& binding = transform_feedback_buffers[index];
  807. Buffer& buffer = slot_buffers[binding.buffer_id];
  808. TouchBuffer(buffer, binding.buffer_id);
  809. const u32 size = binding.size;
  810. SynchronizeBuffer(buffer, binding.cpu_addr, size);
  811. const u32 offset = buffer.Offset(binding.cpu_addr);
  812. runtime.BindTransformFeedbackBuffer(index, buffer, offset, size);
  813. }
  814. }
  815. template <class P>
  816. void BufferCache<P>::BindHostComputeUniformBuffers() {
  817. if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
  818. // Mark all uniform buffers as dirty
  819. dirty_uniform_buffers.fill(~u32{0});
  820. fast_bound_uniform_buffers.fill(0);
  821. }
  822. u32 binding_index = 0;
  823. ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) {
  824. const Binding& binding = compute_uniform_buffers[index];
  825. Buffer& buffer = slot_buffers[binding.buffer_id];
  826. TouchBuffer(buffer, binding.buffer_id);
  827. const u32 size = std::min(binding.size, (*compute_uniform_buffer_sizes)[index]);
  828. SynchronizeBuffer(buffer, binding.cpu_addr, size);
  829. const u32 offset = buffer.Offset(binding.cpu_addr);
  830. if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
  831. runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size);
  832. ++binding_index;
  833. } else {
  834. runtime.BindUniformBuffer(buffer, offset, size);
  835. }
  836. });
  837. }
  838. template <class P>
  839. void BufferCache<P>::BindHostComputeStorageBuffers() {
  840. u32 binding_index = 0;
  841. ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) {
  842. const Binding& binding = compute_storage_buffers[index];
  843. Buffer& buffer = slot_buffers[binding.buffer_id];
  844. TouchBuffer(buffer, binding.buffer_id);
  845. const u32 size = binding.size;
  846. SynchronizeBuffer(buffer, binding.cpu_addr, size);
  847. const u32 offset = buffer.Offset(binding.cpu_addr);
  848. const bool is_written = ((written_compute_storage_buffers >> index) & 1) != 0;
  849. if constexpr (NEEDS_BIND_STORAGE_INDEX) {
  850. runtime.BindComputeStorageBuffer(binding_index, buffer, offset, size, is_written);
  851. ++binding_index;
  852. } else {
  853. runtime.BindStorageBuffer(buffer, offset, size, is_written);
  854. }
  855. });
  856. }
  857. template <class P>
  858. void BufferCache<P>::BindHostComputeTextureBuffers() {
  859. ForEachEnabledBit(enabled_compute_texture_buffers, [&](u32 index) {
  860. const TextureBufferBinding& binding = compute_texture_buffers[index];
  861. Buffer& buffer = slot_buffers[binding.buffer_id];
  862. const u32 size = binding.size;
  863. SynchronizeBuffer(buffer, binding.cpu_addr, size);
  864. const u32 offset = buffer.Offset(binding.cpu_addr);
  865. const PixelFormat format = binding.format;
  866. if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
  867. if (((image_compute_texture_buffers >> index) & 1) != 0) {
  868. runtime.BindImageBuffer(buffer, offset, size, format);
  869. } else {
  870. runtime.BindTextureBuffer(buffer, offset, size, format);
  871. }
  872. } else {
  873. runtime.BindTextureBuffer(buffer, offset, size, format);
  874. }
  875. });
  876. }
  877. template <class P>
  878. void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
  879. do {
  880. has_deleted_buffers = false;
  881. if (is_indexed) {
  882. UpdateIndexBuffer();
  883. }
  884. UpdateVertexBuffers();
  885. UpdateTransformFeedbackBuffers();
  886. for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
  887. UpdateUniformBuffers(stage);
  888. UpdateStorageBuffers(stage);
  889. UpdateTextureBuffers(stage);
  890. }
  891. if (current_draw_indirect) {
  892. UpdateDrawIndirect();
  893. }
  894. } while (has_deleted_buffers);
  895. }
  896. template <class P>
  897. void BufferCache<P>::DoUpdateComputeBuffers() {
  898. UpdateComputeUniformBuffers();
  899. UpdateComputeStorageBuffers();
  900. UpdateComputeTextureBuffers();
  901. }
  902. template <class P>
  903. void BufferCache<P>::UpdateIndexBuffer() {
  904. // We have to check for the dirty flags and index count
  905. // The index count is currently changed without updating the dirty flags
  906. const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
  907. const auto& index_buffer_ref = draw_state.index_buffer;
  908. auto& flags = maxwell3d->dirty.flags;
  909. if (!flags[Dirty::IndexBuffer]) {
  910. return;
  911. }
  912. flags[Dirty::IndexBuffer] = false;
  913. if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
  914. auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size());
  915. u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE);
  916. if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] {
  917. inline_buffer_id = CreateBuffer(0, buffer_size);
  918. }
  919. if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] {
  920. slot_buffers.erase(inline_buffer_id);
  921. inline_buffer_id = CreateBuffer(0, buffer_size);
  922. }
  923. index_buffer = Binding{
  924. .cpu_addr = 0,
  925. .size = inline_index_size,
  926. .buffer_id = inline_buffer_id,
  927. };
  928. return;
  929. }
  930. const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress();
  931. const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress();
  932. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
  933. const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
  934. const u32 draw_size =
  935. (index_buffer_ref.count + index_buffer_ref.first) * index_buffer_ref.FormatSizeInBytes();
  936. const u32 size = std::min(address_size, draw_size);
  937. if (size == 0 || !cpu_addr) {
  938. index_buffer = NULL_BINDING;
  939. return;
  940. }
  941. index_buffer = Binding{
  942. .cpu_addr = *cpu_addr,
  943. .size = size,
  944. .buffer_id = FindBuffer(*cpu_addr, size),
  945. };
  946. }
  947. template <class P>
  948. void BufferCache<P>::UpdateVertexBuffers() {
  949. auto& flags = maxwell3d->dirty.flags;
  950. if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) {
  951. return;
  952. }
  953. flags[Dirty::VertexBuffers] = false;
  954. for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
  955. UpdateVertexBuffer(index);
  956. }
  957. }
  958. template <class P>
  959. void BufferCache<P>::UpdateVertexBuffer(u32 index) {
  960. if (!maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) {
  961. return;
  962. }
  963. const auto& array = maxwell3d->regs.vertex_streams[index];
  964. const auto& limit = maxwell3d->regs.vertex_stream_limits[index];
  965. const GPUVAddr gpu_addr_begin = array.Address();
  966. const GPUVAddr gpu_addr_end = limit.Address() + 1;
  967. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
  968. const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
  969. u32 size = address_size; // TODO: Analyze stride and number of vertices
  970. if (array.enable == 0 || size == 0 || !cpu_addr) {
  971. vertex_buffers[index] = NULL_BINDING;
  972. return;
  973. }
  974. if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
  975. size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
  976. }
  977. vertex_buffers[index] = Binding{
  978. .cpu_addr = *cpu_addr,
  979. .size = size,
  980. .buffer_id = FindBuffer(*cpu_addr, size),
  981. };
  982. }
  983. template <class P>
  984. void BufferCache<P>::UpdateDrawIndirect() {
  985. const auto update = [this](GPUVAddr gpu_addr, size_t size, Binding& binding) {
  986. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
  987. if (!cpu_addr) {
  988. binding = NULL_BINDING;
  989. return;
  990. }
  991. binding = Binding{
  992. .cpu_addr = *cpu_addr,
  993. .size = static_cast<u32>(size),
  994. .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
  995. };
  996. };
  997. if (current_draw_indirect->include_count) {
  998. update(current_draw_indirect->count_start_address, sizeof(u32), count_buffer_binding);
  999. }
  1000. update(current_draw_indirect->indirect_start_address, current_draw_indirect->buffer_size,
  1001. indirect_buffer_binding);
  1002. }
  1003. template <class P>
  1004. void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
  1005. ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) {
  1006. Binding& binding = uniform_buffers[stage][index];
  1007. if (binding.buffer_id) {
  1008. // Already updated
  1009. return;
  1010. }
  1011. // Mark as dirty
  1012. if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
  1013. dirty_uniform_buffers[stage] |= 1U << index;
  1014. }
  1015. // Resolve buffer
  1016. binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size);
  1017. });
  1018. }
  1019. template <class P>
  1020. void BufferCache<P>::UpdateStorageBuffers(size_t stage) {
  1021. const u32 written_mask = written_storage_buffers[stage];
  1022. ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) {
  1023. // Resolve buffer
  1024. Binding& binding = storage_buffers[stage][index];
  1025. const BufferId buffer_id = FindBuffer(binding.cpu_addr, binding.size);
  1026. binding.buffer_id = buffer_id;
  1027. // Mark buffer as written if needed
  1028. if (((written_mask >> index) & 1) != 0) {
  1029. MarkWrittenBuffer(buffer_id, binding.cpu_addr, binding.size);
  1030. }
  1031. });
  1032. }
  1033. template <class P>
  1034. void BufferCache<P>::UpdateTextureBuffers(size_t stage) {
  1035. ForEachEnabledBit(enabled_texture_buffers[stage], [&](u32 index) {
  1036. Binding& binding = texture_buffers[stage][index];
  1037. binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size);
  1038. // Mark buffer as written if needed
  1039. if (((written_texture_buffers[stage] >> index) & 1) != 0) {
  1040. MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size);
  1041. }
  1042. });
  1043. }
  1044. template <class P>
  1045. void BufferCache<P>::UpdateTransformFeedbackBuffers() {
  1046. if (maxwell3d->regs.transform_feedback_enabled == 0) {
  1047. return;
  1048. }
  1049. for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
  1050. UpdateTransformFeedbackBuffer(index);
  1051. }
  1052. }
  1053. template <class P>
  1054. void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) {
  1055. const auto& binding = maxwell3d->regs.transform_feedback.buffers[index];
  1056. const GPUVAddr gpu_addr = binding.Address() + binding.start_offset;
  1057. const u32 size = binding.size;
  1058. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
  1059. if (binding.enable == 0 || size == 0 || !cpu_addr) {
  1060. transform_feedback_buffers[index] = NULL_BINDING;
  1061. return;
  1062. }
  1063. const BufferId buffer_id = FindBuffer(*cpu_addr, size);
  1064. transform_feedback_buffers[index] = Binding{
  1065. .cpu_addr = *cpu_addr,
  1066. .size = size,
  1067. .buffer_id = buffer_id,
  1068. };
  1069. MarkWrittenBuffer(buffer_id, *cpu_addr, size);
  1070. }
  1071. template <class P>
  1072. void BufferCache<P>::UpdateComputeUniformBuffers() {
  1073. ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) {
  1074. Binding& binding = compute_uniform_buffers[index];
  1075. binding = NULL_BINDING;
  1076. const auto& launch_desc = kepler_compute->launch_description;
  1077. if (((launch_desc.const_buffer_enable_mask >> index) & 1) != 0) {
  1078. const auto& cbuf = launch_desc.const_buffer_config[index];
  1079. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(cbuf.Address());
  1080. if (cpu_addr) {
  1081. binding.cpu_addr = *cpu_addr;
  1082. binding.size = cbuf.size;
  1083. }
  1084. }
  1085. binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size);
  1086. });
  1087. }
  1088. template <class P>
  1089. void BufferCache<P>::UpdateComputeStorageBuffers() {
  1090. ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) {
  1091. // Resolve buffer
  1092. Binding& binding = compute_storage_buffers[index];
  1093. binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size);
  1094. // Mark as written if needed
  1095. if (((written_compute_storage_buffers >> index) & 1) != 0) {
  1096. MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size);
  1097. }
  1098. });
  1099. }
  1100. template <class P>
  1101. void BufferCache<P>::UpdateComputeTextureBuffers() {
  1102. ForEachEnabledBit(enabled_compute_texture_buffers, [&](u32 index) {
  1103. Binding& binding = compute_texture_buffers[index];
  1104. binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size);
  1105. // Mark as written if needed
  1106. if (((written_compute_texture_buffers >> index) & 1) != 0) {
  1107. MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size);
  1108. }
  1109. });
  1110. }
  1111. template <class P>
  1112. void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) {
  1113. memory_tracker.MarkRegionAsGpuModified(cpu_addr, size);
  1114. if (memory_tracker.IsRegionCpuModified(cpu_addr, size)) {
  1115. SynchronizeBuffer(slot_buffers[buffer_id], cpu_addr, size);
  1116. }
  1117. const IntervalType base_interval{cpu_addr, cpu_addr + size};
  1118. common_ranges.add(base_interval);
  1119. uncommitted_ranges.add(base_interval);
  1120. pending_ranges.add(base_interval);
  1121. }
  1122. template <class P>
  1123. BufferId BufferCache<P>::FindBuffer(VAddr cpu_addr, u32 size) {
  1124. if (cpu_addr == 0) {
  1125. return NULL_BUFFER_ID;
  1126. }
  1127. const u64 page = cpu_addr >> CACHING_PAGEBITS;
  1128. const BufferId buffer_id = page_table[page];
  1129. if (!buffer_id) {
  1130. return CreateBuffer(cpu_addr, size);
  1131. }
  1132. const Buffer& buffer = slot_buffers[buffer_id];
  1133. if (buffer.IsInBounds(cpu_addr, size)) {
  1134. return buffer_id;
  1135. }
  1136. return CreateBuffer(cpu_addr, size);
  1137. }
  1138. template <class P>
  1139. typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr,
  1140. u32 wanted_size) {
  1141. static constexpr int STREAM_LEAP_THRESHOLD = 16;
  1142. std::vector<BufferId> overlap_ids;
  1143. VAddr begin = cpu_addr;
  1144. VAddr end = cpu_addr + wanted_size;
  1145. int stream_score = 0;
  1146. bool has_stream_leap = false;
  1147. if (begin == 0) {
  1148. return OverlapResult{
  1149. .ids = std::move(overlap_ids),
  1150. .begin = begin,
  1151. .end = end,
  1152. .has_stream_leap = has_stream_leap,
  1153. };
  1154. }
  1155. for (; cpu_addr >> CACHING_PAGEBITS < Common::DivCeil(end, CACHING_PAGESIZE);
  1156. cpu_addr += CACHING_PAGESIZE) {
  1157. const BufferId overlap_id = page_table[cpu_addr >> CACHING_PAGEBITS];
  1158. if (!overlap_id) {
  1159. continue;
  1160. }
  1161. Buffer& overlap = slot_buffers[overlap_id];
  1162. if (overlap.IsPicked()) {
  1163. continue;
  1164. }
  1165. overlap_ids.push_back(overlap_id);
  1166. overlap.Pick();
  1167. const VAddr overlap_cpu_addr = overlap.CpuAddr();
  1168. const bool expands_left = overlap_cpu_addr < begin;
  1169. if (expands_left) {
  1170. cpu_addr = begin = overlap_cpu_addr;
  1171. }
  1172. const VAddr overlap_end = overlap_cpu_addr + overlap.SizeBytes();
  1173. const bool expands_right = overlap_end > end;
  1174. if (overlap_end > end) {
  1175. end = overlap_end;
  1176. }
  1177. stream_score += overlap.StreamScore();
  1178. if (stream_score > STREAM_LEAP_THRESHOLD && !has_stream_leap) {
  1179. // When this memory region has been joined a bunch of times, we assume it's being used
  1180. // as a stream buffer. Increase the size to skip constantly recreating buffers.
  1181. has_stream_leap = true;
  1182. if (expands_right) {
  1183. begin -= CACHING_PAGESIZE * 256;
  1184. cpu_addr = begin;
  1185. }
  1186. if (expands_left) {
  1187. end += CACHING_PAGESIZE * 256;
  1188. }
  1189. }
  1190. }
  1191. return OverlapResult{
  1192. .ids = std::move(overlap_ids),
  1193. .begin = begin,
  1194. .end = end,
  1195. .has_stream_leap = has_stream_leap,
  1196. };
  1197. }
  1198. template <class P>
  1199. void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
  1200. bool accumulate_stream_score) {
  1201. Buffer& new_buffer = slot_buffers[new_buffer_id];
  1202. Buffer& overlap = slot_buffers[overlap_id];
  1203. if (accumulate_stream_score) {
  1204. new_buffer.IncreaseStreamScore(overlap.StreamScore() + 1);
  1205. }
  1206. boost::container::small_vector<BufferCopy, 1> copies;
  1207. const size_t dst_base_offset = overlap.CpuAddr() - new_buffer.CpuAddr();
  1208. copies.push_back(BufferCopy{
  1209. .src_offset = 0,
  1210. .dst_offset = dst_base_offset,
  1211. .size = overlap.SizeBytes(),
  1212. });
  1213. runtime.CopyBuffer(new_buffer, overlap, copies);
  1214. DeleteBuffer(overlap_id, true);
  1215. }
  1216. template <class P>
  1217. BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
  1218. VAddr cpu_addr_end = Common::AlignUp(cpu_addr + wanted_size, CACHING_PAGESIZE);
  1219. cpu_addr = Common::AlignDown(cpu_addr, CACHING_PAGESIZE);
  1220. wanted_size = static_cast<u32>(cpu_addr_end - cpu_addr);
  1221. const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size);
  1222. const u32 size = static_cast<u32>(overlap.end - overlap.begin);
  1223. const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
  1224. auto& new_buffer = slot_buffers[new_buffer_id];
  1225. runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0);
  1226. for (const BufferId overlap_id : overlap.ids) {
  1227. JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
  1228. }
  1229. Register(new_buffer_id);
  1230. TouchBuffer(new_buffer, new_buffer_id);
  1231. return new_buffer_id;
  1232. }
  1233. template <class P>
  1234. void BufferCache<P>::Register(BufferId buffer_id) {
  1235. ChangeRegister<true>(buffer_id);
  1236. }
  1237. template <class P>
  1238. void BufferCache<P>::Unregister(BufferId buffer_id) {
  1239. ChangeRegister<false>(buffer_id);
  1240. }
  1241. template <class P>
  1242. template <bool insert>
  1243. void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
  1244. Buffer& buffer = slot_buffers[buffer_id];
  1245. const auto size = buffer.SizeBytes();
  1246. if (insert) {
  1247. total_used_memory += Common::AlignUp(size, 1024);
  1248. buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick));
  1249. } else {
  1250. total_used_memory -= Common::AlignUp(size, 1024);
  1251. lru_cache.Free(buffer.getLRUID());
  1252. }
  1253. const VAddr cpu_addr_begin = buffer.CpuAddr();
  1254. const VAddr cpu_addr_end = cpu_addr_begin + size;
  1255. const u64 page_begin = cpu_addr_begin / CACHING_PAGESIZE;
  1256. const u64 page_end = Common::DivCeil(cpu_addr_end, CACHING_PAGESIZE);
  1257. for (u64 page = page_begin; page != page_end; ++page) {
  1258. if constexpr (insert) {
  1259. page_table[page] = buffer_id;
  1260. } else {
  1261. page_table[page] = BufferId{};
  1262. }
  1263. }
  1264. }
  1265. template <class P>
  1266. void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
  1267. if (buffer_id != NULL_BUFFER_ID) {
  1268. lru_cache.Touch(buffer.getLRUID(), frame_tick);
  1269. }
  1270. }
  1271. template <class P>
  1272. bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
  1273. return SynchronizeBufferImpl(buffer, cpu_addr, size);
  1274. }
  1275. template <class P>
  1276. bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
  1277. boost::container::small_vector<BufferCopy, 4> copies;
  1278. u64 total_size_bytes = 0;
  1279. u64 largest_copy = 0;
  1280. VAddr buffer_start = buffer.CpuAddr();
  1281. memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
  1282. copies.push_back(BufferCopy{
  1283. .src_offset = total_size_bytes,
  1284. .dst_offset = cpu_addr_out - buffer_start,
  1285. .size = range_size,
  1286. });
  1287. total_size_bytes += range_size;
  1288. largest_copy = std::max(largest_copy, range_size);
  1289. });
  1290. if (total_size_bytes == 0) {
  1291. return true;
  1292. }
  1293. const std::span<BufferCopy> copies_span(copies.data(), copies.size());
  1294. UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
  1295. return false;
  1296. }
  1297. template <class P>
  1298. bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) {
  1299. boost::container::small_vector<BufferCopy, 4> copies;
  1300. u64 total_size_bytes = 0;
  1301. u64 largest_copy = 0;
  1302. IntervalSet found_sets{};
  1303. auto make_copies = [&] {
  1304. for (auto& interval : found_sets) {
  1305. const std::size_t sub_size = interval.upper() - interval.lower();
  1306. const VAddr cpu_addr_ = interval.lower();
  1307. copies.push_back(BufferCopy{
  1308. .src_offset = total_size_bytes,
  1309. .dst_offset = cpu_addr_ - buffer.CpuAddr(),
  1310. .size = sub_size,
  1311. });
  1312. total_size_bytes += sub_size;
  1313. largest_copy = std::max<u64>(largest_copy, sub_size);
  1314. }
  1315. const std::span<BufferCopy> copies_span(copies.data(), copies.size());
  1316. UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
  1317. };
  1318. memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
  1319. const VAddr base_adr = cpu_addr_out;
  1320. const VAddr end_adr = base_adr + range_size;
  1321. const IntervalType add_interval{base_adr, end_adr};
  1322. found_sets.add(add_interval);
  1323. });
  1324. if (found_sets.empty()) {
  1325. return true;
  1326. }
  1327. const IntervalType search_interval{cpu_addr, cpu_addr + size};
  1328. auto it = common_ranges.lower_bound(search_interval);
  1329. auto it_end = common_ranges.upper_bound(search_interval);
  1330. if (it == common_ranges.end()) {
  1331. make_copies();
  1332. return false;
  1333. }
  1334. while (it != it_end) {
  1335. found_sets.subtract(*it);
  1336. it++;
  1337. }
  1338. make_copies();
  1339. return false;
  1340. }
  1341. template <class P>
  1342. void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
  1343. std::span<BufferCopy> copies) {
  1344. if constexpr (USE_MEMORY_MAPS) {
  1345. MappedUploadMemory(buffer, total_size_bytes, copies);
  1346. } else {
  1347. ImmediateUploadMemory(buffer, largest_copy, copies);
  1348. }
  1349. }
  1350. template <class P>
  1351. void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
  1352. [[maybe_unused]] u64 largest_copy,
  1353. [[maybe_unused]] std::span<const BufferCopy> copies) {
  1354. if constexpr (!USE_MEMORY_MAPS) {
  1355. std::span<u8> immediate_buffer;
  1356. for (const BufferCopy& copy : copies) {
  1357. std::span<const u8> upload_span;
  1358. const VAddr cpu_addr = buffer.CpuAddr() + copy.dst_offset;
  1359. if (IsRangeGranular(cpu_addr, copy.size)) {
  1360. upload_span = std::span(cpu_memory.GetPointer(cpu_addr), copy.size);
  1361. } else {
  1362. if (immediate_buffer.empty()) {
  1363. immediate_buffer = ImmediateBuffer(largest_copy);
  1364. }
  1365. cpu_memory.ReadBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
  1366. upload_span = immediate_buffer.subspan(0, copy.size);
  1367. }
  1368. buffer.ImmediateUpload(copy.dst_offset, upload_span);
  1369. }
  1370. }
  1371. }
  1372. template <class P>
  1373. void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
  1374. [[maybe_unused]] u64 total_size_bytes,
  1375. [[maybe_unused]] std::span<BufferCopy> copies) {
  1376. if constexpr (USE_MEMORY_MAPS) {
  1377. auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes);
  1378. const std::span<u8> staging_pointer = upload_staging.mapped_span;
  1379. for (BufferCopy& copy : copies) {
  1380. u8* const src_pointer = staging_pointer.data() + copy.src_offset;
  1381. const VAddr cpu_addr = buffer.CpuAddr() + copy.dst_offset;
  1382. cpu_memory.ReadBlockUnsafe(cpu_addr, src_pointer, copy.size);
  1383. // Apply the staging offset
  1384. copy.src_offset += upload_staging.offset;
  1385. }
  1386. runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
  1387. }
  1388. }
  1389. template <class P>
  1390. bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
  1391. std::span<const u8> inlined_buffer) {
  1392. const bool is_dirty = IsRegionRegistered(dest_address, copy_size);
  1393. if (!is_dirty) {
  1394. return false;
  1395. }
  1396. VAddr aligned_start = Common::AlignDown(dest_address, YUZU_PAGESIZE);
  1397. VAddr aligned_end = Common::AlignUp(dest_address + copy_size, YUZU_PAGESIZE);
  1398. if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) {
  1399. return false;
  1400. }
  1401. const IntervalType subtract_interval{dest_address, dest_address + copy_size};
  1402. ClearDownload(subtract_interval);
  1403. common_ranges.subtract(subtract_interval);
  1404. BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size));
  1405. auto& buffer = slot_buffers[buffer_id];
  1406. SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
  1407. if constexpr (USE_MEMORY_MAPS) {
  1408. auto upload_staging = runtime.UploadStagingBuffer(copy_size);
  1409. std::array copies{BufferCopy{
  1410. .src_offset = upload_staging.offset,
  1411. .dst_offset = buffer.Offset(dest_address),
  1412. .size = copy_size,
  1413. }};
  1414. u8* const src_pointer = upload_staging.mapped_span.data();
  1415. std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
  1416. runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
  1417. } else {
  1418. buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size));
  1419. }
  1420. return true;
  1421. }
  1422. template <class P>
  1423. void BufferCache<P>::DownloadBufferMemory(Buffer& buffer) {
  1424. DownloadBufferMemory(buffer, buffer.CpuAddr(), buffer.SizeBytes());
  1425. }
  1426. template <class P>
  1427. void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 size) {
  1428. boost::container::small_vector<BufferCopy, 1> copies;
  1429. u64 total_size_bytes = 0;
  1430. u64 largest_copy = 0;
  1431. memory_tracker.ForEachDownloadRangeAndClear(
  1432. cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
  1433. const VAddr buffer_addr = buffer.CpuAddr();
  1434. const auto add_download = [&](VAddr start, VAddr end) {
  1435. const u64 new_offset = start - buffer_addr;
  1436. const u64 new_size = end - start;
  1437. copies.push_back(BufferCopy{
  1438. .src_offset = new_offset,
  1439. .dst_offset = total_size_bytes,
  1440. .size = new_size,
  1441. });
  1442. // Align up to avoid cache conflicts
  1443. constexpr u64 align = 64ULL;
  1444. constexpr u64 mask = ~(align - 1ULL);
  1445. total_size_bytes += (new_size + align - 1) & mask;
  1446. largest_copy = std::max(largest_copy, new_size);
  1447. };
  1448. const VAddr start_address = cpu_addr_out;
  1449. const VAddr end_address = start_address + range_size;
  1450. ForEachInRangeSet(common_ranges, start_address, range_size, add_download);
  1451. const IntervalType subtract_interval{start_address, end_address};
  1452. ClearDownload(subtract_interval);
  1453. common_ranges.subtract(subtract_interval);
  1454. });
  1455. if (total_size_bytes == 0) {
  1456. return;
  1457. }
  1458. MICROPROFILE_SCOPE(GPU_DownloadMemory);
  1459. if constexpr (USE_MEMORY_MAPS) {
  1460. auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
  1461. const u8* const mapped_memory = download_staging.mapped_span.data();
  1462. const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
  1463. for (BufferCopy& copy : copies) {
  1464. // Modify copies to have the staging offset in mind
  1465. copy.dst_offset += download_staging.offset;
  1466. }
  1467. runtime.CopyBuffer(download_staging.buffer, buffer, copies_span);
  1468. runtime.Finish();
  1469. for (const BufferCopy& copy : copies) {
  1470. const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
  1471. // Undo the modified offset
  1472. const u64 dst_offset = copy.dst_offset - download_staging.offset;
  1473. const u8* copy_mapped_memory = mapped_memory + dst_offset;
  1474. cpu_memory.WriteBlockUnsafe(copy_cpu_addr, copy_mapped_memory, copy.size);
  1475. }
  1476. } else {
  1477. const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
  1478. for (const BufferCopy& copy : copies) {
  1479. buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
  1480. const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
  1481. cpu_memory.WriteBlockUnsafe(copy_cpu_addr, immediate_buffer.data(), copy.size);
  1482. }
  1483. }
  1484. }
  1485. template <class P>
  1486. void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
  1487. const auto scalar_replace = [buffer_id](Binding& binding) {
  1488. if (binding.buffer_id == buffer_id) {
  1489. binding.buffer_id = BufferId{};
  1490. }
  1491. };
  1492. const auto replace = [scalar_replace](std::span<Binding> bindings) {
  1493. std::ranges::for_each(bindings, scalar_replace);
  1494. };
  1495. scalar_replace(index_buffer);
  1496. replace(vertex_buffers);
  1497. std::ranges::for_each(uniform_buffers, replace);
  1498. std::ranges::for_each(storage_buffers, replace);
  1499. replace(transform_feedback_buffers);
  1500. replace(compute_uniform_buffers);
  1501. replace(compute_storage_buffers);
  1502. std::erase(cached_write_buffer_ids, buffer_id);
  1503. // Mark the whole buffer as CPU written to stop tracking CPU writes
  1504. if (!do_not_mark) {
  1505. Buffer& buffer = slot_buffers[buffer_id];
  1506. memory_tracker.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes());
  1507. }
  1508. Unregister(buffer_id);
  1509. delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
  1510. slot_buffers.erase(buffer_id);
  1511. NotifyBufferDeletion();
  1512. }
  1513. template <class P>
  1514. void BufferCache<P>::NotifyBufferDeletion() {
  1515. if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
  1516. dirty_uniform_buffers.fill(~u32{0});
  1517. uniform_buffer_binding_sizes.fill({});
  1518. }
  1519. auto& flags = maxwell3d->dirty.flags;
  1520. flags[Dirty::IndexBuffer] = true;
  1521. flags[Dirty::VertexBuffers] = true;
  1522. for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
  1523. flags[Dirty::VertexBuffer0 + index] = true;
  1524. }
  1525. has_deleted_buffers = true;
  1526. }
  1527. template <class P>
  1528. typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr,
  1529. u32 cbuf_index,
  1530. bool is_written) const {
  1531. const GPUVAddr gpu_addr = gpu_memory->Read<u64>(ssbo_addr);
  1532. const auto size = [&]() {
  1533. const bool is_nvn_cbuf = cbuf_index == 0;
  1534. // The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size.
  1535. if (is_nvn_cbuf) {
  1536. return gpu_memory->Read<u32>(ssbo_addr + 8);
  1537. }
  1538. // Other titles (notably Doom Eternal) may use STG/LDG on buffer addresses in custom defined
  1539. // cbufs, which do not store the sizes adjacent to the addresses, so use the fully
  1540. // mapped buffer size for now.
  1541. const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr));
  1542. LOG_INFO(HW_GPU, "Binding storage buffer for cbuf index {}, MemoryLayoutSize 0x{:X}",
  1543. cbuf_index, memory_layout_size);
  1544. return memory_layout_size;
  1545. }();
  1546. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
  1547. if (!cpu_addr || size == 0) {
  1548. LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
  1549. return NULL_BINDING;
  1550. }
  1551. const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE);
  1552. const Binding binding{
  1553. .cpu_addr = *cpu_addr,
  1554. .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr),
  1555. .buffer_id = BufferId{},
  1556. };
  1557. return binding;
  1558. }
  1559. template <class P>
  1560. typename BufferCache<P>::TextureBufferBinding BufferCache<P>::GetTextureBufferBinding(
  1561. GPUVAddr gpu_addr, u32 size, PixelFormat format) {
  1562. const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
  1563. TextureBufferBinding binding;
  1564. if (!cpu_addr || size == 0) {
  1565. binding.cpu_addr = 0;
  1566. binding.size = 0;
  1567. binding.buffer_id = NULL_BUFFER_ID;
  1568. binding.format = PixelFormat::Invalid;
  1569. } else {
  1570. binding.cpu_addr = *cpu_addr;
  1571. binding.size = size;
  1572. binding.buffer_id = BufferId{};
  1573. binding.format = format;
  1574. }
  1575. return binding;
  1576. }
  1577. template <class P>
  1578. std::span<const u8> BufferCache<P>::ImmediateBufferWithData(VAddr cpu_addr, size_t size) {
  1579. u8* const base_pointer = cpu_memory.GetPointer(cpu_addr);
  1580. if (IsRangeGranular(cpu_addr, size) ||
  1581. base_pointer + size == cpu_memory.GetPointer(cpu_addr + size)) {
  1582. return std::span(base_pointer, size);
  1583. } else {
  1584. const std::span<u8> span = ImmediateBuffer(size);
  1585. cpu_memory.ReadBlockUnsafe(cpu_addr, span.data(), size);
  1586. return span;
  1587. }
  1588. }
  1589. template <class P>
  1590. std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) {
  1591. immediate_buffer_alloc.resize_destructive(wanted_capacity);
  1592. return std::span<u8>(immediate_buffer_alloc.data(), wanted_capacity);
  1593. }
  1594. template <class P>
  1595. bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept {
  1596. if constexpr (IS_OPENGL) {
  1597. return ((fast_bound_uniform_buffers[stage] >> binding_index) & 1) != 0;
  1598. } else {
  1599. // Only OpenGL has fast uniform buffers
  1600. return false;
  1601. }
  1602. }
  1603. template <class P>
  1604. std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectCount() {
  1605. auto& buffer = slot_buffers[count_buffer_binding.buffer_id];
  1606. return std::make_pair(&buffer, buffer.Offset(count_buffer_binding.cpu_addr));
  1607. }
  1608. template <class P>
  1609. std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectBuffer() {
  1610. auto& buffer = slot_buffers[indirect_buffer_binding.buffer_id];
  1611. return std::make_pair(&buffer, buffer.Offset(indirect_buffer_binding.cpu_addr));
  1612. }
  1613. } // namespace VideoCommon