Texture.cs 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Common.Memory;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Gpu.Memory;
  5. using Ryujinx.Graphics.Texture;
  6. using Ryujinx.Graphics.Texture.Astc;
  7. using Ryujinx.Memory;
  8. using Ryujinx.Memory.Range;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Diagnostics;
  12. using System.Linq;
  13. using System.Numerics;
  14. namespace Ryujinx.Graphics.Gpu.Image
  15. {
  16. /// <summary>
  17. /// Represents a cached GPU texture.
  18. /// </summary>
  19. class Texture : IMultiRangeItem, IDisposable
  20. {
  21. // How many updates we need before switching to the byte-by-byte comparison
  22. // modification check method.
  23. // This method uses much more memory so we want to avoid it if possible.
  24. private const int ByteComparisonSwitchThreshold = 4;
  25. // Tuning for blacklisting textures from scaling when their data is updated from CPU.
  26. // Each write adds the weight, each GPU modification subtracts 1.
  27. // Exceeding the threshold blacklists the texture.
  28. private const int ScaledSetWeight = 10;
  29. private const int ScaledSetThreshold = 30;
  30. private const int MinLevelsForForceAnisotropy = 5;
  31. private struct TexturePoolOwner
  32. {
  33. public TexturePool Pool;
  34. public int ID;
  35. public ulong GpuAddress;
  36. }
  37. private GpuContext _context;
  38. private PhysicalMemory _physicalMemory;
  39. private SizeInfo _sizeInfo;
  40. /// <summary>
  41. /// Texture format.
  42. /// </summary>
  43. public Format Format => Info.FormatInfo.Format;
  44. /// <summary>
  45. /// Texture target.
  46. /// </summary>
  47. public Target Target { get; private set; }
  48. /// <summary>
  49. /// Texture width.
  50. /// </summary>
  51. public int Width { get; private set; }
  52. /// <summary>
  53. /// Texture height.
  54. /// </summary>
  55. public int Height { get; private set; }
  56. /// <summary>
  57. /// Texture information.
  58. /// </summary>
  59. public TextureInfo Info { get; private set; }
  60. /// <summary>
  61. /// Set when anisotropic filtering can be forced on the given texture.
  62. /// </summary>
  63. public bool CanForceAnisotropy { get; private set; }
  64. /// <summary>
  65. /// Host scale factor.
  66. /// </summary>
  67. public float ScaleFactor { get; private set; }
  68. /// <summary>
  69. /// Upscaling mode. Informs if a texture is scaled, or is eligible for scaling.
  70. /// </summary>
  71. public TextureScaleMode ScaleMode { get; private set; }
  72. /// <summary>
  73. /// Group that this texture belongs to. Manages read/write memory tracking.
  74. /// </summary>
  75. public TextureGroup Group { get; private set; }
  76. /// <summary>
  77. /// Set when a texture's GPU VA has ever been partially or fully unmapped.
  78. /// This indicates that the range must be fully checked when matching the texture.
  79. /// </summary>
  80. public bool ChangedMapping { get; private set; }
  81. /// <summary>
  82. /// True if the data for this texture must always be flushed when an overlap appears.
  83. /// This is useful if SetData is called directly on this texture, but the data is meant for a future texture.
  84. /// </summary>
  85. public bool AlwaysFlushOnOverlap { get; private set; }
  86. /// <summary>
  87. /// Increments when the host texture is swapped, or when the texture is removed from all pools.
  88. /// </summary>
  89. public int InvalidatedSequence { get; private set; }
  90. private int _depth;
  91. private int _layers;
  92. public int FirstLayer { get; private set; }
  93. public int FirstLevel { get; private set; }
  94. private bool _hasData;
  95. private bool _dirty = true;
  96. private int _updateCount;
  97. private byte[] _currentData;
  98. private bool _modifiedStale = true;
  99. private ITexture _arrayViewTexture;
  100. private Target _arrayViewTarget;
  101. private ITexture _flushHostTexture;
  102. private ITexture _setHostTexture;
  103. private int _scaledSetScore;
  104. private Texture _viewStorage;
  105. private List<Texture> _views;
  106. /// <summary>
  107. /// Host texture.
  108. /// </summary>
  109. public ITexture HostTexture { get; private set; }
  110. /// <summary>
  111. /// Intrusive linked list node used on the auto deletion texture cache.
  112. /// </summary>
  113. public LinkedListNode<Texture> CacheNode { get; set; }
  114. /// <summary>
  115. /// Entry for this texture in the short duration cache, if present.
  116. /// </summary>
  117. public ShortTextureCacheEntry ShortCacheEntry { get; set; }
  118. /// Physical memory ranges where the texture data is located.
  119. /// </summary>
  120. public MultiRange Range { get; private set; }
  121. /// <summary>
  122. /// Layer size in bytes.
  123. /// </summary>
  124. public int LayerSize => _sizeInfo.LayerSize;
  125. /// <summary>
  126. /// Texture size in bytes.
  127. /// </summary>
  128. public ulong Size => (ulong)_sizeInfo.TotalSize;
  129. /// <summary>
  130. /// Whether or not the texture belongs is a view.
  131. /// </summary>
  132. public bool IsView => _viewStorage != this;
  133. /// <summary>
  134. /// Whether or not this texture has views.
  135. /// </summary>
  136. public bool HasViews => _views.Count > 0;
  137. private int _referenceCount;
  138. private List<TexturePoolOwner> _poolOwners;
  139. /// <summary>
  140. /// Constructs a new instance of the cached GPU texture.
  141. /// </summary>
  142. /// <param name="context">GPU context that the texture belongs to</param>
  143. /// <param name="physicalMemory">Physical memory where the texture is mapped</param>
  144. /// <param name="info">Texture information</param>
  145. /// <param name="sizeInfo">Size information of the texture</param>
  146. /// <param name="range">Physical memory ranges where the texture data is located</param>
  147. /// <param name="firstLayer">The first layer of the texture, or 0 if the texture has no parent</param>
  148. /// <param name="firstLevel">The first mipmap level of the texture, or 0 if the texture has no parent</param>
  149. /// <param name="scaleFactor">The floating point scale factor to initialize with</param>
  150. /// <param name="scaleMode">The scale mode to initialize with</param>
  151. private Texture(
  152. GpuContext context,
  153. PhysicalMemory physicalMemory,
  154. TextureInfo info,
  155. SizeInfo sizeInfo,
  156. MultiRange range,
  157. int firstLayer,
  158. int firstLevel,
  159. float scaleFactor,
  160. TextureScaleMode scaleMode)
  161. {
  162. InitializeTexture(context, physicalMemory, info, sizeInfo, range);
  163. FirstLayer = firstLayer;
  164. FirstLevel = firstLevel;
  165. ScaleFactor = scaleFactor;
  166. ScaleMode = scaleMode;
  167. InitializeData(true);
  168. }
  169. /// <summary>
  170. /// Constructs a new instance of the cached GPU texture.
  171. /// </summary>
  172. /// <param name="context">GPU context that the texture belongs to</param>
  173. /// <param name="physicalMemory">Physical memory where the texture is mapped</param>
  174. /// <param name="info">Texture information</param>
  175. /// <param name="sizeInfo">Size information of the texture</param>
  176. /// <param name="range">Physical memory ranges where the texture data is located</param>
  177. /// <param name="scaleMode">The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up</param>
  178. public Texture(
  179. GpuContext context,
  180. PhysicalMemory physicalMemory,
  181. TextureInfo info,
  182. SizeInfo sizeInfo,
  183. MultiRange range,
  184. TextureScaleMode scaleMode)
  185. {
  186. ScaleFactor = 1f; // Texture is first loaded at scale 1x.
  187. ScaleMode = scaleMode;
  188. InitializeTexture(context, physicalMemory, info, sizeInfo, range);
  189. }
  190. /// <summary>
  191. /// Common texture initialization method.
  192. /// This sets the context, info and sizeInfo fields.
  193. /// Other fields are initialized with their default values.
  194. /// </summary>
  195. /// <param name="context">GPU context that the texture belongs to</param>
  196. /// <param name="physicalMemory">Physical memory where the texture is mapped</param>
  197. /// <param name="info">Texture information</param>
  198. /// <param name="sizeInfo">Size information of the texture</param>
  199. /// <param name="range">Physical memory ranges where the texture data is located</param>
  200. private void InitializeTexture(
  201. GpuContext context,
  202. PhysicalMemory physicalMemory,
  203. TextureInfo info,
  204. SizeInfo sizeInfo,
  205. MultiRange range)
  206. {
  207. _context = context;
  208. _physicalMemory = physicalMemory;
  209. _sizeInfo = sizeInfo;
  210. Range = range;
  211. SetInfo(info);
  212. _viewStorage = this;
  213. _views = new List<Texture>();
  214. _poolOwners = new List<TexturePoolOwner>();
  215. }
  216. /// <summary>
  217. /// Initializes the data for a texture. Can optionally initialize the texture with or without data.
  218. /// If the texture is a view, it will initialize memory tracking to be non-dirty.
  219. /// </summary>
  220. /// <param name="isView">True if the texture is a view, false otherwise</param>
  221. /// <param name="withData">True if the texture is to be initialized with data</param>
  222. public void InitializeData(bool isView, bool withData = false)
  223. {
  224. withData |= Group != null && Group.FlushIncompatibleOverlapsIfNeeded();
  225. if (withData)
  226. {
  227. Debug.Assert(!isView);
  228. TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
  229. HostTexture = _context.Renderer.CreateTexture(createInfo, ScaleFactor);
  230. SynchronizeMemory(); // Load the data.
  231. if (ScaleMode == TextureScaleMode.Scaled)
  232. {
  233. SetScale(GraphicsConfig.ResScale); // Scale the data up.
  234. }
  235. }
  236. else
  237. {
  238. _hasData = true;
  239. if (!isView)
  240. {
  241. // Don't update this texture the next time we synchronize.
  242. CheckModified(true);
  243. if (ScaleMode == TextureScaleMode.Scaled)
  244. {
  245. // Don't need to start at 1x as there is no data to scale, just go straight to the target scale.
  246. ScaleFactor = GraphicsConfig.ResScale;
  247. }
  248. TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
  249. HostTexture = _context.Renderer.CreateTexture(createInfo, ScaleFactor);
  250. }
  251. }
  252. }
  253. /// <summary>
  254. /// Initialize a new texture group with this texture as storage.
  255. /// </summary>
  256. /// <param name="hasLayerViews">True if the texture will have layer views</param>
  257. /// <param name="hasMipViews">True if the texture will have mip views</param>
  258. /// <param name="incompatibleOverlaps">Groups that overlap with this one but are incompatible</param>
  259. public void InitializeGroup(bool hasLayerViews, bool hasMipViews, List<TextureIncompatibleOverlap> incompatibleOverlaps)
  260. {
  261. Group = new TextureGroup(_context, _physicalMemory, this, incompatibleOverlaps);
  262. Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews);
  263. }
  264. /// <summary>
  265. /// Create a texture view from this texture.
  266. /// A texture view is defined as a child texture, from a sub-range of their parent texture.
  267. /// For example, the initial layer and mipmap level of the view can be defined, so the texture
  268. /// will start at the given layer/level of the parent texture.
  269. /// </summary>
  270. /// <param name="info">Child texture information</param>
  271. /// <param name="sizeInfo">Child texture size information</param>
  272. /// <param name="range">Physical memory ranges where the texture data is located</param>
  273. /// <param name="firstLayer">Start layer of the child texture on the parent texture</param>
  274. /// <param name="firstLevel">Start mipmap level of the child texture on the parent texture</param>
  275. /// <returns>The child texture</returns>
  276. public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, MultiRange range, int firstLayer, int firstLevel)
  277. {
  278. Texture texture = new Texture(
  279. _context,
  280. _physicalMemory,
  281. info,
  282. sizeInfo,
  283. range,
  284. FirstLayer + firstLayer,
  285. FirstLevel + firstLevel,
  286. ScaleFactor,
  287. ScaleMode);
  288. TextureCreateInfo createInfo = TextureCache.GetCreateInfo(info, _context.Capabilities, ScaleFactor);
  289. texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel);
  290. _viewStorage.AddView(texture);
  291. return texture;
  292. }
  293. /// <summary>
  294. /// Adds a child texture to this texture.
  295. /// </summary>
  296. /// <param name="texture">The child texture</param>
  297. private void AddView(Texture texture)
  298. {
  299. IncrementReferenceCount();
  300. _views.Add(texture);
  301. texture._viewStorage = this;
  302. Group.UpdateViews(_views);
  303. if (texture.Group != null && texture.Group != Group)
  304. {
  305. if (texture.Group.Storage == texture)
  306. {
  307. // This texture's group is no longer used.
  308. Group.Inherit(texture.Group);
  309. texture.Group.Dispose();
  310. }
  311. }
  312. texture.Group = Group;
  313. }
  314. /// <summary>
  315. /// Removes a child texture from this texture.
  316. /// </summary>
  317. /// <param name="texture">The child texture</param>
  318. private void RemoveView(Texture texture)
  319. {
  320. _views.Remove(texture);
  321. texture._viewStorage = texture;
  322. DecrementReferenceCount();
  323. }
  324. /// <summary>
  325. /// Replaces the texture's physical memory range. This forces tracking to regenerate.
  326. /// </summary>
  327. /// <param name="range">New physical memory range backing the texture</param>
  328. public void ReplaceRange(MultiRange range)
  329. {
  330. Range = range;
  331. Group.RangeChanged();
  332. }
  333. /// <summary>
  334. /// Create a copy dependency to a texture that is view compatible with this one.
  335. /// When either texture is modified, the texture data will be copied to the other to keep them in sync.
  336. /// This is essentially an emulated view, useful for handling multiple view parents or format incompatibility.
  337. /// This also forces a copy on creation, to or from the given texture to get them in sync immediately.
  338. /// </summary>
  339. /// <param name="contained">The view compatible texture to create a dependency to</param>
  340. /// <param name="layer">The base layer of the given texture relative to this one</param>
  341. /// <param name="level">The base level of the given texture relative to this one</param>
  342. /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
  343. public void CreateCopyDependency(Texture contained, int layer, int level, bool copyTo)
  344. {
  345. if (contained.Group == Group)
  346. {
  347. return;
  348. }
  349. Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
  350. }
  351. /// <summary>
  352. /// Registers when a texture has had its data set after being scaled, and
  353. /// determines if it should be blacklisted from scaling to improve performance.
  354. /// </summary>
  355. /// <returns>True if setting data for a scaled texture is allowed, false if the texture has been blacklisted</returns>
  356. private bool AllowScaledSetData()
  357. {
  358. _scaledSetScore += ScaledSetWeight;
  359. if (_scaledSetScore >= ScaledSetThreshold)
  360. {
  361. BlacklistScale();
  362. return false;
  363. }
  364. return true;
  365. }
  366. /// <summary>
  367. /// Blacklists this texture from being scaled. Resets its scale to 1 if needed.
  368. /// </summary>
  369. public void BlacklistScale()
  370. {
  371. ScaleMode = TextureScaleMode.Blacklisted;
  372. SetScale(1f);
  373. }
  374. /// <summary>
  375. /// Propagates the scale between this texture and another to ensure they have the same scale.
  376. /// If one texture is blacklisted from scaling, the other will become blacklisted too.
  377. /// </summary>
  378. /// <param name="other">The other texture</param>
  379. public void PropagateScale(Texture other)
  380. {
  381. if (other.ScaleMode == TextureScaleMode.Blacklisted || ScaleMode == TextureScaleMode.Blacklisted)
  382. {
  383. BlacklistScale();
  384. other.BlacklistScale();
  385. }
  386. else
  387. {
  388. // Prefer the configured scale if present. If not, prefer the max.
  389. float targetScale = GraphicsConfig.ResScale;
  390. float sharedScale = (ScaleFactor == targetScale || other.ScaleFactor == targetScale) ? targetScale : Math.Max(ScaleFactor, other.ScaleFactor);
  391. SetScale(sharedScale);
  392. other.SetScale(sharedScale);
  393. }
  394. }
  395. /// <summary>
  396. /// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
  397. /// </summary>
  398. /// <param name="scale">Scale factor</param>
  399. /// <param name="copy">True if the data should be copied to the texture, false otherwise</param>
  400. /// <param name="storage">Texture to use instead of creating one</param>
  401. /// <returns>A host texture containing a scaled version of this texture</returns>
  402. private ITexture GetScaledHostTexture(float scale, bool copy, ITexture storage = null)
  403. {
  404. if (storage == null)
  405. {
  406. TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, scale);
  407. storage = _context.Renderer.CreateTexture(createInfo, scale);
  408. }
  409. if (copy)
  410. {
  411. HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
  412. }
  413. return storage;
  414. }
  415. /// <summary>
  416. /// Sets the Scale Factor on this texture, and immediately recreates it at the correct size.
  417. /// When a texture is resized, a scaled copy is performed from the old texture to the new one, to ensure no data is lost.
  418. /// If scale is equivalent, this only propagates the blacklisted/scaled mode.
  419. /// If called on a view, its storage is resized instead.
  420. /// When resizing storage, all texture views are recreated.
  421. /// </summary>
  422. /// <param name="scale">The new scale factor for this texture</param>
  423. public void SetScale(float scale)
  424. {
  425. bool unscaled = ScaleMode == TextureScaleMode.Blacklisted || (ScaleMode == TextureScaleMode.Undesired && scale == 1);
  426. TextureScaleMode newScaleMode = unscaled ? ScaleMode : TextureScaleMode.Scaled;
  427. if (_viewStorage != this)
  428. {
  429. _viewStorage.ScaleMode = newScaleMode;
  430. _viewStorage.SetScale(scale);
  431. return;
  432. }
  433. if (ScaleFactor != scale)
  434. {
  435. Logger.Debug?.Print(LogClass.Gpu, $"Rescaling {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()} to ({ScaleFactor} to {scale}). ");
  436. ScaleFactor = scale;
  437. ITexture newStorage = GetScaledHostTexture(ScaleFactor, true);
  438. Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}");
  439. ReplaceStorage(newStorage);
  440. // All views must be recreated against the new storage.
  441. foreach (var view in _views)
  442. {
  443. Logger.Debug?.Print(LogClass.Gpu, $" Recreating view {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()}.");
  444. view.ScaleFactor = scale;
  445. TextureCreateInfo viewCreateInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, scale);
  446. ITexture newView = HostTexture.CreateView(viewCreateInfo, view.FirstLayer - FirstLayer, view.FirstLevel - FirstLevel);
  447. view.ReplaceStorage(newView);
  448. view.ScaleMode = newScaleMode;
  449. }
  450. }
  451. if (ScaleMode != newScaleMode)
  452. {
  453. ScaleMode = newScaleMode;
  454. foreach (var view in _views)
  455. {
  456. view.ScaleMode = newScaleMode;
  457. }
  458. }
  459. }
  460. /// <summary>
  461. /// Checks if the memory for this texture was modified, and returns true if it was.
  462. /// The modified flags are optionally consumed as a result.
  463. /// </summary>
  464. /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
  465. /// <returns>True if the texture was modified, false otherwise.</returns>
  466. public bool CheckModified(bool consume)
  467. {
  468. return Group.CheckDirty(this, consume);
  469. }
  470. /// <summary>
  471. /// Synchronizes guest and host memory.
  472. /// This will overwrite the texture data with the texture data on the guest memory, if a CPU
  473. /// modification is detected.
  474. /// Be aware that this can cause texture data written by the GPU to be lost, this is just a
  475. /// one way copy (from CPU owned to GPU owned memory).
  476. /// </summary>
  477. public void SynchronizeMemory()
  478. {
  479. if (Target == Target.TextureBuffer)
  480. {
  481. return;
  482. }
  483. if (!_dirty)
  484. {
  485. return;
  486. }
  487. _dirty = false;
  488. if (_hasData)
  489. {
  490. Group.SynchronizeMemory(this);
  491. }
  492. else
  493. {
  494. Group.CheckDirty(this, true);
  495. SynchronizeFull();
  496. }
  497. }
  498. /// <summary>
  499. /// Signal that this texture is dirty, indicating that the texture group must be checked.
  500. /// </summary>
  501. public void SignalGroupDirty()
  502. {
  503. _dirty = true;
  504. }
  505. /// <summary>
  506. /// Signal that the modified state is dirty, indicating that the texture group should be notified when it changes.
  507. /// </summary>
  508. public void SignalModifiedDirty()
  509. {
  510. _modifiedStale = true;
  511. }
  512. /// <summary>
  513. /// Fully synchronizes guest and host memory.
  514. /// This will replace the entire texture with the data present in guest memory.
  515. /// </summary>
  516. public void SynchronizeFull()
  517. {
  518. ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
  519. // If the host does not support ASTC compression, we need to do the decompression.
  520. // The decompression is slow, so we want to avoid it as much as possible.
  521. // This does a byte-by-byte check and skips the update if the data is equal in this case.
  522. // This improves the speed on applications that overwrites ASTC data without changing anything.
  523. if (Info.FormatInfo.Format.IsAstc() && !_context.Capabilities.SupportsAstcCompression)
  524. {
  525. if (_updateCount < ByteComparisonSwitchThreshold)
  526. {
  527. _updateCount++;
  528. }
  529. else
  530. {
  531. bool dataMatches = _currentData != null && data.SequenceEqual(_currentData);
  532. if (dataMatches)
  533. {
  534. return;
  535. }
  536. _currentData = data.ToArray();
  537. }
  538. }
  539. SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
  540. if (ScaleFactor != 1f && AllowScaledSetData())
  541. {
  542. // If needed, create a texture to load from 1x scale.
  543. ITexture texture = _setHostTexture = GetScaledHostTexture(1f, false, _setHostTexture);
  544. texture.SetData(result);
  545. texture.CopyTo(HostTexture, new Extents2D(0, 0, texture.Width, texture.Height), new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), true);
  546. }
  547. else
  548. {
  549. HostTexture.SetData(result);
  550. }
  551. _hasData = true;
  552. }
  553. /// <summary>
  554. /// Uploads new texture data to the host GPU.
  555. /// </summary>
  556. /// <param name="data">New data</param>
  557. public void SetData(SpanOrArray<byte> data)
  558. {
  559. BlacklistScale();
  560. Group.CheckDirty(this, true);
  561. AlwaysFlushOnOverlap = true;
  562. HostTexture.SetData(data);
  563. _hasData = true;
  564. }
  565. /// <summary>
  566. /// Uploads new texture data to the host GPU for a specific layer/level.
  567. /// </summary>
  568. /// <param name="data">New data</param>
  569. /// <param name="layer">Target layer</param>
  570. /// <param name="level">Target level</param>
  571. public void SetData(SpanOrArray<byte> data, int layer, int level)
  572. {
  573. BlacklistScale();
  574. HostTexture.SetData(data, layer, level);
  575. _currentData = null;
  576. _hasData = true;
  577. }
  578. /// <summary>
  579. /// Uploads new texture data to the host GPU for a specific layer/level and 2D sub-region.
  580. /// </summary>
  581. /// <param name="data">New data</param>
  582. /// <param name="layer">Target layer</param>
  583. /// <param name="level">Target level</param>
  584. /// <param name="region">Target sub-region of the texture to update</param>
  585. public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
  586. {
  587. BlacklistScale();
  588. HostTexture.SetData(data, layer, level, region);
  589. _currentData = null;
  590. _hasData = true;
  591. }
  592. /// <summary>
  593. /// Converts texture data to a format and layout that is supported by the host GPU.
  594. /// </summary>
  595. /// <param name="data">Data to be converted</param>
  596. /// <param name="level">Mip level to convert</param>
  597. /// <param name="single">True to convert a single slice</param>
  598. /// <returns>Converted data</returns>
  599. public SpanOrArray<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
  600. {
  601. int width = Info.Width;
  602. int height = Info.Height;
  603. int depth = _depth;
  604. int layers = single ? 1 : _layers;
  605. int levels = single ? 1 : (Info.Levels - level);
  606. width = Math.Max(width >> level, 1);
  607. height = Math.Max(height >> level, 1);
  608. depth = Math.Max(depth >> level, 1);
  609. int sliceDepth = single ? 1 : depth;
  610. SpanOrArray<byte> result;
  611. if (Info.IsLinear)
  612. {
  613. result = LayoutConverter.ConvertLinearStridedToLinear(
  614. width,
  615. height,
  616. Info.FormatInfo.BlockWidth,
  617. Info.FormatInfo.BlockHeight,
  618. Info.Stride,
  619. Info.Stride,
  620. Info.FormatInfo.BytesPerPixel,
  621. data);
  622. }
  623. else
  624. {
  625. result = LayoutConverter.ConvertBlockLinearToLinear(
  626. width,
  627. height,
  628. depth,
  629. sliceDepth,
  630. levels,
  631. layers,
  632. Info.FormatInfo.BlockWidth,
  633. Info.FormatInfo.BlockHeight,
  634. Info.FormatInfo.BytesPerPixel,
  635. Info.GobBlocksInY,
  636. Info.GobBlocksInZ,
  637. Info.GobBlocksInTileX,
  638. _sizeInfo,
  639. data);
  640. }
  641. // Handle compressed cases not supported by the host:
  642. // - ASTC is usually not supported on desktop cards.
  643. // - BC4/BC5 is not supported on 3D textures.
  644. if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
  645. {
  646. if (!AstcDecoder.TryDecodeToRgba8P(
  647. result.ToArray(),
  648. Info.FormatInfo.BlockWidth,
  649. Info.FormatInfo.BlockHeight,
  650. width,
  651. height,
  652. sliceDepth,
  653. levels,
  654. layers,
  655. out byte[] decoded))
  656. {
  657. string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
  658. Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.GpuAddress:X} ({texInfo}).");
  659. }
  660. if (GraphicsConfig.EnableTextureRecompression)
  661. {
  662. decoded = BCnEncoder.EncodeBC7(decoded, width, height, sliceDepth, levels, layers);
  663. }
  664. result = decoded;
  665. }
  666. else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
  667. {
  668. switch (Format)
  669. {
  670. case Format.Etc2RgbaSrgb:
  671. case Format.Etc2RgbaUnorm:
  672. result = ETC2Decoder.DecodeRgba(result, width, height, sliceDepth, levels, layers);
  673. break;
  674. case Format.Etc2RgbPtaSrgb:
  675. case Format.Etc2RgbPtaUnorm:
  676. result = ETC2Decoder.DecodePta(result, width, height, sliceDepth, levels, layers);
  677. break;
  678. case Format.Etc2RgbSrgb:
  679. case Format.Etc2RgbUnorm:
  680. result = ETC2Decoder.DecodeRgb(result, width, height, sliceDepth, levels, layers);
  681. break;
  682. }
  683. }
  684. else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
  685. {
  686. switch (Format)
  687. {
  688. case Format.Bc1RgbaSrgb:
  689. case Format.Bc1RgbaUnorm:
  690. result = BCnDecoder.DecodeBC1(result, width, height, sliceDepth, levels, layers);
  691. break;
  692. case Format.Bc2Srgb:
  693. case Format.Bc2Unorm:
  694. result = BCnDecoder.DecodeBC2(result, width, height, sliceDepth, levels, layers);
  695. break;
  696. case Format.Bc3Srgb:
  697. case Format.Bc3Unorm:
  698. result = BCnDecoder.DecodeBC3(result, width, height, sliceDepth, levels, layers);
  699. break;
  700. case Format.Bc4Snorm:
  701. case Format.Bc4Unorm:
  702. result = BCnDecoder.DecodeBC4(result, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
  703. break;
  704. case Format.Bc5Snorm:
  705. case Format.Bc5Unorm:
  706. result = BCnDecoder.DecodeBC5(result, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
  707. break;
  708. case Format.Bc6HSfloat:
  709. case Format.Bc6HUfloat:
  710. result = BCnDecoder.DecodeBC6(result, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
  711. break;
  712. case Format.Bc7Srgb:
  713. case Format.Bc7Unorm:
  714. result = BCnDecoder.DecodeBC7(result, width, height, sliceDepth, levels, layers);
  715. break;
  716. }
  717. }
  718. else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
  719. {
  720. result = PixelConverter.ConvertR4G4ToR4G4B4A4(result, width);
  721. if (!_context.Capabilities.SupportsR4G4B4A4Format)
  722. {
  723. result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
  724. }
  725. }
  726. else if (Format == Format.R4G4B4A4Unorm)
  727. {
  728. if (!_context.Capabilities.SupportsR4G4B4A4Format)
  729. {
  730. result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
  731. }
  732. }
  733. else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked())
  734. {
  735. switch (Format)
  736. {
  737. case Format.B5G6R5Unorm:
  738. case Format.R5G6B5Unorm:
  739. result = PixelConverter.ConvertR5G6B5ToR8G8B8A8(result, width);
  740. break;
  741. case Format.B5G5R5A1Unorm:
  742. case Format.R5G5B5X1Unorm:
  743. case Format.R5G5B5A1Unorm:
  744. result = PixelConverter.ConvertR5G5B5ToR8G8B8A8(result, width, Format == Format.R5G5B5X1Unorm);
  745. break;
  746. case Format.A1B5G5R5Unorm:
  747. result = PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result, width);
  748. break;
  749. case Format.R4G4B4A4Unorm:
  750. result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
  751. break;
  752. }
  753. }
  754. return result;
  755. }
  756. /// <summary>
  757. /// Converts texture data from a format and layout that is supported by the host GPU, back into the intended format on the guest GPU.
  758. /// </summary>
  759. /// <param name="output">Optional output span to convert into</param>
  760. /// <param name="data">Data to be converted</param>
  761. /// <param name="level">Mip level to convert</param>
  762. /// <param name="single">True to convert a single slice</param>
  763. /// <returns>Converted data</returns>
  764. public ReadOnlySpan<byte> ConvertFromHostCompatibleFormat(Span<byte> output, ReadOnlySpan<byte> data, int level = 0, bool single = false)
  765. {
  766. if (Target != Target.TextureBuffer)
  767. {
  768. int width = Info.Width;
  769. int height = Info.Height;
  770. int depth = _depth;
  771. int layers = single ? 1 : _layers;
  772. int levels = single ? 1 : (Info.Levels - level);
  773. width = Math.Max(width >> level, 1);
  774. height = Math.Max(height >> level, 1);
  775. depth = Math.Max(depth >> level, 1);
  776. if (Info.IsLinear)
  777. {
  778. data = LayoutConverter.ConvertLinearToLinearStrided(
  779. output,
  780. Info.Width,
  781. Info.Height,
  782. Info.FormatInfo.BlockWidth,
  783. Info.FormatInfo.BlockHeight,
  784. Info.Stride,
  785. Info.FormatInfo.BytesPerPixel,
  786. data);
  787. }
  788. else
  789. {
  790. data = LayoutConverter.ConvertLinearToBlockLinear(
  791. output,
  792. width,
  793. height,
  794. depth,
  795. single ? 1 : depth,
  796. levels,
  797. layers,
  798. Info.FormatInfo.BlockWidth,
  799. Info.FormatInfo.BlockHeight,
  800. Info.FormatInfo.BytesPerPixel,
  801. Info.GobBlocksInY,
  802. Info.GobBlocksInZ,
  803. Info.GobBlocksInTileX,
  804. _sizeInfo,
  805. data);
  806. }
  807. }
  808. return data;
  809. }
  810. /// <summary>
  811. /// Flushes the texture data.
  812. /// This causes the texture data to be written back to guest memory.
  813. /// If the texture was written by the GPU, this includes all modification made by the GPU
  814. /// up to this point.
  815. /// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
  816. /// This may cause data corruption if the memory is already being used for something else on the CPU side.
  817. /// </summary>
  818. /// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param>
  819. /// <returns>True if data was flushed, false otherwise</returns>
  820. public bool FlushModified(bool tracked = true)
  821. {
  822. return TextureCompatibility.CanTextureFlush(Info, _context.Capabilities) && Group.FlushModified(this, tracked);
  823. }
  824. /// <summary>
  825. /// Flushes the texture data.
  826. /// This causes the texture data to be written back to guest memory.
  827. /// If the texture was written by the GPU, this includes all modification made by the GPU
  828. /// up to this point.
  829. /// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
  830. /// This may cause data corruption if the memory is already being used for something else on the CPU side.
  831. /// </summary>
  832. /// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param>
  833. public void Flush(bool tracked)
  834. {
  835. if (TextureCompatibility.CanTextureFlush(Info, _context.Capabilities))
  836. {
  837. FlushTextureDataToGuest(tracked);
  838. }
  839. }
  840. /// <summary>
  841. /// Gets a host texture to use for flushing the texture, at 1x resolution.
  842. /// If the HostTexture is already at 1x resolution, it is returned directly.
  843. /// </summary>
  844. /// <returns>The host texture to flush</returns>
  845. public ITexture GetFlushTexture()
  846. {
  847. ITexture texture = HostTexture;
  848. if (ScaleFactor != 1f)
  849. {
  850. // If needed, create a texture to flush back to host at 1x scale.
  851. texture = _flushHostTexture = GetScaledHostTexture(1f, true, _flushHostTexture);
  852. }
  853. return texture;
  854. }
  855. /// <summary>
  856. /// Gets data from the host GPU, and flushes it all to guest memory.
  857. /// </summary>
  858. /// <remarks>
  859. /// This method should be used to retrieve data that was modified by the host GPU.
  860. /// This is not cheap, avoid doing that unless strictly needed.
  861. /// When possible, the data is written directly into guest memory, rather than copied.
  862. /// </remarks>
  863. /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
  864. /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
  865. public void FlushTextureDataToGuest(bool tracked, ITexture texture = null)
  866. {
  867. using WritableRegion region = _physicalMemory.GetWritableRegion(Range, tracked);
  868. GetTextureDataFromGpu(region.Memory.Span, tracked, texture);
  869. }
  870. /// <summary>
  871. /// Gets data from the host GPU.
  872. /// </summary>
  873. /// <remarks>
  874. /// This method should be used to retrieve data that was modified by the host GPU.
  875. /// This is not cheap, avoid doing that unless strictly needed.
  876. /// </remarks>
  877. /// <param name="output">An output span to place the texture data into. If empty, one is generated</param>
  878. /// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
  879. /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
  880. /// <returns>The span containing the texture data</returns>
  881. private ReadOnlySpan<byte> GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null)
  882. {
  883. ReadOnlySpan<byte> data;
  884. if (texture != null)
  885. {
  886. data = texture.GetData();
  887. }
  888. else
  889. {
  890. if (blacklist)
  891. {
  892. BlacklistScale();
  893. data = HostTexture.GetData();
  894. }
  895. else if (ScaleFactor != 1f)
  896. {
  897. float scale = ScaleFactor;
  898. SetScale(1f);
  899. data = HostTexture.GetData();
  900. SetScale(scale);
  901. }
  902. else
  903. {
  904. data = HostTexture.GetData();
  905. }
  906. }
  907. data = ConvertFromHostCompatibleFormat(output, data);
  908. return data;
  909. }
  910. /// <summary>
  911. /// Gets data from the host GPU for a single slice.
  912. /// </summary>
  913. /// <remarks>
  914. /// This method should be used to retrieve data that was modified by the host GPU.
  915. /// This is not cheap, avoid doing that unless strictly needed.
  916. /// </remarks>
  917. /// <param name="output">An output span to place the texture data into. If empty, one is generated</param>
  918. /// <param name="layer">The layer of the texture to flush</param>
  919. /// <param name="level">The level of the texture to flush</param>
  920. /// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
  921. /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
  922. /// <returns>The span containing the texture data</returns>
  923. public ReadOnlySpan<byte> GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
  924. {
  925. ReadOnlySpan<byte> data;
  926. if (texture != null)
  927. {
  928. data = texture.GetData(layer, level);
  929. }
  930. else
  931. {
  932. if (blacklist)
  933. {
  934. BlacklistScale();
  935. data = HostTexture.GetData(layer, level);
  936. }
  937. else if (ScaleFactor != 1f)
  938. {
  939. float scale = ScaleFactor;
  940. SetScale(1f);
  941. data = HostTexture.GetData(layer, level);
  942. SetScale(scale);
  943. }
  944. else
  945. {
  946. data = HostTexture.GetData(layer, level);
  947. }
  948. }
  949. data = ConvertFromHostCompatibleFormat(output, data, level, true);
  950. return data;
  951. }
  952. /// <summary>
  953. /// This performs a strict comparison, used to check if this texture is equal to the one supplied.
  954. /// </summary>
  955. /// <param name="info">Texture information to compare against</param>
  956. /// <param name="flags">Comparison flags</param>
  957. /// <returns>A value indicating how well this texture matches the given info</returns>
  958. public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
  959. {
  960. bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0;
  961. TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0);
  962. if (matchQuality == TextureMatchQuality.NoMatch)
  963. {
  964. return matchQuality;
  965. }
  966. if (!TextureCompatibility.LayoutMatches(Info, info))
  967. {
  968. return TextureMatchQuality.NoMatch;
  969. }
  970. if (!TextureCompatibility.SizeMatches(Info, info, forSampler))
  971. {
  972. return TextureMatchQuality.NoMatch;
  973. }
  974. if ((flags & TextureSearchFlags.ForSampler) != 0)
  975. {
  976. if (!TextureCompatibility.SamplerParamsMatches(Info, info))
  977. {
  978. return TextureMatchQuality.NoMatch;
  979. }
  980. }
  981. if ((flags & TextureSearchFlags.ForCopy) != 0)
  982. {
  983. bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;
  984. if (!msTargetCompatible && !TextureCompatibility.TargetAndSamplesCompatible(Info, info))
  985. {
  986. return TextureMatchQuality.NoMatch;
  987. }
  988. }
  989. else if (!TextureCompatibility.TargetAndSamplesCompatible(Info, info))
  990. {
  991. return TextureMatchQuality.NoMatch;
  992. }
  993. return Info.Levels == info.Levels ? matchQuality : TextureMatchQuality.NoMatch;
  994. }
  995. /// <summary>
  996. /// Check if it's possible to create a view, with the given parameters, from this texture.
  997. /// </summary>
  998. /// <param name="info">Texture view information</param>
  999. /// <param name="range">Texture view physical memory ranges</param>
  1000. /// <param name="exactSize">Indicates if the texture sizes must be exactly equal, or width is allowed to differ</param>
  1001. /// <param name="layerSize">Layer size on the given texture</param>
  1002. /// <param name="caps">Host GPU capabilities</param>
  1003. /// <param name="firstLayer">Texture view initial layer on this texture</param>
  1004. /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
  1005. /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
  1006. public TextureViewCompatibility IsViewCompatible(
  1007. TextureInfo info,
  1008. MultiRange range,
  1009. bool exactSize,
  1010. int layerSize,
  1011. Capabilities caps,
  1012. out int firstLayer,
  1013. out int firstLevel)
  1014. {
  1015. TextureViewCompatibility result = TextureViewCompatibility.Full;
  1016. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
  1017. if (result != TextureViewCompatibility.Incompatible)
  1018. {
  1019. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps));
  1020. bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
  1021. if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
  1022. {
  1023. result = TextureViewCompatibility.Incompatible;
  1024. }
  1025. if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
  1026. {
  1027. // AMD and Intel have a bug where the view format is always ignored;
  1028. // they use the parent format instead.
  1029. // Create a copy dependency to avoid this issue.
  1030. result = TextureViewCompatibility.CopyOnly;
  1031. }
  1032. }
  1033. firstLayer = 0;
  1034. firstLevel = 0;
  1035. if (result == TextureViewCompatibility.Incompatible)
  1036. {
  1037. return TextureViewCompatibility.Incompatible;
  1038. }
  1039. int offset = Range.FindOffset(range);
  1040. if (offset < 0 || !_sizeInfo.FindView(offset, out firstLayer, out firstLevel))
  1041. {
  1042. return TextureViewCompatibility.LayoutIncompatible;
  1043. }
  1044. if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel))
  1045. {
  1046. return TextureViewCompatibility.LayoutIncompatible;
  1047. }
  1048. if (info.GetSlices() > 1 && LayerSize != layerSize)
  1049. {
  1050. return TextureViewCompatibility.LayoutIncompatible;
  1051. }
  1052. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, exactSize, firstLevel));
  1053. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
  1054. return result;
  1055. }
  1056. /// <summary>
  1057. /// Gets a texture of the specified target type from this texture.
  1058. /// This can be used to get an array texture from a non-array texture and vice-versa.
  1059. /// If this texture and the requested targets are equal, then this texture Host texture is returned directly.
  1060. /// </summary>
  1061. /// <param name="target">The desired target type</param>
  1062. /// <returns>A view of this texture with the requested target, or null if the target is invalid for this texture</returns>
  1063. public ITexture GetTargetTexture(Target target)
  1064. {
  1065. if (target == Target)
  1066. {
  1067. return HostTexture;
  1068. }
  1069. if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
  1070. {
  1071. FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info, _context.Capabilities);
  1072. TextureCreateInfo createInfo = new TextureCreateInfo(
  1073. Info.Width,
  1074. Info.Height,
  1075. target == Target.CubemapArray ? 6 : 1,
  1076. Info.Levels,
  1077. Info.Samples,
  1078. formatInfo.BlockWidth,
  1079. formatInfo.BlockHeight,
  1080. formatInfo.BytesPerPixel,
  1081. formatInfo.Format,
  1082. Info.DepthStencilMode,
  1083. target,
  1084. Info.SwizzleR,
  1085. Info.SwizzleG,
  1086. Info.SwizzleB,
  1087. Info.SwizzleA);
  1088. ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0);
  1089. _arrayViewTexture = viewTexture;
  1090. _arrayViewTarget = target;
  1091. return viewTexture;
  1092. }
  1093. else if (_arrayViewTarget == target)
  1094. {
  1095. return _arrayViewTexture;
  1096. }
  1097. return null;
  1098. }
  1099. /// <summary>
  1100. /// Determine if this texture can have anisotropic filtering forced.
  1101. /// Filtered textures that we might want to force anisotropy on should have a lot of mip levels.
  1102. /// </summary>
  1103. /// <returns>True if anisotropic filtering can be forced, false otherwise</returns>
  1104. private bool CanTextureForceAnisotropy()
  1105. {
  1106. if (!(Target == Target.Texture2D || Target == Target.Texture2DArray))
  1107. {
  1108. return false;
  1109. }
  1110. int maxSize = Math.Max(Info.Width, Info.Height);
  1111. int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
  1112. return Info.Levels >= Math.Min(MinLevelsForForceAnisotropy, maxLevels);
  1113. }
  1114. /// <summary>
  1115. /// Check if this texture and the specified target have the same number of dimensions.
  1116. /// For the purposes of this comparison, 2D and 2D Multisample textures are not considered to have
  1117. /// the same number of dimensions. Same for Cubemap and 3D textures.
  1118. /// </summary>
  1119. /// <param name="target">The target to compare with</param>
  1120. /// <returns>True if both targets have the same number of dimensions, false otherwise</returns>
  1121. private bool IsSameDimensionsTarget(Target target)
  1122. {
  1123. switch (Info.Target)
  1124. {
  1125. case Target.Texture1D:
  1126. case Target.Texture1DArray:
  1127. return target == Target.Texture1D ||
  1128. target == Target.Texture1DArray;
  1129. case Target.Texture2D:
  1130. case Target.Texture2DArray:
  1131. return target == Target.Texture2D ||
  1132. target == Target.Texture2DArray;
  1133. case Target.Cubemap:
  1134. case Target.CubemapArray:
  1135. return target == Target.Cubemap ||
  1136. target == Target.CubemapArray;
  1137. case Target.Texture2DMultisample:
  1138. case Target.Texture2DMultisampleArray:
  1139. return target == Target.Texture2DMultisample ||
  1140. target == Target.Texture2DMultisampleArray;
  1141. case Target.Texture3D:
  1142. return target == Target.Texture3D;
  1143. }
  1144. return false;
  1145. }
  1146. /// <summary>
  1147. /// Replaces view texture information.
  1148. /// This should only be used for child textures with a parent.
  1149. /// </summary>
  1150. /// <param name="parent">The parent texture</param>
  1151. /// <param name="info">The new view texture information</param>
  1152. /// <param name="hostTexture">The new host texture</param>
  1153. /// <param name="firstLayer">The first layer of the view</param>
  1154. /// <param name="firstLevel">The first level of the view</param>
  1155. public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture, int firstLayer, int firstLevel)
  1156. {
  1157. IncrementReferenceCount();
  1158. parent._viewStorage.SynchronizeMemory();
  1159. // If this texture has views, they must be given to the new parent.
  1160. if (_views.Count > 0)
  1161. {
  1162. Texture[] viewCopy = _views.ToArray();
  1163. foreach (Texture view in viewCopy)
  1164. {
  1165. TextureCreateInfo createInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, ScaleFactor);
  1166. ITexture newView = parent.HostTexture.CreateView(createInfo, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
  1167. view.ReplaceView(parent, view.Info, newView, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
  1168. }
  1169. }
  1170. ReplaceStorage(hostTexture);
  1171. if (_viewStorage != this)
  1172. {
  1173. _viewStorage.RemoveView(this);
  1174. }
  1175. FirstLayer = parent.FirstLayer + firstLayer;
  1176. FirstLevel = parent.FirstLevel + firstLevel;
  1177. parent._viewStorage.AddView(this);
  1178. SetInfo(info);
  1179. DecrementReferenceCount();
  1180. }
  1181. /// <summary>
  1182. /// Sets the internal texture information structure.
  1183. /// </summary>
  1184. /// <param name="info">The new texture information</param>
  1185. private void SetInfo(TextureInfo info)
  1186. {
  1187. Info = info;
  1188. Target = info.Target;
  1189. Width = info.Width;
  1190. Height = info.Height;
  1191. CanForceAnisotropy = CanTextureForceAnisotropy();
  1192. _depth = info.GetDepth();
  1193. _layers = info.GetLayers();
  1194. }
  1195. /// <summary>
  1196. /// Signals that the texture has been modified.
  1197. /// </summary>
  1198. public void SignalModified()
  1199. {
  1200. _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
  1201. if (_modifiedStale || Group.HasCopyDependencies)
  1202. {
  1203. _modifiedStale = false;
  1204. Group.SignalModified(this);
  1205. }
  1206. _physicalMemory.TextureCache.Lift(this);
  1207. }
  1208. /// <summary>
  1209. /// Signals that a texture has been bound, or has been unbound.
  1210. /// During this time, lazy copies will not clear the dirty flag.
  1211. /// </summary>
  1212. /// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
  1213. public void SignalModifying(bool bound)
  1214. {
  1215. if (bound)
  1216. {
  1217. _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
  1218. }
  1219. if (_modifiedStale || Group.HasCopyDependencies)
  1220. {
  1221. _modifiedStale = false;
  1222. Group.SignalModifying(this, bound);
  1223. }
  1224. _physicalMemory.TextureCache.Lift(this);
  1225. if (bound)
  1226. {
  1227. IncrementReferenceCount();
  1228. }
  1229. else
  1230. {
  1231. DecrementReferenceCount();
  1232. }
  1233. }
  1234. /// <summary>
  1235. /// Replaces the host texture, while disposing of the old one if needed.
  1236. /// </summary>
  1237. /// <param name="hostTexture">The new host texture</param>
  1238. private void ReplaceStorage(ITexture hostTexture)
  1239. {
  1240. DisposeTextures();
  1241. HostTexture = hostTexture;
  1242. }
  1243. /// <summary>
  1244. /// Determine if any of this texture's data overlaps with another.
  1245. /// </summary>
  1246. /// <param name="texture">The texture to check against</param>
  1247. /// <param name="compatibility">The view compatibility of the two textures</param>
  1248. /// <returns>True if any slice of the textures overlap, false otherwise</returns>
  1249. public bool DataOverlaps(Texture texture, TextureViewCompatibility compatibility)
  1250. {
  1251. if (compatibility == TextureViewCompatibility.LayoutIncompatible && Info.GobBlocksInZ > 1 && Info.GobBlocksInZ == texture.Info.GobBlocksInZ)
  1252. {
  1253. // Allow overlapping slices of layout compatible 3D textures with matching GobBlocksInZ, as they are interleaved.
  1254. return false;
  1255. }
  1256. if (texture._sizeInfo.AllOffsets.Length == 1 && _sizeInfo.AllOffsets.Length == 1)
  1257. {
  1258. return Range.OverlapsWith(texture.Range);
  1259. }
  1260. MultiRange otherRange = texture.Range;
  1261. IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.GetSlice((ulong)region.Offset, (ulong)region.Size));
  1262. IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.GetSlice((ulong)region.Offset, (ulong)region.Size));
  1263. foreach (MultiRange region in regions)
  1264. {
  1265. foreach (MultiRange otherRegion in otherRegions)
  1266. {
  1267. if (region.OverlapsWith(otherRegion))
  1268. {
  1269. return true;
  1270. }
  1271. }
  1272. }
  1273. return false;
  1274. }
  1275. /// <summary>
  1276. /// Increments the texture reference count.
  1277. /// </summary>
  1278. public void IncrementReferenceCount()
  1279. {
  1280. _referenceCount++;
  1281. }
  1282. /// <summary>
  1283. /// Increments the reference count and records the given texture pool and ID as a pool owner.
  1284. /// </summary>
  1285. /// <param name="pool">The texture pool this texture has been added to</param>
  1286. /// <param name="id">The ID of the reference to this texture in the pool</param>
  1287. /// <param name="gpuVa">GPU VA of the pool reference</param>
  1288. public void IncrementReferenceCount(TexturePool pool, int id, ulong gpuVa)
  1289. {
  1290. lock (_poolOwners)
  1291. {
  1292. _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id, GpuAddress = gpuVa });
  1293. }
  1294. _referenceCount++;
  1295. if (ShortCacheEntry != null)
  1296. {
  1297. _physicalMemory.TextureCache.RemoveShortCache(this);
  1298. }
  1299. }
  1300. /// <summary>
  1301. /// Indicates that the texture has one reference left, and will delete on reference decrement.
  1302. /// </summary>
  1303. /// <returns>True if there is one reference remaining, false otherwise</returns>
  1304. public bool HasOneReference()
  1305. {
  1306. return _referenceCount == 1;
  1307. }
  1308. /// <summary>
  1309. /// Decrements the texture reference count.
  1310. /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
  1311. /// </summary>
  1312. /// <returns>True if the texture is now referenceless, false otherwise</returns>
  1313. public bool DecrementReferenceCount()
  1314. {
  1315. int newRefCount = --_referenceCount;
  1316. if (newRefCount == 0)
  1317. {
  1318. if (_viewStorage != this)
  1319. {
  1320. _viewStorage.RemoveView(this);
  1321. }
  1322. _physicalMemory.TextureCache.RemoveTextureFromCache(this);
  1323. }
  1324. Debug.Assert(newRefCount >= 0);
  1325. DeleteIfNotUsed();
  1326. return newRefCount <= 0;
  1327. }
  1328. /// <summary>
  1329. /// Decrements the texture reference count, also removing an associated pool owner reference.
  1330. /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
  1331. /// </summary>
  1332. /// <param name="pool">The texture pool this texture is being removed from</param>
  1333. /// <param name="id">The ID of the reference to this texture in the pool</param>
  1334. /// <returns>True if the texture is now referenceless, false otherwise</returns>
  1335. public bool DecrementReferenceCount(TexturePool pool, int id = -1)
  1336. {
  1337. lock (_poolOwners)
  1338. {
  1339. int references = _poolOwners.RemoveAll(entry => entry.Pool == pool && entry.ID == id || id == -1);
  1340. if (references == 0)
  1341. {
  1342. // This reference has already been removed.
  1343. return _referenceCount <= 0;
  1344. }
  1345. Debug.Assert(references == 1);
  1346. }
  1347. return DecrementReferenceCount();
  1348. }
  1349. /// <summary>
  1350. /// Forcibly remove this texture from all pools that reference it.
  1351. /// </summary>
  1352. /// <param name="deferred">Indicates if the removal is being done from another thread.</param>
  1353. public void RemoveFromPools(bool deferred)
  1354. {
  1355. lock (_poolOwners)
  1356. {
  1357. foreach (var owner in _poolOwners)
  1358. {
  1359. owner.Pool.ForceRemove(this, owner.ID, deferred);
  1360. }
  1361. _poolOwners.Clear();
  1362. }
  1363. if (ShortCacheEntry != null && _context.IsGpuThread())
  1364. {
  1365. // If this is called from another thread (unmapped), the short cache will
  1366. // have to remove this texture on a future tick.
  1367. _physicalMemory.TextureCache.RemoveShortCache(this);
  1368. }
  1369. InvalidatedSequence++;
  1370. }
  1371. /// <summary>
  1372. /// Queue updating texture mappings on the pool. Happens from another thread.
  1373. /// </summary>
  1374. public void UpdatePoolMappings()
  1375. {
  1376. lock (_poolOwners)
  1377. {
  1378. ulong address = 0;
  1379. foreach (var owner in _poolOwners)
  1380. {
  1381. if (address == 0 || address == owner.GpuAddress)
  1382. {
  1383. address = owner.GpuAddress;
  1384. owner.Pool.QueueUpdateMapping(this, owner.ID);
  1385. }
  1386. else
  1387. {
  1388. // If there is a different GPU VA mapping, prefer the first and delete the others.
  1389. owner.Pool.ForceRemove(this, owner.ID, true);
  1390. }
  1391. }
  1392. _poolOwners.Clear();
  1393. }
  1394. InvalidatedSequence++;
  1395. }
  1396. /// <summary>
  1397. /// Delete the texture if it is not used anymore.
  1398. /// The texture is considered unused when the reference count is zero,
  1399. /// and it has no child views.
  1400. /// </summary>
  1401. private void DeleteIfNotUsed()
  1402. {
  1403. // We can delete the texture as long it is not being used
  1404. // in any cache (the reference count is 0 in this case), and
  1405. // also all views that may be created from this texture were
  1406. // already deleted (views count is 0).
  1407. if (_referenceCount == 0 && _views.Count == 0)
  1408. {
  1409. Dispose();
  1410. }
  1411. }
  1412. /// <summary>
  1413. /// Performs texture disposal, deleting the texture.
  1414. /// </summary>
  1415. private void DisposeTextures()
  1416. {
  1417. InvalidatedSequence++;
  1418. _currentData = null;
  1419. HostTexture.Release();
  1420. _arrayViewTexture?.Release();
  1421. _arrayViewTexture = null;
  1422. _flushHostTexture?.Release();
  1423. _flushHostTexture = null;
  1424. _setHostTexture?.Release();
  1425. _setHostTexture = null;
  1426. }
  1427. /// <summary>
  1428. /// Called when the memory for this texture has been unmapped.
  1429. /// Calls are from non-gpu threads.
  1430. /// </summary>
  1431. /// <param name="unmapRange">The range of memory being unmapped</param>
  1432. public void Unmapped(MultiRange unmapRange)
  1433. {
  1434. ChangedMapping = true;
  1435. if (Group.Storage == this)
  1436. {
  1437. Group.ClearModified(unmapRange);
  1438. }
  1439. UpdatePoolMappings();
  1440. }
  1441. /// <summary>
  1442. /// Performs texture disposal, deleting the texture.
  1443. /// </summary>
  1444. public void Dispose()
  1445. {
  1446. DisposeTextures();
  1447. if (Group.Storage == this)
  1448. {
  1449. Group.Dispose();
  1450. }
  1451. }
  1452. }
  1453. }