TextureGroup.cs 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697
  1. using Ryujinx.Common.Memory;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Gpu.Memory;
  4. using Ryujinx.Graphics.Texture;
  5. using Ryujinx.Memory;
  6. using Ryujinx.Memory.Range;
  7. using Ryujinx.Memory.Tracking;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Runtime.CompilerServices;
  11. namespace Ryujinx.Graphics.Gpu.Image
  12. {
  13. /// <summary>
  14. /// An overlapping texture group with a given view compatibility.
  15. /// </summary>
  16. readonly struct TextureIncompatibleOverlap
  17. {
  18. public readonly TextureGroup Group;
  19. public readonly TextureViewCompatibility Compatibility;
  20. /// <summary>
  21. /// Create a new texture incompatible overlap.
  22. /// </summary>
  23. /// <param name="group">The group that is incompatible</param>
  24. /// <param name="compatibility">The view compatibility for the group</param>
  25. public TextureIncompatibleOverlap(TextureGroup group, TextureViewCompatibility compatibility)
  26. {
  27. Group = group;
  28. Compatibility = compatibility;
  29. }
  30. }
  31. /// <summary>
  32. /// A texture group represents a group of textures that belong to the same storage.
  33. /// When views are created, this class will track memory accesses for them separately.
  34. /// The group iteratively adds more granular tracking as views of different kinds are added.
  35. /// Note that a texture group can be absorbed into another when it becomes a view parent.
  36. /// </summary>
  37. class TextureGroup : IDisposable
  38. {
  39. /// <summary>
  40. /// Threshold of layers to force granular handles (and thus partial loading) on array/3D textures.
  41. /// </summary>
  42. private const int GranularLayerThreshold = 8;
  43. private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false);
  44. /// <summary>
  45. /// The storage texture associated with this group.
  46. /// </summary>
  47. public Texture Storage { get; }
  48. /// <summary>
  49. /// Indicates if the texture has copy dependencies. If true, then all modifications
  50. /// must be signalled to the group, rather than skipping ones still to be flushed.
  51. /// </summary>
  52. public bool HasCopyDependencies { get; set; }
  53. /// <summary>
  54. /// Indicates if the texture group has a pre-emptive flush buffer.
  55. /// When one is present, the group must always be notified on unbind.
  56. /// </summary>
  57. public bool HasFlushBuffer => _flushBuffer != BufferHandle.Null;
  58. /// <summary>
  59. /// Indicates if this texture has any incompatible overlaps alive.
  60. /// </summary>
  61. public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0;
  62. /// <summary>
  63. /// Number indicating the order this texture group was modified relative to others.
  64. /// </summary>
  65. public long ModifiedSequence { get; private set; }
  66. private readonly GpuContext _context;
  67. private readonly PhysicalMemory _physicalMemory;
  68. private int[] _allOffsets;
  69. private int[] _sliceSizes;
  70. private readonly bool _is3D;
  71. private readonly bool _isBuffer;
  72. private bool _hasMipViews;
  73. private bool _hasLayerViews;
  74. private readonly int _layers;
  75. private readonly int _levels;
  76. private MultiRange TextureRange => Storage.Range;
  77. /// <summary>
  78. /// The views list from the storage texture.
  79. /// </summary>
  80. private List<Texture> _views;
  81. private TextureGroupHandle[] _handles;
  82. private bool[] _loadNeeded;
  83. /// <summary>
  84. /// Other texture groups that have incompatible overlaps with this one.
  85. /// </summary>
  86. private readonly List<TextureIncompatibleOverlap> _incompatibleOverlaps;
  87. private bool _incompatibleOverlapsDirty = true;
  88. private readonly bool _flushIncompatibleOverlaps;
  89. private BufferHandle _flushBuffer;
  90. private bool _flushBufferImported;
  91. private bool _flushBufferInvalid;
  92. /// <summary>
  93. /// Create a new texture group.
  94. /// </summary>
  95. /// <param name="context">GPU context that the texture group belongs to</param>
  96. /// <param name="physicalMemory">Physical memory where the <paramref name="storage"/> texture is mapped</param>
  97. /// <param name="storage">The storage texture for this group</param>
  98. /// <param name="incompatibleOverlaps">Groups that overlap with this one but are incompatible</param>
  99. public TextureGroup(GpuContext context, PhysicalMemory physicalMemory, Texture storage, List<TextureIncompatibleOverlap> incompatibleOverlaps)
  100. {
  101. Storage = storage;
  102. _context = context;
  103. _physicalMemory = physicalMemory;
  104. _is3D = storage.Info.Target == Target.Texture3D;
  105. _isBuffer = storage.Info.Target == Target.TextureBuffer;
  106. _layers = storage.Info.GetSlices();
  107. _levels = storage.Info.Levels;
  108. _incompatibleOverlaps = incompatibleOverlaps;
  109. _flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
  110. }
  111. /// <summary>
  112. /// Initialize a new texture group's dirty regions and offsets.
  113. /// </summary>
  114. /// <param name="size">Size info for the storage texture</param>
  115. /// <param name="hasLayerViews">True if the storage will have layer views</param>
  116. /// <param name="hasMipViews">True if the storage will have mip views</param>
  117. public void Initialize(ref SizeInfo size, bool hasLayerViews, bool hasMipViews)
  118. {
  119. _allOffsets = size.AllOffsets;
  120. _sliceSizes = size.SliceSizes;
  121. if (Storage.Target.HasDepthOrLayers() && Storage.Info.GetSlices() > GranularLayerThreshold)
  122. {
  123. _hasLayerViews = true;
  124. _hasMipViews = true;
  125. }
  126. else
  127. {
  128. (_hasLayerViews, _hasMipViews) = PropagateGranularity(hasLayerViews, hasMipViews);
  129. // If the texture is partially mapped, fully subdivide handles immediately.
  130. MultiRange range = Storage.Range;
  131. for (int i = 0; i < range.Count; i++)
  132. {
  133. if (range.GetSubRange(i).Address == MemoryManager.PteUnmapped)
  134. {
  135. _hasLayerViews = true;
  136. _hasMipViews = true;
  137. break;
  138. }
  139. }
  140. }
  141. RecalculateHandleRegions();
  142. }
  143. /// <summary>
  144. /// Initialize all incompatible overlaps in the list, registering them with the other texture groups
  145. /// and creating copy dependencies when partially compatible.
  146. /// </summary>
  147. public void InitializeOverlaps()
  148. {
  149. foreach (TextureIncompatibleOverlap overlap in _incompatibleOverlaps)
  150. {
  151. if (overlap.Compatibility == TextureViewCompatibility.LayoutIncompatible)
  152. {
  153. CreateCopyDependency(overlap.Group, false);
  154. }
  155. overlap.Group._incompatibleOverlaps.Add(new TextureIncompatibleOverlap(this, overlap.Compatibility));
  156. overlap.Group._incompatibleOverlapsDirty = true;
  157. }
  158. if (_incompatibleOverlaps.Count > 0)
  159. {
  160. SignalIncompatibleOverlapModified();
  161. }
  162. }
  163. /// <summary>
  164. /// Signal that the group is dirty to all views and the storage.
  165. /// </summary>
  166. private void SignalAllDirty()
  167. {
  168. Storage.SignalGroupDirty();
  169. if (_views != null)
  170. {
  171. foreach (Texture texture in _views)
  172. {
  173. texture.SignalGroupDirty();
  174. }
  175. }
  176. }
  177. /// <summary>
  178. /// Signal that an incompatible overlap has been modified.
  179. /// If this group must flush incompatible overlaps, the group is signalled as dirty too.
  180. /// </summary>
  181. private void SignalIncompatibleOverlapModified()
  182. {
  183. _incompatibleOverlapsDirty = true;
  184. if (_flushIncompatibleOverlaps)
  185. {
  186. SignalAllDirty();
  187. }
  188. }
  189. /// <summary>
  190. /// Flushes incompatible overlaps if the storage format requires it, and they have been modified.
  191. /// This allows unsupported host formats to accept data written to format aliased textures.
  192. /// </summary>
  193. /// <returns>True if data was flushed, false otherwise</returns>
  194. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  195. public bool FlushIncompatibleOverlapsIfNeeded()
  196. {
  197. if (_flushIncompatibleOverlaps && _incompatibleOverlapsDirty)
  198. {
  199. bool flushed = false;
  200. foreach (var overlap in _incompatibleOverlaps)
  201. {
  202. flushed |= overlap.Group.Storage.FlushModified(true);
  203. }
  204. _incompatibleOverlapsDirty = false;
  205. return flushed;
  206. }
  207. else
  208. {
  209. return false;
  210. }
  211. }
  212. /// <summary>
  213. /// Check and optionally consume the dirty flags for a given texture.
  214. /// The state is shared between views of the same layers and levels.
  215. /// </summary>
  216. /// <param name="texture">The texture being used</param>
  217. /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
  218. /// <returns>True if a flag was dirty, false otherwise</returns>
  219. public bool CheckDirty(Texture texture, bool consume)
  220. {
  221. bool dirty = false;
  222. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  223. {
  224. for (int i = 0; i < regionCount; i++)
  225. {
  226. TextureGroupHandle group = _handles[baseHandle + i];
  227. foreach (RegionHandle handle in group.Handles)
  228. {
  229. if (handle.Dirty)
  230. {
  231. if (consume)
  232. {
  233. handle.Reprotect();
  234. }
  235. dirty = true;
  236. }
  237. }
  238. }
  239. });
  240. return dirty;
  241. }
  242. /// <summary>
  243. /// Discards all data for a given texture.
  244. /// This clears all dirty flags and pending copies from other textures.
  245. /// </summary>
  246. /// <param name="texture">The texture being discarded</param>
  247. public void DiscardData(Texture texture)
  248. {
  249. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  250. {
  251. for (int i = 0; i < regionCount; i++)
  252. {
  253. TextureGroupHandle group = _handles[baseHandle + i];
  254. group.DiscardData();
  255. }
  256. });
  257. }
  258. /// <summary>
  259. /// Synchronize memory for a given texture.
  260. /// If overlapping tracking handles are dirty, fully or partially synchronize the texture data.
  261. /// </summary>
  262. /// <param name="texture">The texture being used</param>
  263. public void SynchronizeMemory(Texture texture)
  264. {
  265. FlushIncompatibleOverlapsIfNeeded();
  266. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  267. {
  268. bool dirty = false;
  269. bool anyModified = false;
  270. bool anyNotDirty = false;
  271. for (int i = 0; i < regionCount; i++)
  272. {
  273. TextureGroupHandle group = _handles[baseHandle + i];
  274. bool modified = group.Modified;
  275. bool handleDirty = false;
  276. bool handleUnmapped = false;
  277. foreach (RegionHandle handle in group.Handles)
  278. {
  279. if (handle.Dirty)
  280. {
  281. handle.Reprotect();
  282. handleDirty = true;
  283. }
  284. else
  285. {
  286. handleUnmapped |= handle.Unmapped;
  287. }
  288. }
  289. // If the modified flag is still present, prefer the data written from gpu.
  290. // A write from CPU will do a flush before writing its data, which should unset this.
  291. if (modified)
  292. {
  293. handleDirty = false;
  294. }
  295. // Evaluate if any copy dependencies need to be fulfilled. A few rules:
  296. // If the copy handle needs to be synchronized, prefer our own state.
  297. // If we need to be synchronized and there is a copy present, prefer the copy.
  298. if (group.NeedsCopy && group.Copy(_context))
  299. {
  300. anyModified |= true; // The copy target has been modified.
  301. handleDirty = false;
  302. }
  303. else
  304. {
  305. anyModified |= modified;
  306. dirty |= handleDirty;
  307. }
  308. if (group.NeedsCopy)
  309. {
  310. // The texture we copied from is still being written to. Copy from it again the next time this texture is used.
  311. texture.SignalGroupDirty();
  312. }
  313. bool loadNeeded = handleDirty && !handleUnmapped;
  314. anyNotDirty |= !loadNeeded;
  315. _loadNeeded[baseHandle + i] = loadNeeded;
  316. }
  317. if (dirty)
  318. {
  319. if (anyNotDirty || (_handles.Length > 1 && (anyModified || split)))
  320. {
  321. // Partial texture invalidation. Only update the layers/levels with dirty flags of the storage.
  322. SynchronizePartial(baseHandle, regionCount);
  323. }
  324. else
  325. {
  326. // Full texture invalidation.
  327. texture.SynchronizeFull();
  328. }
  329. }
  330. });
  331. }
  332. /// <summary>
  333. /// Synchronize part of the storage texture, represented by a given range of handles.
  334. /// Only handles marked by the _loadNeeded array will be synchronized.
  335. /// </summary>
  336. /// <param name="baseHandle">The base index of the range of handles</param>
  337. /// <param name="regionCount">The number of handles to synchronize</param>
  338. private void SynchronizePartial(int baseHandle, int regionCount)
  339. {
  340. int spanEndIndex = -1;
  341. int spanBase = 0;
  342. ReadOnlySpan<byte> dataSpan = ReadOnlySpan<byte>.Empty;
  343. for (int i = 0; i < regionCount; i++)
  344. {
  345. if (_loadNeeded[baseHandle + i])
  346. {
  347. var info = GetHandleInformation(baseHandle + i);
  348. // Ensure the data for this handle is loaded in the span.
  349. if (spanEndIndex <= i - 1)
  350. {
  351. spanEndIndex = i;
  352. if (_is3D)
  353. {
  354. // Look ahead to see how many handles need to be loaded.
  355. for (int j = i + 1; j < regionCount; j++)
  356. {
  357. if (_loadNeeded[baseHandle + j])
  358. {
  359. spanEndIndex = j;
  360. }
  361. else
  362. {
  363. break;
  364. }
  365. }
  366. }
  367. var endInfo = spanEndIndex == i ? info : GetHandleInformation(baseHandle + spanEndIndex);
  368. spanBase = _allOffsets[info.Index];
  369. int spanLast = _allOffsets[endInfo.Index + endInfo.Layers * endInfo.Levels - 1];
  370. int endOffset = Math.Min(spanLast + _sliceSizes[endInfo.BaseLevel + endInfo.Levels - 1], (int)Storage.Size);
  371. int size = endOffset - spanBase;
  372. dataSpan = _physicalMemory.GetSpan(Storage.Range.Slice((ulong)spanBase, (ulong)size));
  373. }
  374. // Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
  375. for (int layer = 0; layer < info.Layers; layer++)
  376. {
  377. for (int level = 0; level < info.Levels; level++)
  378. {
  379. int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
  380. int offset = _allOffsets[offsetIndex];
  381. ReadOnlySpan<byte> data = dataSpan[(offset - spanBase)..];
  382. SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
  383. Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
  384. }
  385. }
  386. }
  387. }
  388. }
  389. /// <summary>
  390. /// Synchronize dependent textures, if any of them have deferred a copy from the given texture.
  391. /// </summary>
  392. /// <param name="texture">The texture to synchronize dependents of</param>
  393. public void SynchronizeDependents(Texture texture)
  394. {
  395. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  396. {
  397. for (int i = 0; i < regionCount; i++)
  398. {
  399. TextureGroupHandle group = _handles[baseHandle + i];
  400. group.SynchronizeDependents();
  401. }
  402. });
  403. }
  404. /// <summary>
  405. /// Determines whether flushes in this texture group should be tracked.
  406. /// Incompatible overlaps may need data from this texture to flush tracked for it to be visible to them.
  407. /// </summary>
  408. /// <returns>True if flushes should be tracked, false otherwise</returns>
  409. private bool ShouldFlushTriggerTracking()
  410. {
  411. foreach (var overlap in _incompatibleOverlaps)
  412. {
  413. if (overlap.Group._flushIncompatibleOverlaps)
  414. {
  415. return true;
  416. }
  417. }
  418. return false;
  419. }
  420. /// <summary>
  421. /// Gets data from the host GPU, and flushes a slice to guest memory.
  422. /// </summary>
  423. /// <remarks>
  424. /// This method should be used to retrieve data that was modified by the host GPU.
  425. /// This is not cheap, avoid doing that unless strictly needed.
  426. /// When possible, the data is written directly into guest memory, rather than copied.
  427. /// </remarks>
  428. /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
  429. /// <param name="sliceIndex">The index of the slice to flush</param>
  430. /// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param>
  431. /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
  432. private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, bool inBuffer, ITexture texture = null)
  433. {
  434. (int layer, int level) = GetLayerLevelForView(sliceIndex);
  435. int offset = _allOffsets[sliceIndex];
  436. int endOffset = Math.Min(offset + _sliceSizes[level], (int)Storage.Size);
  437. int size = endOffset - offset;
  438. using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked);
  439. if (inBuffer)
  440. {
  441. using PinnedSpan<byte> data = _context.Renderer.GetBufferData(_flushBuffer, offset, size);
  442. Storage.ConvertFromHostCompatibleFormat(region.Memory.Span, data.Get(), level, true);
  443. }
  444. else
  445. {
  446. Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture);
  447. }
  448. }
  449. /// <summary>
  450. /// Gets and flushes a number of slices of the storage texture to guest memory.
  451. /// </summary>
  452. /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
  453. /// <param name="sliceStart">The first slice to flush</param>
  454. /// <param name="sliceEnd">The slice to finish flushing on (exclusive)</param>
  455. /// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param>
  456. /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
  457. private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, bool inBuffer, ITexture texture = null)
  458. {
  459. for (int i = sliceStart; i < sliceEnd; i++)
  460. {
  461. FlushTextureDataSliceToGuest(tracked, i, inBuffer, texture);
  462. }
  463. }
  464. /// <summary>
  465. /// Flush modified ranges for a given texture.
  466. /// </summary>
  467. /// <param name="texture">The texture being used</param>
  468. /// <param name="tracked">True if the flush writes should be tracked, false otherwise</param>
  469. /// <returns>True if data was flushed, false otherwise</returns>
  470. public bool FlushModified(Texture texture, bool tracked)
  471. {
  472. tracked = tracked || ShouldFlushTriggerTracking();
  473. bool flushed = false;
  474. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  475. {
  476. int startSlice = 0;
  477. int endSlice = 0;
  478. bool allModified = true;
  479. for (int i = 0; i < regionCount; i++)
  480. {
  481. TextureGroupHandle group = _handles[baseHandle + i];
  482. if (group.Modified)
  483. {
  484. if (endSlice < group.BaseSlice)
  485. {
  486. if (endSlice > startSlice)
  487. {
  488. FlushSliceRange(tracked, startSlice, endSlice, false);
  489. flushed = true;
  490. }
  491. startSlice = group.BaseSlice;
  492. }
  493. endSlice = group.BaseSlice + group.SliceCount;
  494. if (tracked)
  495. {
  496. group.Modified = false;
  497. foreach (Texture texture in group.Overlaps)
  498. {
  499. texture.SignalModifiedDirty();
  500. }
  501. }
  502. }
  503. else
  504. {
  505. allModified = false;
  506. }
  507. }
  508. if (endSlice > startSlice)
  509. {
  510. if (allModified && !split)
  511. {
  512. texture.Flush(tracked);
  513. }
  514. else
  515. {
  516. FlushSliceRange(tracked, startSlice, endSlice, false);
  517. }
  518. flushed = true;
  519. }
  520. });
  521. Storage.SignalModifiedDirty();
  522. return flushed;
  523. }
  524. /// <summary>
  525. /// Flush the texture data into a persistently mapped buffer.
  526. /// If the buffer does not exist, this method will create it.
  527. /// </summary>
  528. /// <param name="handle">Handle of the texture group to flush slices of</param>
  529. public void FlushIntoBuffer(TextureGroupHandle handle)
  530. {
  531. // Ensure that the buffer exists.
  532. if (_flushBufferInvalid && _flushBuffer != BufferHandle.Null)
  533. {
  534. _flushBufferInvalid = false;
  535. _context.Renderer.DeleteBuffer(_flushBuffer);
  536. _flushBuffer = BufferHandle.Null;
  537. }
  538. if (_flushBuffer == BufferHandle.Null)
  539. {
  540. if (!TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities))
  541. {
  542. return;
  543. }
  544. bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel;
  545. var hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0;
  546. if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size))
  547. {
  548. _flushBuffer = _context.Renderer.CreateBuffer(hostPointer, (int)Storage.Size);
  549. _flushBufferImported = true;
  550. }
  551. else
  552. {
  553. _flushBuffer = _context.Renderer.CreateBuffer((int)Storage.Size, BufferAccess.FlushPersistent);
  554. _flushBufferImported = false;
  555. }
  556. Storage.BlacklistScale();
  557. }
  558. int sliceStart = handle.BaseSlice;
  559. int sliceEnd = sliceStart + handle.SliceCount;
  560. for (int i = sliceStart; i < sliceEnd; i++)
  561. {
  562. (int layer, int level) = GetLayerLevelForView(i);
  563. Storage.GetFlushTexture().CopyTo(new BufferRange(_flushBuffer, _allOffsets[i], _sliceSizes[level]), layer, level, _flushBufferImported ? Storage.Info.Stride : 0);
  564. }
  565. }
  566. /// <summary>
  567. /// Clears competing modified flags for all incompatible ranges, if they have possibly been modified.
  568. /// </summary>
  569. /// <param name="texture">The texture that has been modified</param>
  570. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  571. private void ClearIncompatibleOverlaps(Texture texture)
  572. {
  573. if (_incompatibleOverlapsDirty)
  574. {
  575. foreach (TextureIncompatibleOverlap incompatible in _incompatibleOverlaps)
  576. {
  577. incompatible.Group.ClearModified(texture.Range, this);
  578. incompatible.Group.SignalIncompatibleOverlapModified();
  579. }
  580. _incompatibleOverlapsDirty = false;
  581. }
  582. }
  583. /// <summary>
  584. /// Signal that a texture in the group has been modified by the GPU.
  585. /// </summary>
  586. /// <param name="texture">The texture that has been modified</param>
  587. public void SignalModified(Texture texture)
  588. {
  589. ModifiedSequence = _context.GetModifiedSequence();
  590. ClearIncompatibleOverlaps(texture);
  591. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  592. {
  593. for (int i = 0; i < regionCount; i++)
  594. {
  595. TextureGroupHandle group = _handles[baseHandle + i];
  596. group.SignalModified(_context);
  597. }
  598. });
  599. }
  600. /// <summary>
  601. /// Signal that a texture in the group is actively bound, or has been unbound by the GPU.
  602. /// </summary>
  603. /// <param name="texture">The texture that has been modified</param>
  604. /// <param name="bound">True if this texture is being bound, false if unbound</param>
  605. public void SignalModifying(Texture texture, bool bound)
  606. {
  607. ModifiedSequence = _context.GetModifiedSequence();
  608. ClearIncompatibleOverlaps(texture);
  609. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  610. {
  611. for (int i = 0; i < regionCount; i++)
  612. {
  613. TextureGroupHandle group = _handles[baseHandle + i];
  614. group.SignalModifying(bound, _context);
  615. }
  616. });
  617. }
  618. /// <summary>
  619. /// Register a read/write action to flush for a texture group.
  620. /// </summary>
  621. /// <param name="group">The group to register an action for</param>
  622. public void RegisterAction(TextureGroupHandle group)
  623. {
  624. foreach (RegionHandle handle in group.Handles)
  625. {
  626. handle.RegisterAction((address, size) => FlushAction(group, address, size));
  627. }
  628. }
  629. /// <summary>
  630. /// Propagates the mip/layer view flags depending on the texture type.
  631. /// When the most granular type of subresource has views, the other type of subresource must be segmented granularly too.
  632. /// </summary>
  633. /// <param name="hasLayerViews">True if the storage has layer views</param>
  634. /// <param name="hasMipViews">True if the storage has mip views</param>
  635. /// <returns>The input values after propagation</returns>
  636. private (bool HasLayerViews, bool HasMipViews) PropagateGranularity(bool hasLayerViews, bool hasMipViews)
  637. {
  638. if (_is3D)
  639. {
  640. hasMipViews |= hasLayerViews;
  641. }
  642. else
  643. {
  644. hasLayerViews |= hasMipViews;
  645. }
  646. return (hasLayerViews, hasMipViews);
  647. }
  648. /// <summary>
  649. /// Evaluate the range of tracking handles which a view texture overlaps with.
  650. /// </summary>
  651. /// <param name="texture">The texture to get handles for</param>
  652. /// <param name="callback">
  653. /// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
  654. /// This can be called for multiple disjoint ranges, if required.
  655. /// </param>
  656. private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback)
  657. {
  658. if (texture == Storage || !(_hasMipViews || _hasLayerViews))
  659. {
  660. callback(0, _handles.Length);
  661. return;
  662. }
  663. EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback);
  664. }
  665. /// <summary>
  666. /// Evaluate the range of tracking handles which a view texture overlaps with,
  667. /// using the view's position and slice/level counts.
  668. /// </summary>
  669. /// <param name="firstLayer">The first layer of the texture</param>
  670. /// <param name="firstLevel">The first level of the texture</param>
  671. /// <param name="slices">The slice count of the texture</param>
  672. /// <param name="levels">The level count of the texture</param>
  673. /// <param name="callback">
  674. /// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
  675. /// This can be called for multiple disjoint ranges, if required.
  676. /// </param>
  677. private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback)
  678. {
  679. int targetLayerHandles = _hasLayerViews ? slices : 1;
  680. int targetLevelHandles = _hasMipViews ? levels : 1;
  681. if (_isBuffer)
  682. {
  683. return;
  684. }
  685. else if (_is3D)
  686. {
  687. // Future mip levels come after all layers of the last mip level. Each mipmap has less layers (depth) than the last.
  688. if (!_hasLayerViews)
  689. {
  690. // When there are no layer views, the mips are at a consistent offset.
  691. callback(firstLevel, targetLevelHandles);
  692. }
  693. else
  694. {
  695. (int levelIndex, int layerCount) = Get3DLevelRange(firstLevel);
  696. if (levels > 1 && slices < _layers)
  697. {
  698. // The given texture only covers some of the depth of multiple mips. (a "depth slice")
  699. // Callback with each mip's range separately.
  700. // Can assume that the group is fully subdivided (both slices and levels > 1 for storage)
  701. while (levels-- > 1)
  702. {
  703. callback(firstLayer + levelIndex, slices);
  704. levelIndex += layerCount;
  705. layerCount = Math.Max(layerCount >> 1, 1);
  706. slices = Math.Max(layerCount >> 1, 1);
  707. }
  708. }
  709. else
  710. {
  711. int totalSize = Math.Min(layerCount, slices);
  712. while (levels-- > 1)
  713. {
  714. layerCount = Math.Max(layerCount >> 1, 1);
  715. totalSize += layerCount;
  716. }
  717. callback(firstLayer + levelIndex, totalSize);
  718. }
  719. }
  720. }
  721. else
  722. {
  723. // Future layers come after all mipmaps of the last.
  724. int levelHandles = _hasMipViews ? _levels : 1;
  725. if (slices > 1 && levels < _levels)
  726. {
  727. // The given texture only covers some of the mipmaps of multiple slices. (a "mip slice")
  728. // Callback with each layer's range separately.
  729. // Can assume that the group is fully subdivided (both slices and levels > 1 for storage)
  730. for (int i = 0; i < slices; i++)
  731. {
  732. callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true);
  733. }
  734. }
  735. else
  736. {
  737. callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles);
  738. }
  739. }
  740. }
  741. /// <summary>
  742. /// Get the range of offsets for a given mip level of a 3D texture.
  743. /// </summary>
  744. /// <param name="level">The level to return</param>
  745. /// <returns>Start index and count of offsets for the given level</returns>
  746. private (int Index, int Count) Get3DLevelRange(int level)
  747. {
  748. int index = 0;
  749. int count = _layers; // Depth. Halves with each mip level.
  750. while (level-- > 0)
  751. {
  752. index += count;
  753. count = Math.Max(count >> 1, 1);
  754. }
  755. return (index, count);
  756. }
  757. /// <summary>
  758. /// Get view information for a single tracking handle.
  759. /// </summary>
  760. /// <param name="handleIndex">The index of the handle</param>
  761. /// <returns>The layers and levels that the handle covers, and its index in the offsets array</returns>
  762. private (int BaseLayer, int BaseLevel, int Levels, int Layers, int Index) GetHandleInformation(int handleIndex)
  763. {
  764. int baseLayer;
  765. int baseLevel;
  766. int levels = _hasMipViews ? 1 : _levels;
  767. int layers = _hasLayerViews ? 1 : _layers;
  768. int index;
  769. if (_is3D)
  770. {
  771. if (_hasLayerViews)
  772. {
  773. // NOTE: Will also have mip views, or only one level in storage.
  774. index = handleIndex;
  775. baseLevel = 0;
  776. int levelLayers = _layers;
  777. while (handleIndex >= levelLayers)
  778. {
  779. handleIndex -= levelLayers;
  780. baseLevel++;
  781. levelLayers = Math.Max(levelLayers >> 1, 1);
  782. }
  783. baseLayer = handleIndex;
  784. }
  785. else
  786. {
  787. baseLayer = 0;
  788. baseLevel = handleIndex;
  789. (index, _) = Get3DLevelRange(baseLevel);
  790. }
  791. }
  792. else
  793. {
  794. baseLevel = _hasMipViews ? handleIndex % _levels : 0;
  795. baseLayer = _hasMipViews ? handleIndex / _levels : handleIndex;
  796. index = baseLevel + baseLayer * _levels;
  797. }
  798. return (baseLayer, baseLevel, levels, layers, index);
  799. }
  800. /// <summary>
  801. /// Gets the layer and level for a given view.
  802. /// </summary>
  803. /// <param name="index">The index of the view</param>
  804. /// <returns>The layer and level of the specified view</returns>
  805. private (int BaseLayer, int BaseLevel) GetLayerLevelForView(int index)
  806. {
  807. if (_is3D)
  808. {
  809. int baseLevel = 0;
  810. int levelLayers = _layers;
  811. while (index >= levelLayers)
  812. {
  813. index -= levelLayers;
  814. baseLevel++;
  815. levelLayers = Math.Max(levelLayers >> 1, 1);
  816. }
  817. return (index, baseLevel);
  818. }
  819. else
  820. {
  821. return (index / _levels, index % _levels);
  822. }
  823. }
  824. /// <summary>
  825. /// Find the byte offset of a given texture relative to the storage.
  826. /// </summary>
  827. /// <param name="texture">The texture to locate</param>
  828. /// <returns>The offset of the texture in bytes</returns>
  829. public int FindOffset(Texture texture)
  830. {
  831. return _allOffsets[GetOffsetIndex(texture.FirstLayer, texture.FirstLevel)];
  832. }
  833. /// <summary>
  834. /// Find the offset index of a given layer and level.
  835. /// </summary>
  836. /// <param name="layer">The view layer</param>
  837. /// <param name="level">The view level</param>
  838. /// <returns>The offset index of the given layer and level</returns>
  839. public int GetOffsetIndex(int layer, int level)
  840. {
  841. if (_is3D)
  842. {
  843. return layer + Get3DLevelRange(level).Index;
  844. }
  845. else
  846. {
  847. return level + layer * _levels;
  848. }
  849. }
  850. /// <summary>
  851. /// Generate a CpuRegionHandle for a given address and size range in CPU VA.
  852. /// </summary>
  853. /// <param name="address">The start address of the tracked region</param>
  854. /// <param name="size">The size of the tracked region</param>
  855. /// <returns>A CpuRegionHandle covering the given range</returns>
  856. private RegionHandle GenerateHandle(ulong address, ulong size)
  857. {
  858. return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture);
  859. }
  860. /// <summary>
  861. /// Generate a TextureGroupHandle covering a specified range of views.
  862. /// </summary>
  863. /// <param name="viewStart">The start view of the handle</param>
  864. /// <param name="views">The number of views to cover</param>
  865. /// <returns>A TextureGroupHandle covering the given views</returns>
  866. private TextureGroupHandle GenerateHandles(int viewStart, int views)
  867. {
  868. int viewEnd = viewStart + views - 1;
  869. (_, int lastLevel) = GetLayerLevelForView(viewEnd);
  870. int offset = _allOffsets[viewStart];
  871. int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel];
  872. int size = endOffset - offset;
  873. var result = new List<RegionHandle>();
  874. for (int i = 0; i < TextureRange.Count; i++)
  875. {
  876. MemoryRange item = TextureRange.GetSubRange(i);
  877. int subRangeSize = (int)item.Size;
  878. int sliceStart = Math.Clamp(offset, 0, subRangeSize);
  879. int sliceEnd = Math.Clamp(endOffset, 0, subRangeSize);
  880. if (sliceStart != sliceEnd && item.Address != MemoryManager.PteUnmapped)
  881. {
  882. result.Add(GenerateHandle(item.Address + (ulong)sliceStart, (ulong)(sliceEnd - sliceStart)));
  883. }
  884. offset -= subRangeSize;
  885. endOffset -= subRangeSize;
  886. if (endOffset <= 0)
  887. {
  888. break;
  889. }
  890. }
  891. (int firstLayer, int firstLevel) = GetLayerLevelForView(viewStart);
  892. if (_hasLayerViews && _hasMipViews)
  893. {
  894. size = _sliceSizes[firstLevel];
  895. }
  896. offset = _allOffsets[viewStart];
  897. ulong maxSize = Storage.Size - (ulong)offset;
  898. var groupHandle = new TextureGroupHandle(
  899. this,
  900. offset,
  901. Math.Min(maxSize, (ulong)size),
  902. _views,
  903. firstLayer,
  904. firstLevel,
  905. viewStart,
  906. views,
  907. result.ToArray());
  908. return groupHandle;
  909. }
  910. /// <summary>
  911. /// Update the views in this texture group, rebuilding the memory tracking if required.
  912. /// </summary>
  913. /// <param name="views">The views list of the storage texture</param>
  914. /// <param name="texture">The texture that has been added, if that is the only change, otherwise null</param>
  915. public void UpdateViews(List<Texture> views, Texture texture)
  916. {
  917. // This is saved to calculate overlapping views for each handle.
  918. _views = views;
  919. bool layerViews = _hasLayerViews;
  920. bool mipViews = _hasMipViews;
  921. bool regionsRebuilt = false;
  922. if (!(layerViews && mipViews))
  923. {
  924. foreach (Texture view in views)
  925. {
  926. if (view.Info.GetSlices() < _layers)
  927. {
  928. layerViews = true;
  929. }
  930. if (view.Info.Levels < _levels)
  931. {
  932. mipViews = true;
  933. }
  934. }
  935. (layerViews, mipViews) = PropagateGranularity(layerViews, mipViews);
  936. if (layerViews != _hasLayerViews || mipViews != _hasMipViews)
  937. {
  938. _hasLayerViews = layerViews;
  939. _hasMipViews = mipViews;
  940. RecalculateHandleRegions();
  941. regionsRebuilt = true;
  942. }
  943. }
  944. if (!regionsRebuilt)
  945. {
  946. if (texture != null)
  947. {
  948. int offset = FindOffset(texture);
  949. foreach (TextureGroupHandle handle in _handles)
  950. {
  951. handle.AddOverlap(offset, texture);
  952. }
  953. }
  954. else
  955. {
  956. // Must update the overlapping views on all handles, but only if they were not just recreated.
  957. foreach (TextureGroupHandle handle in _handles)
  958. {
  959. handle.RecalculateOverlaps(this, views);
  960. }
  961. }
  962. }
  963. SignalAllDirty();
  964. }
  965. /// <summary>
  966. /// Removes a view from the group, removing it from all overlap lists.
  967. /// </summary>
  968. /// <param name="view">View to remove from the group</param>
  969. public void RemoveView(Texture view)
  970. {
  971. int offset = FindOffset(view);
  972. foreach (TextureGroupHandle handle in _handles)
  973. {
  974. handle.RemoveOverlap(offset, view);
  975. }
  976. }
  977. /// <summary>
  978. /// Inherit handle state from an old set of handles, such as modified and dirty flags.
  979. /// </summary>
  980. /// <param name="oldHandles">The set of handles to inherit state from</param>
  981. /// <param name="handles">The set of handles inheriting the state</param>
  982. /// <param name="relativeOffset">The offset of the old handles in relation to the new ones</param>
  983. private void InheritHandles(TextureGroupHandle[] oldHandles, TextureGroupHandle[] handles, int relativeOffset)
  984. {
  985. foreach (var group in handles)
  986. {
  987. foreach (var handle in group.Handles)
  988. {
  989. bool dirty = false;
  990. foreach (var oldGroup in oldHandles)
  991. {
  992. if (group.OverlapsWith(oldGroup.Offset + relativeOffset, oldGroup.Size))
  993. {
  994. foreach (var oldHandle in oldGroup.Handles)
  995. {
  996. if (handle.OverlapsWith(oldHandle.Address, oldHandle.Size))
  997. {
  998. dirty |= oldHandle.Dirty;
  999. }
  1000. }
  1001. group.Inherit(oldGroup, group.Offset == oldGroup.Offset + relativeOffset);
  1002. }
  1003. }
  1004. if (dirty && !handle.Dirty)
  1005. {
  1006. handle.Reprotect(true);
  1007. }
  1008. if (group.Modified)
  1009. {
  1010. handle.RegisterAction((address, size) => FlushAction(group, address, size));
  1011. }
  1012. }
  1013. }
  1014. foreach (var oldGroup in oldHandles)
  1015. {
  1016. oldGroup.Modified = false;
  1017. }
  1018. }
  1019. /// <summary>
  1020. /// Inherit state from another texture group.
  1021. /// </summary>
  1022. /// <param name="other">The texture group to inherit from</param>
  1023. public void Inherit(TextureGroup other)
  1024. {
  1025. bool layerViews = _hasLayerViews || other._hasLayerViews;
  1026. bool mipViews = _hasMipViews || other._hasMipViews;
  1027. if (layerViews != _hasLayerViews || mipViews != _hasMipViews)
  1028. {
  1029. _hasLayerViews = layerViews;
  1030. _hasMipViews = mipViews;
  1031. RecalculateHandleRegions();
  1032. }
  1033. foreach (TextureIncompatibleOverlap incompatible in other._incompatibleOverlaps)
  1034. {
  1035. RegisterIncompatibleOverlap(incompatible, false);
  1036. incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == other);
  1037. }
  1038. int relativeOffset = Storage.Range.FindOffset(other.Storage.Range);
  1039. InheritHandles(other._handles, _handles, relativeOffset);
  1040. }
  1041. /// <summary>
  1042. /// Replace the current handles with the new handles. It is assumed that the new handles start dirty.
  1043. /// The dirty flags from the previous handles will be kept.
  1044. /// </summary>
  1045. /// <param name="handles">The handles to replace the current handles with</param>
  1046. /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param>
  1047. private void ReplaceHandles(TextureGroupHandle[] handles, bool rangeChanged)
  1048. {
  1049. if (_handles != null)
  1050. {
  1051. // When replacing handles, they should start as non-dirty.
  1052. foreach (TextureGroupHandle groupHandle in handles)
  1053. {
  1054. if (rangeChanged)
  1055. {
  1056. // When the storage range changes, this becomes a little different.
  1057. // If a range does not match one in the original, treat it as modified.
  1058. // It has been newly mapped and its data must be synchronized.
  1059. if (groupHandle.Handles.Length == 0)
  1060. {
  1061. continue;
  1062. }
  1063. foreach (var oldGroup in _handles)
  1064. {
  1065. if (!groupHandle.OverlapsWith(oldGroup.Offset, oldGroup.Size))
  1066. {
  1067. continue;
  1068. }
  1069. foreach (RegionHandle handle in groupHandle.Handles)
  1070. {
  1071. bool hasMatch = false;
  1072. foreach (var oldHandle in oldGroup.Handles)
  1073. {
  1074. if (oldHandle.RangeEquals(handle))
  1075. {
  1076. hasMatch = true;
  1077. break;
  1078. }
  1079. }
  1080. if (hasMatch)
  1081. {
  1082. handle.Reprotect();
  1083. }
  1084. }
  1085. }
  1086. }
  1087. else
  1088. {
  1089. foreach (RegionHandle handle in groupHandle.Handles)
  1090. {
  1091. handle.Reprotect();
  1092. }
  1093. }
  1094. }
  1095. InheritHandles(_handles, handles, 0);
  1096. foreach (var oldGroup in _handles)
  1097. {
  1098. foreach (var oldHandle in oldGroup.Handles)
  1099. {
  1100. oldHandle.Dispose();
  1101. }
  1102. }
  1103. }
  1104. _handles = handles;
  1105. _loadNeeded = new bool[_handles.Length];
  1106. }
  1107. /// <summary>
  1108. /// Recalculate handle regions for this texture group, and inherit existing state into the new handles.
  1109. /// </summary>
  1110. /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param>
  1111. private void RecalculateHandleRegions(bool rangeChanged = false)
  1112. {
  1113. TextureGroupHandle[] handles;
  1114. if (_isBuffer)
  1115. {
  1116. handles = Array.Empty<TextureGroupHandle>();
  1117. }
  1118. else if (!(_hasMipViews || _hasLayerViews))
  1119. {
  1120. // Single dirty region.
  1121. var cpuRegionHandles = new RegionHandle[TextureRange.Count];
  1122. int count = 0;
  1123. for (int i = 0; i < TextureRange.Count; i++)
  1124. {
  1125. var currentRange = TextureRange.GetSubRange(i);
  1126. if (currentRange.Address != MemoryManager.PteUnmapped)
  1127. {
  1128. cpuRegionHandles[count++] = GenerateHandle(currentRange.Address, currentRange.Size);
  1129. }
  1130. }
  1131. if (count != TextureRange.Count)
  1132. {
  1133. Array.Resize(ref cpuRegionHandles, count);
  1134. }
  1135. var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
  1136. handles = new TextureGroupHandle[] { groupHandle };
  1137. }
  1138. else
  1139. {
  1140. // Get views for the host texture.
  1141. // It's worth noting that either the texture has layer views or mip views when getting to this point, which simplifies the logic a little.
  1142. // Depending on if the texture is 3d, either the mip views imply that layer views are present (2d) or the other way around (3d).
  1143. // This is enforced by the way the texture matched as a view, so we don't need to check.
  1144. int layerHandles = _hasLayerViews ? _layers : 1;
  1145. int levelHandles = _hasMipViews ? _levels : 1;
  1146. int handleIndex = 0;
  1147. if (_is3D)
  1148. {
  1149. var handlesList = new List<TextureGroupHandle>();
  1150. for (int i = 0; i < levelHandles; i++)
  1151. {
  1152. for (int j = 0; j < layerHandles; j++)
  1153. {
  1154. (int viewStart, int views) = Get3DLevelRange(i);
  1155. viewStart += j;
  1156. views = _hasLayerViews ? 1 : views; // A layer view is also a mip view.
  1157. handlesList.Add(GenerateHandles(viewStart, views));
  1158. }
  1159. layerHandles = Math.Max(1, layerHandles >> 1);
  1160. }
  1161. handles = handlesList.ToArray();
  1162. }
  1163. else
  1164. {
  1165. handles = new TextureGroupHandle[layerHandles * levelHandles];
  1166. for (int i = 0; i < layerHandles; i++)
  1167. {
  1168. for (int j = 0; j < levelHandles; j++)
  1169. {
  1170. int viewStart = j + i * _levels;
  1171. int views = _hasMipViews ? 1 : _levels; // A mip view is also a layer view.
  1172. handles[handleIndex++] = GenerateHandles(viewStart, views);
  1173. }
  1174. }
  1175. }
  1176. }
  1177. ReplaceHandles(handles, rangeChanged);
  1178. }
  1179. /// <summary>
  1180. /// Regenerates handles when the storage range has been remapped.
  1181. /// This forces the regions to be fully subdivided.
  1182. /// </summary>
  1183. public void RangeChanged()
  1184. {
  1185. _hasLayerViews = true;
  1186. _hasMipViews = true;
  1187. RecalculateHandleRegions(true);
  1188. SignalAllDirty();
  1189. }
  1190. /// <summary>
  1191. /// Ensure that there is a handle for each potential texture view. Required for copy dependencies to work.
  1192. /// </summary>
  1193. private void EnsureFullSubdivision()
  1194. {
  1195. if (!(_hasLayerViews && _hasMipViews))
  1196. {
  1197. _hasLayerViews = true;
  1198. _hasMipViews = true;
  1199. RecalculateHandleRegions();
  1200. }
  1201. }
  1202. /// <summary>
  1203. /// Create a copy dependency between this texture group, and a texture at a given layer/level offset.
  1204. /// </summary>
  1205. /// <param name="other">The view compatible texture to create a dependency to</param>
  1206. /// <param name="firstLayer">The base layer of the given texture relative to the storage</param>
  1207. /// <param name="firstLevel">The base level of the given texture relative to the storage</param>
  1208. /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
  1209. public void CreateCopyDependency(Texture other, int firstLayer, int firstLevel, bool copyTo)
  1210. {
  1211. TextureGroup otherGroup = other.Group;
  1212. EnsureFullSubdivision();
  1213. otherGroup.EnsureFullSubdivision();
  1214. // Get the location of each texture within its storage, so we can find the handles to apply the dependency to.
  1215. // This can consist of multiple disjoint regions, for example if this is a mip slice of an array texture.
  1216. var targetRange = new List<(int BaseHandle, int RegionCount)>();
  1217. var otherRange = new List<(int BaseHandle, int RegionCount)>();
  1218. EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split) => targetRange.Add((baseHandle, regionCount)));
  1219. otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split) => otherRange.Add((baseHandle, regionCount)));
  1220. int targetIndex = 0;
  1221. int otherIndex = 0;
  1222. (int Handle, int RegionCount) targetRegion = (0, 0);
  1223. (int Handle, int RegionCount) otherRegion = (0, 0);
  1224. while (true)
  1225. {
  1226. if (targetRegion.RegionCount == 0)
  1227. {
  1228. if (targetIndex >= targetRange.Count)
  1229. {
  1230. break;
  1231. }
  1232. targetRegion = targetRange[targetIndex++];
  1233. }
  1234. if (otherRegion.RegionCount == 0)
  1235. {
  1236. if (otherIndex >= otherRange.Count)
  1237. {
  1238. break;
  1239. }
  1240. otherRegion = otherRange[otherIndex++];
  1241. }
  1242. TextureGroupHandle handle = _handles[targetRegion.Handle++];
  1243. TextureGroupHandle otherHandle = other.Group._handles[otherRegion.Handle++];
  1244. targetRegion.RegionCount--;
  1245. otherRegion.RegionCount--;
  1246. handle.CreateCopyDependency(otherHandle, copyTo);
  1247. // If "copyTo" is true, this texture must copy to the other.
  1248. // Otherwise, it must copy to this texture.
  1249. if (copyTo)
  1250. {
  1251. otherHandle.Copy(_context, handle);
  1252. }
  1253. else
  1254. {
  1255. handle.Copy(_context, otherHandle);
  1256. }
  1257. }
  1258. }
  1259. /// <summary>
  1260. /// Creates a copy dependency to another texture group, where handles overlap.
  1261. /// Scans through all handles to find compatible patches in the other group.
  1262. /// </summary>
  1263. /// <param name="other">The texture group that overlaps this one</param>
  1264. /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
  1265. public void CreateCopyDependency(TextureGroup other, bool copyTo)
  1266. {
  1267. for (int i = 0; i < _allOffsets.Length; i++)
  1268. {
  1269. (_, int level) = GetLayerLevelForView(i);
  1270. MultiRange handleRange = Storage.Range.Slice((ulong)_allOffsets[i], 1);
  1271. ulong handleBase = handleRange.GetSubRange(0).Address;
  1272. for (int j = 0; j < other._handles.Length; j++)
  1273. {
  1274. (_, int otherLevel) = other.GetLayerLevelForView(j);
  1275. MultiRange otherHandleRange = other.Storage.Range.Slice((ulong)other._allOffsets[j], 1);
  1276. ulong otherHandleBase = otherHandleRange.GetSubRange(0).Address;
  1277. if (handleBase == otherHandleBase)
  1278. {
  1279. // Check if the two sizes are compatible.
  1280. TextureInfo info = Storage.Info;
  1281. TextureInfo otherInfo = other.Storage.Info;
  1282. if (TextureCompatibility.ViewLayoutCompatible(info, otherInfo, level, otherLevel) &&
  1283. TextureCompatibility.CopySizeMatches(info, otherInfo, level, otherLevel))
  1284. {
  1285. // These textures are copy compatible. Create the dependency.
  1286. EnsureFullSubdivision();
  1287. other.EnsureFullSubdivision();
  1288. TextureGroupHandle handle = _handles[i];
  1289. TextureGroupHandle otherHandle = other._handles[j];
  1290. handle.CreateCopyDependency(otherHandle, copyTo);
  1291. // If "copyTo" is true, this texture must copy to the other.
  1292. // Otherwise, it must copy to this texture.
  1293. if (copyTo)
  1294. {
  1295. otherHandle.Copy(_context, handle);
  1296. }
  1297. else
  1298. {
  1299. handle.Copy(_context, otherHandle);
  1300. }
  1301. }
  1302. }
  1303. }
  1304. }
  1305. }
  1306. /// <summary>
  1307. /// Registers another texture group as an incompatible overlap, if not already registered.
  1308. /// </summary>
  1309. /// <param name="other">The texture group to add to the incompatible overlaps list</param>
  1310. /// <param name="copy">True if the overlap should register copy dependencies</param>
  1311. public void RegisterIncompatibleOverlap(TextureIncompatibleOverlap other, bool copy)
  1312. {
  1313. if (!_incompatibleOverlaps.Exists(overlap => overlap.Group == other.Group))
  1314. {
  1315. if (copy && other.Compatibility == TextureViewCompatibility.LayoutIncompatible)
  1316. {
  1317. // Any of the group's views may share compatibility, even if the parents do not fully.
  1318. CreateCopyDependency(other.Group, false);
  1319. }
  1320. _incompatibleOverlaps.Add(other);
  1321. other.Group._incompatibleOverlaps.Add(new TextureIncompatibleOverlap(this, other.Compatibility));
  1322. }
  1323. other.Group.SignalIncompatibleOverlapModified();
  1324. SignalIncompatibleOverlapModified();
  1325. }
  1326. /// <summary>
  1327. /// Clear modified flags in the given range.
  1328. /// This will stop any GPU written data from flushing or copying to dependent textures.
  1329. /// </summary>
  1330. /// <param name="range">The range to clear modified flags in</param>
  1331. /// <param name="ignore">Ignore handles that have a copy dependency to the specified group</param>
  1332. public void ClearModified(MultiRange range, TextureGroup ignore = null)
  1333. {
  1334. TextureGroupHandle[] handles = _handles;
  1335. foreach (TextureGroupHandle handle in handles)
  1336. {
  1337. // Handles list is not modified by another thread, only replaced, so this is thread safe.
  1338. // Remove modified flags from all overlapping handles, so that the textures don't flush to unmapped/remapped GPU memory.
  1339. MultiRange subRange = Storage.Range.Slice((ulong)handle.Offset, (ulong)handle.Size);
  1340. if (range.OverlapsWith(subRange))
  1341. {
  1342. if ((ignore == null || !handle.HasDependencyTo(ignore)) && handle.Modified)
  1343. {
  1344. handle.Modified = false;
  1345. handle.DeferredCopy = null;
  1346. Storage.SignalModifiedDirty();
  1347. lock (handle.Overlaps)
  1348. {
  1349. foreach (Texture texture in handle.Overlaps)
  1350. {
  1351. texture.SignalModifiedDirty();
  1352. }
  1353. }
  1354. }
  1355. }
  1356. }
  1357. Storage.SignalModifiedDirty();
  1358. if (_views != null)
  1359. {
  1360. foreach (Texture texture in _views)
  1361. {
  1362. texture.SignalModifiedDirty();
  1363. }
  1364. }
  1365. }
  1366. /// <summary>
  1367. /// A flush has been requested on a tracked region. Flush texture data for the given handle.
  1368. /// </summary>
  1369. /// <param name="handle">The handle this flush action is for</param>
  1370. /// <param name="address">The address of the flushing memory access</param>
  1371. /// <param name="size">The size of the flushing memory access</param>
  1372. public void FlushAction(TextureGroupHandle handle, ulong address, ulong size)
  1373. {
  1374. // There is a small gap here where the action is removed but _actionRegistered is still 1.
  1375. // In this case it will skip registering the action, but here we are already handling it,
  1376. // so there shouldn't be any issue as it's the same handler for all actions.
  1377. handle.ClearActionRegistered();
  1378. if (!handle.Modified)
  1379. {
  1380. return;
  1381. }
  1382. bool isGpuThread = _context.IsGpuThread();
  1383. if (isGpuThread)
  1384. {
  1385. // No need to wait if we're on the GPU thread, we can just clear the modified flag immediately.
  1386. handle.Modified = false;
  1387. }
  1388. _context.Renderer.BackgroundContextAction(() =>
  1389. {
  1390. bool inBuffer = !isGpuThread && handle.Sync(_context);
  1391. Storage.SignalModifiedDirty();
  1392. lock (handle.Overlaps)
  1393. {
  1394. foreach (Texture texture in handle.Overlaps)
  1395. {
  1396. texture.SignalModifiedDirty();
  1397. }
  1398. }
  1399. if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities) && !(inBuffer && _flushBufferImported))
  1400. {
  1401. FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, inBuffer, Storage.GetFlushTexture());
  1402. }
  1403. });
  1404. }
  1405. /// <summary>
  1406. /// Called if any part of the storage texture is unmapped.
  1407. /// </summary>
  1408. public void Unmapped()
  1409. {
  1410. if (_flushBufferImported)
  1411. {
  1412. _flushBufferInvalid = true;
  1413. }
  1414. }
  1415. /// <summary>
  1416. /// Dispose this texture group, disposing all related memory tracking handles.
  1417. /// </summary>
  1418. public void Dispose()
  1419. {
  1420. foreach (TextureGroupHandle group in _handles)
  1421. {
  1422. group.Dispose();
  1423. }
  1424. foreach (TextureIncompatibleOverlap incompatible in _incompatibleOverlaps)
  1425. {
  1426. incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == this);
  1427. }
  1428. if (_flushBuffer != BufferHandle.Null)
  1429. {
  1430. _context.Renderer.DeleteBuffer(_flushBuffer);
  1431. }
  1432. }
  1433. }
  1434. }