Texture.cs 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707
  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. If empty, one is generated</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. /// <returns>The span containing the texture data</returns>
  882. private ReadOnlySpan<byte> GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null)
  883. {
  884. ReadOnlySpan<byte> data;
  885. if (texture != null)
  886. {
  887. data = texture.GetData();
  888. }
  889. else
  890. {
  891. if (blacklist)
  892. {
  893. BlacklistScale();
  894. data = HostTexture.GetData();
  895. }
  896. else if (ScaleFactor != 1f)
  897. {
  898. float scale = ScaleFactor;
  899. SetScale(1f);
  900. data = HostTexture.GetData();
  901. SetScale(scale);
  902. }
  903. else
  904. {
  905. data = HostTexture.GetData();
  906. }
  907. }
  908. data = ConvertFromHostCompatibleFormat(output, data);
  909. return data;
  910. }
  911. /// <summary>
  912. /// Gets data from the host GPU for a single slice.
  913. /// </summary>
  914. /// <remarks>
  915. /// This method should be used to retrieve data that was modified by the host GPU.
  916. /// This is not cheap, avoid doing that unless strictly needed.
  917. /// </remarks>
  918. /// <param name="output">An output span to place the texture data into. If empty, one is generated</param>
  919. /// <param name="layer">The layer of the texture to flush</param>
  920. /// <param name="level">The level of the texture to flush</param>
  921. /// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
  922. /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
  923. /// <returns>The span containing the texture data</returns>
  924. public ReadOnlySpan<byte> GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
  925. {
  926. ReadOnlySpan<byte> data;
  927. if (texture != null)
  928. {
  929. data = texture.GetData(layer, level);
  930. }
  931. else
  932. {
  933. if (blacklist)
  934. {
  935. BlacklistScale();
  936. data = HostTexture.GetData(layer, level);
  937. }
  938. else if (ScaleFactor != 1f)
  939. {
  940. float scale = ScaleFactor;
  941. SetScale(1f);
  942. data = HostTexture.GetData(layer, level);
  943. SetScale(scale);
  944. }
  945. else
  946. {
  947. data = HostTexture.GetData(layer, level);
  948. }
  949. }
  950. data = ConvertFromHostCompatibleFormat(output, data, level, true);
  951. return data;
  952. }
  953. /// <summary>
  954. /// This performs a strict comparison, used to check if this texture is equal to the one supplied.
  955. /// </summary>
  956. /// <param name="info">Texture information to compare against</param>
  957. /// <param name="flags">Comparison flags</param>
  958. /// <returns>A value indicating how well this texture matches the given info</returns>
  959. public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
  960. {
  961. bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0;
  962. TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0);
  963. if (matchQuality == TextureMatchQuality.NoMatch)
  964. {
  965. return matchQuality;
  966. }
  967. if (!TextureCompatibility.LayoutMatches(Info, info))
  968. {
  969. return TextureMatchQuality.NoMatch;
  970. }
  971. if (!TextureCompatibility.SizeMatches(Info, info, forSampler))
  972. {
  973. return TextureMatchQuality.NoMatch;
  974. }
  975. if ((flags & TextureSearchFlags.ForSampler) != 0)
  976. {
  977. if (!TextureCompatibility.SamplerParamsMatches(Info, info))
  978. {
  979. return TextureMatchQuality.NoMatch;
  980. }
  981. }
  982. if ((flags & TextureSearchFlags.ForCopy) != 0)
  983. {
  984. bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;
  985. if (!msTargetCompatible && !TextureCompatibility.TargetAndSamplesCompatible(Info, info))
  986. {
  987. return TextureMatchQuality.NoMatch;
  988. }
  989. }
  990. else if (!TextureCompatibility.TargetAndSamplesCompatible(Info, info))
  991. {
  992. return TextureMatchQuality.NoMatch;
  993. }
  994. return Info.Levels == info.Levels ? matchQuality : TextureMatchQuality.NoMatch;
  995. }
  996. /// <summary>
  997. /// Check if it's possible to create a view, with the given parameters, from this texture.
  998. /// </summary>
  999. /// <param name="info">Texture view information</param>
  1000. /// <param name="range">Texture view physical memory ranges</param>
  1001. /// <param name="exactSize">Indicates if the texture sizes must be exactly equal, or width is allowed to differ</param>
  1002. /// <param name="layerSize">Layer size on the given texture</param>
  1003. /// <param name="caps">Host GPU capabilities</param>
  1004. /// <param name="firstLayer">Texture view initial layer on this texture</param>
  1005. /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
  1006. /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
  1007. public TextureViewCompatibility IsViewCompatible(
  1008. TextureInfo info,
  1009. MultiRange range,
  1010. bool exactSize,
  1011. int layerSize,
  1012. Capabilities caps,
  1013. out int firstLayer,
  1014. out int firstLevel)
  1015. {
  1016. TextureViewCompatibility result = TextureViewCompatibility.Full;
  1017. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
  1018. if (result != TextureViewCompatibility.Incompatible)
  1019. {
  1020. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps));
  1021. bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
  1022. if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
  1023. {
  1024. result = TextureViewCompatibility.Incompatible;
  1025. }
  1026. if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
  1027. {
  1028. // AMD and Intel have a bug where the view format is always ignored;
  1029. // they use the parent format instead.
  1030. // Create a copy dependency to avoid this issue.
  1031. result = TextureViewCompatibility.CopyOnly;
  1032. }
  1033. }
  1034. firstLayer = 0;
  1035. firstLevel = 0;
  1036. if (result == TextureViewCompatibility.Incompatible)
  1037. {
  1038. return TextureViewCompatibility.Incompatible;
  1039. }
  1040. int offset = Range.FindOffset(range);
  1041. if (offset < 0 || !_sizeInfo.FindView(offset, out firstLayer, out firstLevel))
  1042. {
  1043. return TextureViewCompatibility.LayoutIncompatible;
  1044. }
  1045. if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel))
  1046. {
  1047. return TextureViewCompatibility.LayoutIncompatible;
  1048. }
  1049. if (info.GetSlices() > 1 && LayerSize != layerSize)
  1050. {
  1051. return TextureViewCompatibility.LayoutIncompatible;
  1052. }
  1053. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, exactSize, firstLevel));
  1054. result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
  1055. return result;
  1056. }
  1057. /// <summary>
  1058. /// Gets a texture of the specified target type from this texture.
  1059. /// This can be used to get an array texture from a non-array texture and vice-versa.
  1060. /// If this texture and the requested targets are equal, then this texture Host texture is returned directly.
  1061. /// </summary>
  1062. /// <param name="target">The desired target type</param>
  1063. /// <returns>A view of this texture with the requested target, or null if the target is invalid for this texture</returns>
  1064. public ITexture GetTargetTexture(Target target)
  1065. {
  1066. if (target == Target)
  1067. {
  1068. return HostTexture;
  1069. }
  1070. if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
  1071. {
  1072. FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info, _context.Capabilities);
  1073. TextureCreateInfo createInfo = new TextureCreateInfo(
  1074. Info.Width,
  1075. Info.Height,
  1076. target == Target.CubemapArray ? 6 : 1,
  1077. Info.Levels,
  1078. Info.Samples,
  1079. formatInfo.BlockWidth,
  1080. formatInfo.BlockHeight,
  1081. formatInfo.BytesPerPixel,
  1082. formatInfo.Format,
  1083. Info.DepthStencilMode,
  1084. target,
  1085. Info.SwizzleR,
  1086. Info.SwizzleG,
  1087. Info.SwizzleB,
  1088. Info.SwizzleA);
  1089. ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0);
  1090. _arrayViewTexture = viewTexture;
  1091. _arrayViewTarget = target;
  1092. return viewTexture;
  1093. }
  1094. else if (_arrayViewTarget == target)
  1095. {
  1096. return _arrayViewTexture;
  1097. }
  1098. return null;
  1099. }
  1100. /// <summary>
  1101. /// Determine if this texture can have anisotropic filtering forced.
  1102. /// Filtered textures that we might want to force anisotropy on should have a lot of mip levels.
  1103. /// </summary>
  1104. /// <returns>True if anisotropic filtering can be forced, false otherwise</returns>
  1105. private bool CanTextureForceAnisotropy()
  1106. {
  1107. if (!(Target == Target.Texture2D || Target == Target.Texture2DArray))
  1108. {
  1109. return false;
  1110. }
  1111. int maxSize = Math.Max(Info.Width, Info.Height);
  1112. int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
  1113. return Info.Levels >= Math.Min(MinLevelsForForceAnisotropy, maxLevels);
  1114. }
  1115. /// <summary>
  1116. /// Check if this texture and the specified target have the same number of dimensions.
  1117. /// For the purposes of this comparison, 2D and 2D Multisample textures are not considered to have
  1118. /// the same number of dimensions. Same for Cubemap and 3D textures.
  1119. /// </summary>
  1120. /// <param name="target">The target to compare with</param>
  1121. /// <returns>True if both targets have the same number of dimensions, false otherwise</returns>
  1122. private bool IsSameDimensionsTarget(Target target)
  1123. {
  1124. switch (Info.Target)
  1125. {
  1126. case Target.Texture1D:
  1127. case Target.Texture1DArray:
  1128. return target == Target.Texture1D ||
  1129. target == Target.Texture1DArray;
  1130. case Target.Texture2D:
  1131. case Target.Texture2DArray:
  1132. return target == Target.Texture2D ||
  1133. target == Target.Texture2DArray;
  1134. case Target.Cubemap:
  1135. case Target.CubemapArray:
  1136. return target == Target.Cubemap ||
  1137. target == Target.CubemapArray;
  1138. case Target.Texture2DMultisample:
  1139. case Target.Texture2DMultisampleArray:
  1140. return target == Target.Texture2DMultisample ||
  1141. target == Target.Texture2DMultisampleArray;
  1142. case Target.Texture3D:
  1143. return target == Target.Texture3D;
  1144. }
  1145. return false;
  1146. }
  1147. /// <summary>
  1148. /// Replaces view texture information.
  1149. /// This should only be used for child textures with a parent.
  1150. /// </summary>
  1151. /// <param name="parent">The parent texture</param>
  1152. /// <param name="info">The new view texture information</param>
  1153. /// <param name="hostTexture">The new host texture</param>
  1154. /// <param name="firstLayer">The first layer of the view</param>
  1155. /// <param name="firstLevel">The first level of the view</param>
  1156. public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture, int firstLayer, int firstLevel)
  1157. {
  1158. IncrementReferenceCount();
  1159. parent._viewStorage.SynchronizeMemory();
  1160. // If this texture has views, they must be given to the new parent.
  1161. if (_views.Count > 0)
  1162. {
  1163. Texture[] viewCopy = _views.ToArray();
  1164. foreach (Texture view in viewCopy)
  1165. {
  1166. TextureCreateInfo createInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, ScaleFactor);
  1167. ITexture newView = parent.HostTexture.CreateView(createInfo, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
  1168. view.ReplaceView(parent, view.Info, newView, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
  1169. }
  1170. }
  1171. ReplaceStorage(hostTexture);
  1172. if (_viewStorage != this)
  1173. {
  1174. _viewStorage.RemoveView(this);
  1175. }
  1176. FirstLayer = parent.FirstLayer + firstLayer;
  1177. FirstLevel = parent.FirstLevel + firstLevel;
  1178. parent._viewStorage.AddView(this);
  1179. SetInfo(info);
  1180. DecrementReferenceCount();
  1181. }
  1182. /// <summary>
  1183. /// Sets the internal texture information structure.
  1184. /// </summary>
  1185. /// <param name="info">The new texture information</param>
  1186. private void SetInfo(TextureInfo info)
  1187. {
  1188. Info = info;
  1189. Target = info.Target;
  1190. Width = info.Width;
  1191. Height = info.Height;
  1192. CanForceAnisotropy = CanTextureForceAnisotropy();
  1193. _depth = info.GetDepth();
  1194. _layers = info.GetLayers();
  1195. }
  1196. /// <summary>
  1197. /// Signals that the texture has been modified.
  1198. /// </summary>
  1199. public void SignalModified()
  1200. {
  1201. _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
  1202. if (_modifiedStale || Group.HasCopyDependencies)
  1203. {
  1204. _modifiedStale = false;
  1205. Group.SignalModified(this);
  1206. }
  1207. _physicalMemory.TextureCache.Lift(this);
  1208. }
  1209. /// <summary>
  1210. /// Signals that a texture has been bound, or has been unbound.
  1211. /// During this time, lazy copies will not clear the dirty flag.
  1212. /// </summary>
  1213. /// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
  1214. public void SignalModifying(bool bound)
  1215. {
  1216. if (bound)
  1217. {
  1218. _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
  1219. }
  1220. if (_modifiedStale || Group.HasCopyDependencies)
  1221. {
  1222. _modifiedStale = false;
  1223. Group.SignalModifying(this, bound);
  1224. }
  1225. _physicalMemory.TextureCache.Lift(this);
  1226. if (bound)
  1227. {
  1228. IncrementReferenceCount();
  1229. }
  1230. else
  1231. {
  1232. DecrementReferenceCount();
  1233. }
  1234. }
  1235. /// <summary>
  1236. /// Replaces the host texture, while disposing of the old one if needed.
  1237. /// </summary>
  1238. /// <param name="hostTexture">The new host texture</param>
  1239. private void ReplaceStorage(ITexture hostTexture)
  1240. {
  1241. DisposeTextures();
  1242. HostTexture = hostTexture;
  1243. }
  1244. /// <summary>
  1245. /// Determine if any of this texture's data overlaps with another.
  1246. /// </summary>
  1247. /// <param name="texture">The texture to check against</param>
  1248. /// <param name="compatibility">The view compatibility of the two textures</param>
  1249. /// <returns>True if any slice of the textures overlap, false otherwise</returns>
  1250. public bool DataOverlaps(Texture texture, TextureViewCompatibility compatibility)
  1251. {
  1252. if (compatibility == TextureViewCompatibility.LayoutIncompatible && Info.GobBlocksInZ > 1 && Info.GobBlocksInZ == texture.Info.GobBlocksInZ)
  1253. {
  1254. // Allow overlapping slices of layout compatible 3D textures with matching GobBlocksInZ, as they are interleaved.
  1255. return false;
  1256. }
  1257. if (texture._sizeInfo.AllOffsets.Length == 1 && _sizeInfo.AllOffsets.Length == 1)
  1258. {
  1259. return Range.OverlapsWith(texture.Range);
  1260. }
  1261. MultiRange otherRange = texture.Range;
  1262. IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.Slice((ulong)region.Offset, (ulong)region.Size));
  1263. IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.Slice((ulong)region.Offset, (ulong)region.Size));
  1264. foreach (MultiRange region in regions)
  1265. {
  1266. foreach (MultiRange otherRegion in otherRegions)
  1267. {
  1268. if (region.OverlapsWith(otherRegion))
  1269. {
  1270. return true;
  1271. }
  1272. }
  1273. }
  1274. return false;
  1275. }
  1276. /// <summary>
  1277. /// Increments the texture reference count.
  1278. /// </summary>
  1279. public void IncrementReferenceCount()
  1280. {
  1281. _referenceCount++;
  1282. }
  1283. /// <summary>
  1284. /// Increments the reference count and records the given texture pool and ID as a pool owner.
  1285. /// </summary>
  1286. /// <param name="pool">The texture pool this texture has been added to</param>
  1287. /// <param name="id">The ID of the reference to this texture in the pool</param>
  1288. /// <param name="gpuVa">GPU VA of the pool reference</param>
  1289. public void IncrementReferenceCount(TexturePool pool, int id, ulong gpuVa)
  1290. {
  1291. lock (_poolOwners)
  1292. {
  1293. _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id, GpuAddress = gpuVa });
  1294. }
  1295. _referenceCount++;
  1296. if (ShortCacheEntry != null)
  1297. {
  1298. _physicalMemory.TextureCache.RemoveShortCache(this);
  1299. }
  1300. }
  1301. /// <summary>
  1302. /// Indicates that the texture has one reference left, and will delete on reference decrement.
  1303. /// </summary>
  1304. /// <returns>True if there is one reference remaining, false otherwise</returns>
  1305. public bool HasOneReference()
  1306. {
  1307. return _referenceCount == 1;
  1308. }
  1309. /// <summary>
  1310. /// Decrements the texture reference count.
  1311. /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
  1312. /// </summary>
  1313. /// <returns>True if the texture is now referenceless, false otherwise</returns>
  1314. public bool DecrementReferenceCount()
  1315. {
  1316. int newRefCount = --_referenceCount;
  1317. if (newRefCount == 0)
  1318. {
  1319. if (_viewStorage != this)
  1320. {
  1321. _viewStorage.RemoveView(this);
  1322. }
  1323. _physicalMemory.TextureCache.RemoveTextureFromCache(this);
  1324. }
  1325. Debug.Assert(newRefCount >= 0);
  1326. DeleteIfNotUsed();
  1327. return newRefCount <= 0;
  1328. }
  1329. /// <summary>
  1330. /// Decrements the texture reference count, also removing an associated pool owner reference.
  1331. /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
  1332. /// </summary>
  1333. /// <param name="pool">The texture pool this texture is being removed from</param>
  1334. /// <param name="id">The ID of the reference to this texture in the pool</param>
  1335. /// <returns>True if the texture is now referenceless, false otherwise</returns>
  1336. public bool DecrementReferenceCount(TexturePool pool, int id = -1)
  1337. {
  1338. lock (_poolOwners)
  1339. {
  1340. int references = _poolOwners.RemoveAll(entry => entry.Pool == pool && entry.ID == id || id == -1);
  1341. if (references == 0)
  1342. {
  1343. // This reference has already been removed.
  1344. return _referenceCount <= 0;
  1345. }
  1346. Debug.Assert(references == 1);
  1347. }
  1348. return DecrementReferenceCount();
  1349. }
  1350. /// <summary>
  1351. /// Forcibly remove this texture from all pools that reference it.
  1352. /// </summary>
  1353. /// <param name="deferred">Indicates if the removal is being done from another thread.</param>
  1354. public void RemoveFromPools(bool deferred)
  1355. {
  1356. lock (_poolOwners)
  1357. {
  1358. foreach (var owner in _poolOwners)
  1359. {
  1360. owner.Pool.ForceRemove(this, owner.ID, deferred);
  1361. }
  1362. _poolOwners.Clear();
  1363. }
  1364. if (ShortCacheEntry != null && _context.IsGpuThread())
  1365. {
  1366. // If this is called from another thread (unmapped), the short cache will
  1367. // have to remove this texture on a future tick.
  1368. _physicalMemory.TextureCache.RemoveShortCache(this);
  1369. }
  1370. InvalidatedSequence++;
  1371. }
  1372. /// <summary>
  1373. /// Queue updating texture mappings on the pool. Happens from another thread.
  1374. /// </summary>
  1375. public void UpdatePoolMappings()
  1376. {
  1377. lock (_poolOwners)
  1378. {
  1379. ulong address = 0;
  1380. foreach (var owner in _poolOwners)
  1381. {
  1382. if (address == 0 || address == owner.GpuAddress)
  1383. {
  1384. address = owner.GpuAddress;
  1385. owner.Pool.QueueUpdateMapping(this, owner.ID);
  1386. }
  1387. else
  1388. {
  1389. // If there is a different GPU VA mapping, prefer the first and delete the others.
  1390. owner.Pool.ForceRemove(this, owner.ID, true);
  1391. }
  1392. }
  1393. _poolOwners.Clear();
  1394. }
  1395. InvalidatedSequence++;
  1396. }
  1397. /// <summary>
  1398. /// Delete the texture if it is not used anymore.
  1399. /// The texture is considered unused when the reference count is zero,
  1400. /// and it has no child views.
  1401. /// </summary>
  1402. private void DeleteIfNotUsed()
  1403. {
  1404. // We can delete the texture as long it is not being used
  1405. // in any cache (the reference count is 0 in this case), and
  1406. // also all views that may be created from this texture were
  1407. // already deleted (views count is 0).
  1408. if (_referenceCount == 0 && _views.Count == 0)
  1409. {
  1410. Dispose();
  1411. }
  1412. }
  1413. /// <summary>
  1414. /// Performs texture disposal, deleting the texture.
  1415. /// </summary>
  1416. private void DisposeTextures()
  1417. {
  1418. InvalidatedSequence++;
  1419. _currentData = null;
  1420. HostTexture.Release();
  1421. _arrayViewTexture?.Release();
  1422. _arrayViewTexture = null;
  1423. _flushHostTexture?.Release();
  1424. _flushHostTexture = null;
  1425. _setHostTexture?.Release();
  1426. _setHostTexture = null;
  1427. }
  1428. /// <summary>
  1429. /// Called when the memory for this texture has been unmapped.
  1430. /// Calls are from non-gpu threads.
  1431. /// </summary>
  1432. /// <param name="unmapRange">The range of memory being unmapped</param>
  1433. public void Unmapped(MultiRange unmapRange)
  1434. {
  1435. ChangedMapping = true;
  1436. if (Group.Storage == this)
  1437. {
  1438. Group.ClearModified(unmapRange);
  1439. }
  1440. UpdatePoolMappings();
  1441. }
  1442. /// <summary>
  1443. /// Performs texture disposal, deleting the texture.
  1444. /// </summary>
  1445. public void Dispose()
  1446. {
  1447. DisposeTextures();
  1448. if (Group.Storage == this)
  1449. {
  1450. Group.Dispose();
  1451. }
  1452. }
  1453. }
  1454. }