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, texture);
  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. Group.RemoveView(texture);
  322. texture._viewStorage = texture;
  323. DecrementReferenceCount();
  324. }
  325. /// <summary>
  326. /// Replaces the texture's physical memory range. This forces tracking to regenerate.
  327. /// </summary>
  328. /// <param name="range">New physical memory range backing the texture</param>
  329. public void ReplaceRange(MultiRange range)
  330. {
  331. Range = range;
  332. Group.RangeChanged();
  333. }
  334. /// <summary>
  335. /// Create a copy dependency to a texture that is view compatible with this one.
  336. /// When either texture is modified, the texture data will be copied to the other to keep them in sync.
  337. /// This is essentially an emulated view, useful for handling multiple view parents or format incompatibility.
  338. /// This also forces a copy on creation, to or from the given texture to get them in sync immediately.
  339. /// </summary>
  340. /// <param name="contained">The view compatible texture to create a dependency to</param>
  341. /// <param name="layer">The base layer of the given texture relative to this one</param>
  342. /// <param name="level">The base level of the given texture relative to this one</param>
  343. /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
  344. public void CreateCopyDependency(Texture contained, int layer, int level, bool copyTo)
  345. {
  346. if (contained.Group == Group)
  347. {
  348. return;
  349. }
  350. Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
  351. }
  352. /// <summary>
  353. /// Registers when a texture has had its data set after being scaled, and
  354. /// determines if it should be blacklisted from scaling to improve performance.
  355. /// </summary>
  356. /// <returns>True if setting data for a scaled texture is allowed, false if the texture has been blacklisted</returns>
  357. private bool AllowScaledSetData()
  358. {
  359. _scaledSetScore += ScaledSetWeight;
  360. if (_scaledSetScore >= ScaledSetThreshold)
  361. {
  362. BlacklistScale();
  363. return false;
  364. }
  365. return true;
  366. }
  367. /// <summary>
  368. /// Blacklists this texture from being scaled. Resets its scale to 1 if needed.
  369. /// </summary>
  370. public void BlacklistScale()
  371. {
  372. ScaleMode = TextureScaleMode.Blacklisted;
  373. SetScale(1f);
  374. }
  375. /// <summary>
  376. /// Propagates the scale between this texture and another to ensure they have the same scale.
  377. /// If one texture is blacklisted from scaling, the other will become blacklisted too.
  378. /// </summary>
  379. /// <param name="other">The other texture</param>
  380. public void PropagateScale(Texture other)
  381. {
  382. if (other.ScaleMode == TextureScaleMode.Blacklisted || ScaleMode == TextureScaleMode.Blacklisted)
  383. {
  384. BlacklistScale();
  385. other.BlacklistScale();
  386. }
  387. else
  388. {
  389. // Prefer the configured scale if present. If not, prefer the max.
  390. float targetScale = GraphicsConfig.ResScale;
  391. float sharedScale = (ScaleFactor == targetScale || other.ScaleFactor == targetScale) ? targetScale : Math.Max(ScaleFactor, other.ScaleFactor);
  392. SetScale(sharedScale);
  393. other.SetScale(sharedScale);
  394. }
  395. }
  396. /// <summary>
  397. /// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
  398. /// </summary>
  399. /// <param name="scale">Scale factor</param>
  400. /// <param name="copy">True if the data should be copied to the texture, false otherwise</param>
  401. /// <param name="storage">Texture to use instead of creating one</param>
  402. /// <returns>A host texture containing a scaled version of this texture</returns>
  403. private ITexture GetScaledHostTexture(float scale, bool copy, ITexture storage = null)
  404. {
  405. if (storage == null)
  406. {
  407. TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, scale);
  408. storage = _context.Renderer.CreateTexture(createInfo, scale);
  409. }
  410. if (copy)
  411. {
  412. HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
  413. }
  414. return storage;
  415. }
  416. /// <summary>
  417. /// Sets the Scale Factor on this texture, and immediately recreates it at the correct size.
  418. /// When a texture is resized, a scaled copy is performed from the old texture to the new one, to ensure no data is lost.
  419. /// If scale is equivalent, this only propagates the blacklisted/scaled mode.
  420. /// If called on a view, its storage is resized instead.
  421. /// When resizing storage, all texture views are recreated.
  422. /// </summary>
  423. /// <param name="scale">The new scale factor for this texture</param>
  424. public void SetScale(float scale)
  425. {
  426. bool unscaled = ScaleMode == TextureScaleMode.Blacklisted || (ScaleMode == TextureScaleMode.Undesired && scale == 1);
  427. TextureScaleMode newScaleMode = unscaled ? ScaleMode : TextureScaleMode.Scaled;
  428. if (_viewStorage != this)
  429. {
  430. _viewStorage.ScaleMode = newScaleMode;
  431. _viewStorage.SetScale(scale);
  432. return;
  433. }
  434. if (ScaleFactor != scale)
  435. {
  436. Logger.Debug?.Print(LogClass.Gpu, $"Rescaling {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()} to ({ScaleFactor} to {scale}). ");
  437. ScaleFactor = scale;
  438. ITexture newStorage = GetScaledHostTexture(ScaleFactor, true);
  439. Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}");
  440. ReplaceStorage(newStorage);
  441. // All views must be recreated against the new storage.
  442. foreach (var view in _views)
  443. {
  444. Logger.Debug?.Print(LogClass.Gpu, $" Recreating view {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()}.");
  445. view.ScaleFactor = scale;
  446. TextureCreateInfo viewCreateInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, scale);
  447. ITexture newView = HostTexture.CreateView(viewCreateInfo, view.FirstLayer - FirstLayer, view.FirstLevel - FirstLevel);
  448. view.ReplaceStorage(newView);
  449. view.ScaleMode = newScaleMode;
  450. }
  451. }
  452. if (ScaleMode != newScaleMode)
  453. {
  454. ScaleMode = newScaleMode;
  455. foreach (var view in _views)
  456. {
  457. view.ScaleMode = newScaleMode;
  458. }
  459. }
  460. }
  461. /// <summary>
  462. /// Checks if the memory for this texture was modified, and returns true if it was.
  463. /// The modified flags are optionally consumed as a result.
  464. /// </summary>
  465. /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
  466. /// <returns>True if the texture was modified, false otherwise.</returns>
  467. public bool CheckModified(bool consume)
  468. {
  469. return Group.CheckDirty(this, consume);
  470. }
  471. /// <summary>
  472. /// Synchronizes guest and host memory.
  473. /// This will overwrite the texture data with the texture data on the guest memory, if a CPU
  474. /// modification is detected.
  475. /// Be aware that this can cause texture data written by the GPU to be lost, this is just a
  476. /// one way copy (from CPU owned to GPU owned memory).
  477. /// </summary>
  478. public void SynchronizeMemory()
  479. {
  480. if (Target == Target.TextureBuffer)
  481. {
  482. return;
  483. }
  484. if (!_dirty)
  485. {
  486. return;
  487. }
  488. _dirty = false;
  489. if (_hasData)
  490. {
  491. Group.SynchronizeMemory(this);
  492. }
  493. else
  494. {
  495. Group.CheckDirty(this, true);
  496. SynchronizeFull();
  497. }
  498. }
  499. /// <summary>
  500. /// Signal that this texture is dirty, indicating that the texture group must be checked.
  501. /// </summary>
  502. public void SignalGroupDirty()
  503. {
  504. _dirty = true;
  505. }
  506. /// <summary>
  507. /// Signal that the modified state is dirty, indicating that the texture group should be notified when it changes.
  508. /// </summary>
  509. public void SignalModifiedDirty()
  510. {
  511. _modifiedStale = true;
  512. }
  513. /// <summary>
  514. /// Fully synchronizes guest and host memory.
  515. /// This will replace the entire texture with the data present in guest memory.
  516. /// </summary>
  517. public void SynchronizeFull()
  518. {
  519. ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
  520. // If the host does not support ASTC compression, we need to do the decompression.
  521. // The decompression is slow, so we want to avoid it as much as possible.
  522. // This does a byte-by-byte check and skips the update if the data is equal in this case.
  523. // This improves the speed on applications that overwrites ASTC data without changing anything.
  524. if (Info.FormatInfo.Format.IsAstc() && !_context.Capabilities.SupportsAstcCompression)
  525. {
  526. if (_updateCount < ByteComparisonSwitchThreshold)
  527. {
  528. _updateCount++;
  529. }
  530. else
  531. {
  532. bool dataMatches = _currentData != null && data.SequenceEqual(_currentData);
  533. if (dataMatches)
  534. {
  535. return;
  536. }
  537. _currentData = data.ToArray();
  538. }
  539. }
  540. SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
  541. if (ScaleFactor != 1f && AllowScaledSetData())
  542. {
  543. // If needed, create a texture to load from 1x scale.
  544. ITexture texture = _setHostTexture = GetScaledHostTexture(1f, false, _setHostTexture);
  545. texture.SetData(result);
  546. texture.CopyTo(HostTexture, new Extents2D(0, 0, texture.Width, texture.Height), new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), true);
  547. }
  548. else
  549. {
  550. HostTexture.SetData(result);
  551. }
  552. _hasData = true;
  553. }
  554. /// <summary>
  555. /// Uploads new texture data to the host GPU.
  556. /// </summary>
  557. /// <param name="data">New data</param>
  558. public void SetData(SpanOrArray<byte> data)
  559. {
  560. BlacklistScale();
  561. Group.CheckDirty(this, true);
  562. AlwaysFlushOnOverlap = true;
  563. HostTexture.SetData(data);
  564. _hasData = true;
  565. }
  566. /// <summary>
  567. /// Uploads new texture data to the host GPU for a specific layer/level.
  568. /// </summary>
  569. /// <param name="data">New data</param>
  570. /// <param name="layer">Target layer</param>
  571. /// <param name="level">Target level</param>
  572. public void SetData(SpanOrArray<byte> data, int layer, int level)
  573. {
  574. BlacklistScale();
  575. HostTexture.SetData(data, layer, level);
  576. _currentData = null;
  577. _hasData = true;
  578. }
  579. /// <summary>
  580. /// Uploads new texture data to the host GPU for a specific layer/level and 2D sub-region.
  581. /// </summary>
  582. /// <param name="data">New data</param>
  583. /// <param name="layer">Target layer</param>
  584. /// <param name="level">Target level</param>
  585. /// <param name="region">Target sub-region of the texture to update</param>
  586. public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
  587. {
  588. BlacklistScale();
  589. HostTexture.SetData(data, layer, level, region);
  590. _currentData = null;
  591. _hasData = true;
  592. }
  593. /// <summary>
  594. /// Converts texture data to a format and layout that is supported by the host GPU.
  595. /// </summary>
  596. /// <param name="data">Data to be converted</param>
  597. /// <param name="level">Mip level to convert</param>
  598. /// <param name="single">True to convert a single slice</param>
  599. /// <returns>Converted data</returns>
  600. public SpanOrArray<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
  601. {
  602. int width = Info.Width;
  603. int height = Info.Height;
  604. int depth = _depth;
  605. int layers = single ? 1 : _layers;
  606. int levels = single ? 1 : (Info.Levels - level);
  607. width = Math.Max(width >> level, 1);
  608. height = Math.Max(height >> level, 1);
  609. depth = Math.Max(depth >> level, 1);
  610. int sliceDepth = single ? 1 : depth;
  611. SpanOrArray<byte> result;
  612. if (Info.IsLinear)
  613. {
  614. result = LayoutConverter.ConvertLinearStridedToLinear(
  615. width,
  616. height,
  617. Info.FormatInfo.BlockWidth,
  618. Info.FormatInfo.BlockHeight,
  619. Info.Stride,
  620. Info.Stride,
  621. Info.FormatInfo.BytesPerPixel,
  622. data);
  623. }
  624. else
  625. {
  626. result = LayoutConverter.ConvertBlockLinearToLinear(
  627. width,
  628. height,
  629. depth,
  630. sliceDepth,
  631. levels,
  632. layers,
  633. Info.FormatInfo.BlockWidth,
  634. Info.FormatInfo.BlockHeight,
  635. Info.FormatInfo.BytesPerPixel,
  636. Info.GobBlocksInY,
  637. Info.GobBlocksInZ,
  638. Info.GobBlocksInTileX,
  639. _sizeInfo,
  640. data);
  641. }
  642. // Handle compressed cases not supported by the host:
  643. // - ASTC is usually not supported on desktop cards.
  644. // - BC4/BC5 is not supported on 3D textures.
  645. if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
  646. {
  647. if (!AstcDecoder.TryDecodeToRgba8P(
  648. result.ToArray(),
  649. Info.FormatInfo.BlockWidth,
  650. Info.FormatInfo.BlockHeight,
  651. width,
  652. height,
  653. sliceDepth,
  654. levels,
  655. layers,
  656. out byte[] decoded))
  657. {
  658. string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
  659. Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.GpuAddress:X} ({texInfo}).");
  660. }
  661. if (GraphicsConfig.EnableTextureRecompression)
  662. {
  663. decoded = BCnEncoder.EncodeBC7(decoded, width, height, sliceDepth, levels, layers);
  664. }
  665. result = decoded;
  666. }
  667. else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
  668. {
  669. switch (Format)
  670. {
  671. case Format.Etc2RgbaSrgb:
  672. case Format.Etc2RgbaUnorm:
  673. result = ETC2Decoder.DecodeRgba(result, width, height, sliceDepth, levels, layers);
  674. break;
  675. case Format.Etc2RgbPtaSrgb:
  676. case Format.Etc2RgbPtaUnorm:
  677. result = ETC2Decoder.DecodePta(result, width, height, sliceDepth, levels, layers);
  678. break;
  679. case Format.Etc2RgbSrgb:
  680. case Format.Etc2RgbUnorm:
  681. result = ETC2Decoder.DecodeRgb(result, width, height, sliceDepth, levels, layers);
  682. break;
  683. }
  684. }
  685. else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
  686. {
  687. switch (Format)
  688. {
  689. case Format.Bc1RgbaSrgb:
  690. case Format.Bc1RgbaUnorm:
  691. result = BCnDecoder.DecodeBC1(result, width, height, sliceDepth, levels, layers);
  692. break;
  693. case Format.Bc2Srgb:
  694. case Format.Bc2Unorm:
  695. result = BCnDecoder.DecodeBC2(result, width, height, sliceDepth, levels, layers);
  696. break;
  697. case Format.Bc3Srgb:
  698. case Format.Bc3Unorm:
  699. result = BCnDecoder.DecodeBC3(result, width, height, sliceDepth, levels, layers);
  700. break;
  701. case Format.Bc4Snorm:
  702. case Format.Bc4Unorm:
  703. result = BCnDecoder.DecodeBC4(result, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
  704. break;
  705. case Format.Bc5Snorm:
  706. case Format.Bc5Unorm:
  707. result = BCnDecoder.DecodeBC5(result, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
  708. break;
  709. case Format.Bc6HSfloat:
  710. case Format.Bc6HUfloat:
  711. result = BCnDecoder.DecodeBC6(result, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
  712. break;
  713. case Format.Bc7Srgb:
  714. case Format.Bc7Unorm:
  715. result = BCnDecoder.DecodeBC7(result, width, height, sliceDepth, levels, layers);
  716. break;
  717. }
  718. }
  719. else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
  720. {
  721. result = PixelConverter.ConvertR4G4ToR4G4B4A4(result, width);
  722. if (!_context.Capabilities.SupportsR4G4B4A4Format)
  723. {
  724. result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
  725. }
  726. }
  727. else if (Format == Format.R4G4B4A4Unorm)
  728. {
  729. if (!_context.Capabilities.SupportsR4G4B4A4Format)
  730. {
  731. result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
  732. }
  733. }
  734. else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked())
  735. {
  736. switch (Format)
  737. {
  738. case Format.B5G6R5Unorm:
  739. case Format.R5G6B5Unorm:
  740. result = PixelConverter.ConvertR5G6B5ToR8G8B8A8(result, width);
  741. break;
  742. case Format.B5G5R5A1Unorm:
  743. case Format.R5G5B5X1Unorm:
  744. case Format.R5G5B5A1Unorm:
  745. result = PixelConverter.ConvertR5G5B5ToR8G8B8A8(result, width, Format == Format.R5G5B5X1Unorm);
  746. break;
  747. case Format.A1B5G5R5Unorm:
  748. result = PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result, width);
  749. break;
  750. case Format.R4G4B4A4Unorm:
  751. result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
  752. break;
  753. }
  754. }
  755. return result;
  756. }
  757. /// <summary>
  758. /// Converts texture data from a format and layout that is supported by the host GPU, back into the intended format on the guest GPU.
  759. /// </summary>
  760. /// <param name="output">Optional output span to convert into</param>
  761. /// <param name="data">Data to be converted</param>
  762. /// <param name="level">Mip level to convert</param>
  763. /// <param name="single">True to convert a single slice</param>
  764. /// <returns>Converted data</returns>
  765. public ReadOnlySpan<byte> ConvertFromHostCompatibleFormat(Span<byte> output, ReadOnlySpan<byte> data, int level = 0, bool single = false)
  766. {
  767. if (Target != Target.TextureBuffer)
  768. {
  769. int width = Info.Width;
  770. int height = Info.Height;
  771. int depth = _depth;
  772. int layers = single ? 1 : _layers;
  773. int levels = single ? 1 : (Info.Levels - level);
  774. width = Math.Max(width >> level, 1);
  775. height = Math.Max(height >> level, 1);
  776. depth = Math.Max(depth >> level, 1);
  777. if (Info.IsLinear)
  778. {
  779. data = LayoutConverter.ConvertLinearToLinearStrided(
  780. output,
  781. Info.Width,
  782. Info.Height,
  783. Info.FormatInfo.BlockWidth,
  784. Info.FormatInfo.BlockHeight,
  785. Info.Stride,
  786. Info.FormatInfo.BytesPerPixel,
  787. data);
  788. }
  789. else
  790. {
  791. data = LayoutConverter.ConvertLinearToBlockLinear(
  792. output,
  793. width,
  794. height,
  795. depth,
  796. single ? 1 : depth,
  797. levels,
  798. layers,
  799. Info.FormatInfo.BlockWidth,
  800. Info.FormatInfo.BlockHeight,
  801. Info.FormatInfo.BytesPerPixel,
  802. Info.GobBlocksInY,
  803. Info.GobBlocksInZ,
  804. Info.GobBlocksInTileX,
  805. _sizeInfo,
  806. data);
  807. }
  808. }
  809. return data;
  810. }
  811. /// <summary>
  812. /// Flushes the texture data.
  813. /// This causes the texture data to be written back to guest memory.
  814. /// If the texture was written by the GPU, this includes all modification made by the GPU
  815. /// up to this point.
  816. /// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
  817. /// This may cause data corruption if the memory is already being used for something else on the CPU side.
  818. /// </summary>
  819. /// <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>
  820. /// <returns>True if data was flushed, false otherwise</returns>
  821. public bool FlushModified(bool tracked = true)
  822. {
  823. return TextureCompatibility.CanTextureFlush(Info, _context.Capabilities) && Group.FlushModified(this, tracked);
  824. }
  825. /// <summary>
  826. /// Flushes the texture data.
  827. /// This causes the texture data to be written back to guest memory.
  828. /// If the texture was written by the GPU, this includes all modification made by the GPU
  829. /// up to this point.
  830. /// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
  831. /// This may cause data corruption if the memory is already being used for something else on the CPU side.
  832. /// </summary>
  833. /// <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>
  834. public void Flush(bool tracked)
  835. {
  836. if (TextureCompatibility.CanTextureFlush(Info, _context.Capabilities))
  837. {
  838. FlushTextureDataToGuest(tracked);
  839. }
  840. }
  841. /// <summary>
  842. /// Gets a host texture to use for flushing the texture, at 1x resolution.
  843. /// If the HostTexture is already at 1x resolution, it is returned directly.
  844. /// </summary>
  845. /// <returns>The host texture to flush</returns>
  846. public ITexture GetFlushTexture()
  847. {
  848. ITexture texture = HostTexture;
  849. if (ScaleFactor != 1f)
  850. {
  851. // If needed, create a texture to flush back to host at 1x scale.
  852. texture = _flushHostTexture = GetScaledHostTexture(1f, true, _flushHostTexture);
  853. }
  854. return texture;
  855. }
  856. /// <summary>
  857. /// Gets data from the host GPU, and flushes it all to guest memory.
  858. /// </summary>
  859. /// <remarks>
  860. /// This method should be used to retrieve data that was modified by the host GPU.
  861. /// This is not cheap, avoid doing that unless strictly needed.
  862. /// When possible, the data is written directly into guest memory, rather than copied.
  863. /// </remarks>
  864. /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
  865. /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
  866. public void FlushTextureDataToGuest(bool tracked, ITexture texture = null)
  867. {
  868. using WritableRegion region = _physicalMemory.GetWritableRegion(Range, tracked);
  869. GetTextureDataFromGpu(region.Memory.Span, tracked, texture);
  870. }
  871. /// <summary>
  872. /// Gets data from the host GPU.
  873. /// </summary>
  874. /// <remarks>
  875. /// This method should be used to retrieve data that was modified by the host GPU.
  876. /// This is not cheap, avoid doing that unless strictly needed.
  877. /// </remarks>
  878. /// <param name="output">An output span to place the texture data into</param>
  879. /// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
  880. /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
  881. private void GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null)
  882. {
  883. PinnedSpan<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. ConvertFromHostCompatibleFormat(output, data.Get());
  908. data.Dispose();
  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. public void GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
  923. {
  924. PinnedSpan<byte> data;
  925. if (texture != null)
  926. {
  927. data = texture.GetData(layer, level);
  928. }
  929. else
  930. {
  931. if (blacklist)
  932. {
  933. BlacklistScale();
  934. data = HostTexture.GetData(layer, level);
  935. }
  936. else if (ScaleFactor != 1f)
  937. {
  938. float scale = ScaleFactor;
  939. SetScale(1f);
  940. data = HostTexture.GetData(layer, level);
  941. SetScale(scale);
  942. }
  943. else
  944. {
  945. data = HostTexture.GetData(layer, level);
  946. }
  947. }
  948. ConvertFromHostCompatibleFormat(output, data.Get(), level, true);
  949. data.Dispose();
  950. }
  951. /// <summary>
  952. /// This performs a strict comparison, used to check if this texture is equal to the one supplied.
  953. /// </summary>
  954. /// <param name="info">Texture information to compare against</param>
  955. /// <param name="flags">Comparison flags</param>
  956. /// <returns>A value indicating how well this texture matches the given info</returns>
  957. public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
  958. {
  959. bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0;
  960. TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0);
  961. if (matchQuality == TextureMatchQuality.NoMatch)
  962. {
  963. return matchQuality;
  964. }
  965. if (!TextureCompatibility.LayoutMatches(Info, info))
  966. {
  967. return TextureMatchQuality.NoMatch;
  968. }
  969. if (!TextureCompatibility.SizeMatches(Info, info, forSampler))
  970. {
  971. return TextureMatchQuality.NoMatch;
  972. }
  973. if ((flags & TextureSearchFlags.ForSampler) != 0)
  974. {
  975. if (!TextureCompatibility.SamplerParamsMatches(Info, info))
  976. {
  977. return TextureMatchQuality.NoMatch;
  978. }
  979. }
  980. if ((flags & TextureSearchFlags.ForCopy) != 0)
  981. {
  982. bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;
  983. if (!msTargetCompatible && !TextureCompatibility.TargetAndSamplesCompatible(Info, info))
  984. {
  985. return TextureMatchQuality.NoMatch;
  986. }
  987. }
  988. else if (!TextureCompatibility.TargetAndSamplesCompatible(Info, info))
  989. {
  990. return TextureMatchQuality.NoMatch;
  991. }
  992. return Info.Levels == info.Levels ? matchQuality : TextureMatchQuality.NoMatch;
  993. }
  994. /// <summary>
  995. /// Check if it's possible to create a view, with the given parameters, from this texture.
  996. /// </summary>
  997. /// <param name="info">Texture view information</param>
  998. /// <param name="range">Texture view physical memory ranges</param>
  999. /// <param name="exactSize">Indicates if the texture sizes must be exactly equal, or width is allowed to differ</param>
  1000. /// <param name="layerSize">Layer size on the given texture</param>
  1001. /// <param name="caps">Host GPU capabilities</param>
  1002. /// <param name="firstLayer">Texture view initial layer on this texture</param>
  1003. /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
  1004. /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
  1005. public TextureViewCompatibility IsViewCompatible(
  1006. TextureInfo info,
  1007. MultiRange range,
  1008. bool exactSize,
  1009. int layerSize,
  1010. Capabilities caps,
  1011. out int firstLayer,
  1012. out int firstLevel)
  1013. {
  1014. TextureViewCompatibility result = TextureViewCompatibility.Full;
  1015. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
  1016. if (result != TextureViewCompatibility.Incompatible)
  1017. {
  1018. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps));
  1019. bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
  1020. if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
  1021. {
  1022. result = TextureViewCompatibility.Incompatible;
  1023. }
  1024. if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
  1025. {
  1026. // AMD and Intel have a bug where the view format is always ignored;
  1027. // they use the parent format instead.
  1028. // Create a copy dependency to avoid this issue.
  1029. result = TextureViewCompatibility.CopyOnly;
  1030. }
  1031. }
  1032. firstLayer = 0;
  1033. firstLevel = 0;
  1034. if (result == TextureViewCompatibility.Incompatible)
  1035. {
  1036. return TextureViewCompatibility.Incompatible;
  1037. }
  1038. int offset = Range.FindOffset(range);
  1039. if (offset < 0 || !_sizeInfo.FindView(offset, out firstLayer, out firstLevel))
  1040. {
  1041. return TextureViewCompatibility.LayoutIncompatible;
  1042. }
  1043. if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel))
  1044. {
  1045. return TextureViewCompatibility.LayoutIncompatible;
  1046. }
  1047. if (info.GetSlices() > 1 && LayerSize != layerSize)
  1048. {
  1049. return TextureViewCompatibility.LayoutIncompatible;
  1050. }
  1051. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, exactSize, firstLevel));
  1052. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
  1053. return result;
  1054. }
  1055. /// <summary>
  1056. /// Gets a texture of the specified target type from this texture.
  1057. /// This can be used to get an array texture from a non-array texture and vice-versa.
  1058. /// If this texture and the requested targets are equal, then this texture Host texture is returned directly.
  1059. /// </summary>
  1060. /// <param name="target">The desired target type</param>
  1061. /// <returns>A view of this texture with the requested target, or null if the target is invalid for this texture</returns>
  1062. public ITexture GetTargetTexture(Target target)
  1063. {
  1064. if (target == Target)
  1065. {
  1066. return HostTexture;
  1067. }
  1068. if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
  1069. {
  1070. FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info, _context.Capabilities);
  1071. TextureCreateInfo createInfo = new TextureCreateInfo(
  1072. Info.Width,
  1073. Info.Height,
  1074. target == Target.CubemapArray ? 6 : 1,
  1075. Info.Levels,
  1076. Info.Samples,
  1077. formatInfo.BlockWidth,
  1078. formatInfo.BlockHeight,
  1079. formatInfo.BytesPerPixel,
  1080. formatInfo.Format,
  1081. Info.DepthStencilMode,
  1082. target,
  1083. Info.SwizzleR,
  1084. Info.SwizzleG,
  1085. Info.SwizzleB,
  1086. Info.SwizzleA);
  1087. ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0);
  1088. _arrayViewTexture = viewTexture;
  1089. _arrayViewTarget = target;
  1090. return viewTexture;
  1091. }
  1092. else if (_arrayViewTarget == target)
  1093. {
  1094. return _arrayViewTexture;
  1095. }
  1096. return null;
  1097. }
  1098. /// <summary>
  1099. /// Determine if this texture can have anisotropic filtering forced.
  1100. /// Filtered textures that we might want to force anisotropy on should have a lot of mip levels.
  1101. /// </summary>
  1102. /// <returns>True if anisotropic filtering can be forced, false otherwise</returns>
  1103. private bool CanTextureForceAnisotropy()
  1104. {
  1105. if (!(Target == Target.Texture2D || Target == Target.Texture2DArray))
  1106. {
  1107. return false;
  1108. }
  1109. int maxSize = Math.Max(Info.Width, Info.Height);
  1110. int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
  1111. return Info.Levels >= Math.Min(MinLevelsForForceAnisotropy, maxLevels);
  1112. }
  1113. /// <summary>
  1114. /// Check if this texture and the specified target have the same number of dimensions.
  1115. /// For the purposes of this comparison, 2D and 2D Multisample textures are not considered to have
  1116. /// the same number of dimensions. Same for Cubemap and 3D textures.
  1117. /// </summary>
  1118. /// <param name="target">The target to compare with</param>
  1119. /// <returns>True if both targets have the same number of dimensions, false otherwise</returns>
  1120. private bool IsSameDimensionsTarget(Target target)
  1121. {
  1122. switch (Info.Target)
  1123. {
  1124. case Target.Texture1D:
  1125. case Target.Texture1DArray:
  1126. return target == Target.Texture1D ||
  1127. target == Target.Texture1DArray;
  1128. case Target.Texture2D:
  1129. case Target.Texture2DArray:
  1130. return target == Target.Texture2D ||
  1131. target == Target.Texture2DArray;
  1132. case Target.Cubemap:
  1133. case Target.CubemapArray:
  1134. return target == Target.Cubemap ||
  1135. target == Target.CubemapArray;
  1136. case Target.Texture2DMultisample:
  1137. case Target.Texture2DMultisampleArray:
  1138. return target == Target.Texture2DMultisample ||
  1139. target == Target.Texture2DMultisampleArray;
  1140. case Target.Texture3D:
  1141. return target == Target.Texture3D;
  1142. }
  1143. return false;
  1144. }
  1145. /// <summary>
  1146. /// Replaces view texture information.
  1147. /// This should only be used for child textures with a parent.
  1148. /// </summary>
  1149. /// <param name="parent">The parent texture</param>
  1150. /// <param name="info">The new view texture information</param>
  1151. /// <param name="hostTexture">The new host texture</param>
  1152. /// <param name="firstLayer">The first layer of the view</param>
  1153. /// <param name="firstLevel">The first level of the view</param>
  1154. public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture, int firstLayer, int firstLevel)
  1155. {
  1156. IncrementReferenceCount();
  1157. parent._viewStorage.SynchronizeMemory();
  1158. // If this texture has views, they must be given to the new parent.
  1159. if (_views.Count > 0)
  1160. {
  1161. Texture[] viewCopy = _views.ToArray();
  1162. foreach (Texture view in viewCopy)
  1163. {
  1164. TextureCreateInfo createInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, ScaleFactor);
  1165. ITexture newView = parent.HostTexture.CreateView(createInfo, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
  1166. view.ReplaceView(parent, view.Info, newView, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
  1167. }
  1168. }
  1169. ReplaceStorage(hostTexture);
  1170. if (_viewStorage != this)
  1171. {
  1172. _viewStorage.RemoveView(this);
  1173. }
  1174. FirstLayer = parent.FirstLayer + firstLayer;
  1175. FirstLevel = parent.FirstLevel + firstLevel;
  1176. parent._viewStorage.AddView(this);
  1177. SetInfo(info);
  1178. DecrementReferenceCount();
  1179. }
  1180. /// <summary>
  1181. /// Sets the internal texture information structure.
  1182. /// </summary>
  1183. /// <param name="info">The new texture information</param>
  1184. private void SetInfo(TextureInfo info)
  1185. {
  1186. Info = info;
  1187. Target = info.Target;
  1188. Width = info.Width;
  1189. Height = info.Height;
  1190. CanForceAnisotropy = CanTextureForceAnisotropy();
  1191. _depth = info.GetDepth();
  1192. _layers = info.GetLayers();
  1193. }
  1194. /// <summary>
  1195. /// Signals that the texture has been modified.
  1196. /// </summary>
  1197. public void SignalModified()
  1198. {
  1199. _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
  1200. if (_modifiedStale || Group.HasCopyDependencies)
  1201. {
  1202. _modifiedStale = false;
  1203. Group.SignalModified(this);
  1204. }
  1205. _physicalMemory.TextureCache.Lift(this);
  1206. }
  1207. /// <summary>
  1208. /// Signals that a texture has been bound, or has been unbound.
  1209. /// During this time, lazy copies will not clear the dirty flag.
  1210. /// </summary>
  1211. /// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
  1212. public void SignalModifying(bool bound)
  1213. {
  1214. if (bound)
  1215. {
  1216. _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
  1217. }
  1218. if (_modifiedStale || Group.HasCopyDependencies)
  1219. {
  1220. _modifiedStale = false;
  1221. Group.SignalModifying(this, bound);
  1222. }
  1223. _physicalMemory.TextureCache.Lift(this);
  1224. if (bound)
  1225. {
  1226. IncrementReferenceCount();
  1227. }
  1228. else
  1229. {
  1230. DecrementReferenceCount();
  1231. }
  1232. }
  1233. /// <summary>
  1234. /// Replaces the host texture, while disposing of the old one if needed.
  1235. /// </summary>
  1236. /// <param name="hostTexture">The new host texture</param>
  1237. private void ReplaceStorage(ITexture hostTexture)
  1238. {
  1239. DisposeTextures();
  1240. HostTexture = hostTexture;
  1241. }
  1242. /// <summary>
  1243. /// Determine if any of this texture's data overlaps with another.
  1244. /// </summary>
  1245. /// <param name="texture">The texture to check against</param>
  1246. /// <param name="compatibility">The view compatibility of the two textures</param>
  1247. /// <returns>True if any slice of the textures overlap, false otherwise</returns>
  1248. public bool DataOverlaps(Texture texture, TextureViewCompatibility compatibility)
  1249. {
  1250. if (compatibility == TextureViewCompatibility.LayoutIncompatible && Info.GobBlocksInZ > 1 && Info.GobBlocksInZ == texture.Info.GobBlocksInZ)
  1251. {
  1252. // Allow overlapping slices of layout compatible 3D textures with matching GobBlocksInZ, as they are interleaved.
  1253. return false;
  1254. }
  1255. if (texture._sizeInfo.AllOffsets.Length == 1 && _sizeInfo.AllOffsets.Length == 1)
  1256. {
  1257. return Range.OverlapsWith(texture.Range);
  1258. }
  1259. MultiRange otherRange = texture.Range;
  1260. IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.Slice((ulong)region.Offset, (ulong)region.Size));
  1261. IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.Slice((ulong)region.Offset, (ulong)region.Size));
  1262. foreach (MultiRange region in regions)
  1263. {
  1264. foreach (MultiRange otherRegion in otherRegions)
  1265. {
  1266. if (region.OverlapsWith(otherRegion))
  1267. {
  1268. return true;
  1269. }
  1270. }
  1271. }
  1272. return false;
  1273. }
  1274. /// <summary>
  1275. /// Increments the texture reference count.
  1276. /// </summary>
  1277. public void IncrementReferenceCount()
  1278. {
  1279. _referenceCount++;
  1280. }
  1281. /// <summary>
  1282. /// Increments the reference count and records the given texture pool and ID as a pool owner.
  1283. /// </summary>
  1284. /// <param name="pool">The texture pool this texture has been added to</param>
  1285. /// <param name="id">The ID of the reference to this texture in the pool</param>
  1286. /// <param name="gpuVa">GPU VA of the pool reference</param>
  1287. public void IncrementReferenceCount(TexturePool pool, int id, ulong gpuVa)
  1288. {
  1289. lock (_poolOwners)
  1290. {
  1291. _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id, GpuAddress = gpuVa });
  1292. }
  1293. _referenceCount++;
  1294. if (ShortCacheEntry != null)
  1295. {
  1296. _physicalMemory.TextureCache.RemoveShortCache(this);
  1297. }
  1298. }
  1299. /// <summary>
  1300. /// Indicates that the texture has one reference left, and will delete on reference decrement.
  1301. /// </summary>
  1302. /// <returns>True if there is one reference remaining, false otherwise</returns>
  1303. public bool HasOneReference()
  1304. {
  1305. return _referenceCount == 1;
  1306. }
  1307. /// <summary>
  1308. /// Decrements the texture reference count.
  1309. /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
  1310. /// </summary>
  1311. /// <returns>True if the texture is now referenceless, false otherwise</returns>
  1312. public bool DecrementReferenceCount()
  1313. {
  1314. int newRefCount = --_referenceCount;
  1315. if (newRefCount == 0)
  1316. {
  1317. if (_viewStorage != this)
  1318. {
  1319. _viewStorage.RemoveView(this);
  1320. }
  1321. _physicalMemory.TextureCache.RemoveTextureFromCache(this);
  1322. }
  1323. Debug.Assert(newRefCount >= 0);
  1324. DeleteIfNotUsed();
  1325. return newRefCount <= 0;
  1326. }
  1327. /// <summary>
  1328. /// Decrements the texture reference count, also removing an associated pool owner reference.
  1329. /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
  1330. /// </summary>
  1331. /// <param name="pool">The texture pool this texture is being removed from</param>
  1332. /// <param name="id">The ID of the reference to this texture in the pool</param>
  1333. /// <returns>True if the texture is now referenceless, false otherwise</returns>
  1334. public bool DecrementReferenceCount(TexturePool pool, int id = -1)
  1335. {
  1336. lock (_poolOwners)
  1337. {
  1338. int references = _poolOwners.RemoveAll(entry => entry.Pool == pool && entry.ID == id || id == -1);
  1339. if (references == 0)
  1340. {
  1341. // This reference has already been removed.
  1342. return _referenceCount <= 0;
  1343. }
  1344. Debug.Assert(references == 1);
  1345. }
  1346. return DecrementReferenceCount();
  1347. }
  1348. /// <summary>
  1349. /// Forcibly remove this texture from all pools that reference it.
  1350. /// </summary>
  1351. /// <param name="deferred">Indicates if the removal is being done from another thread.</param>
  1352. public void RemoveFromPools(bool deferred)
  1353. {
  1354. lock (_poolOwners)
  1355. {
  1356. foreach (var owner in _poolOwners)
  1357. {
  1358. owner.Pool.ForceRemove(this, owner.ID, deferred);
  1359. }
  1360. _poolOwners.Clear();
  1361. }
  1362. if (ShortCacheEntry != null && _context.IsGpuThread())
  1363. {
  1364. // If this is called from another thread (unmapped), the short cache will
  1365. // have to remove this texture on a future tick.
  1366. _physicalMemory.TextureCache.RemoveShortCache(this);
  1367. }
  1368. InvalidatedSequence++;
  1369. }
  1370. /// <summary>
  1371. /// Queue updating texture mappings on the pool. Happens from another thread.
  1372. /// </summary>
  1373. public void UpdatePoolMappings()
  1374. {
  1375. lock (_poolOwners)
  1376. {
  1377. ulong address = 0;
  1378. foreach (var owner in _poolOwners)
  1379. {
  1380. if (address == 0 || address == owner.GpuAddress)
  1381. {
  1382. address = owner.GpuAddress;
  1383. owner.Pool.QueueUpdateMapping(this, owner.ID);
  1384. }
  1385. else
  1386. {
  1387. // If there is a different GPU VA mapping, prefer the first and delete the others.
  1388. owner.Pool.ForceRemove(this, owner.ID, true);
  1389. }
  1390. }
  1391. _poolOwners.Clear();
  1392. }
  1393. InvalidatedSequence++;
  1394. }
  1395. /// <summary>
  1396. /// Delete the texture if it is not used anymore.
  1397. /// The texture is considered unused when the reference count is zero,
  1398. /// and it has no child views.
  1399. /// </summary>
  1400. private void DeleteIfNotUsed()
  1401. {
  1402. // We can delete the texture as long it is not being used
  1403. // in any cache (the reference count is 0 in this case), and
  1404. // also all views that may be created from this texture were
  1405. // already deleted (views count is 0).
  1406. if (_referenceCount == 0 && _views.Count == 0)
  1407. {
  1408. Dispose();
  1409. }
  1410. }
  1411. /// <summary>
  1412. /// Performs texture disposal, deleting the texture.
  1413. /// </summary>
  1414. private void DisposeTextures()
  1415. {
  1416. InvalidatedSequence++;
  1417. _currentData = null;
  1418. HostTexture.Release();
  1419. _arrayViewTexture?.Release();
  1420. _arrayViewTexture = null;
  1421. _flushHostTexture?.Release();
  1422. _flushHostTexture = null;
  1423. _setHostTexture?.Release();
  1424. _setHostTexture = null;
  1425. }
  1426. /// <summary>
  1427. /// Called when the memory for this texture has been unmapped.
  1428. /// Calls are from non-gpu threads.
  1429. /// </summary>
  1430. /// <param name="unmapRange">The range of memory being unmapped</param>
  1431. public void Unmapped(MultiRange unmapRange)
  1432. {
  1433. ChangedMapping = true;
  1434. if (Group.Storage == this)
  1435. {
  1436. Group.ClearModified(unmapRange);
  1437. }
  1438. UpdatePoolMappings();
  1439. }
  1440. /// <summary>
  1441. /// Performs texture disposal, deleting the texture.
  1442. /// </summary>
  1443. public void Dispose()
  1444. {
  1445. DisposeTextures();
  1446. if (Group.Storage == this)
  1447. {
  1448. Group.Dispose();
  1449. }
  1450. }
  1451. }
  1452. }