Texture.cs 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. using Ryujinx.Common;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Gpu.Memory;
  5. using Ryujinx.Graphics.Texture;
  6. using Ryujinx.Graphics.Texture.Astc;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Diagnostics;
  10. namespace Ryujinx.Graphics.Gpu.Image
  11. {
  12. /// <summary>
  13. /// Represents a cached GPU texture.
  14. /// </summary>
  15. class Texture : IRange, IDisposable
  16. {
  17. private GpuContext _context;
  18. private SizeInfo _sizeInfo;
  19. /// <summary>
  20. /// Texture format.
  21. /// </summary>
  22. public Format Format => Info.FormatInfo.Format;
  23. /// <summary>
  24. /// Texture information.
  25. /// </summary>
  26. public TextureInfo Info { get; private set; }
  27. private int _depth;
  28. private int _layers;
  29. private readonly int _firstLayer;
  30. private readonly int _firstLevel;
  31. private bool _hasData;
  32. private ITexture _arrayViewTexture;
  33. private Target _arrayViewTarget;
  34. private Texture _viewStorage;
  35. private List<Texture> _views;
  36. /// <summary>
  37. /// Host texture.
  38. /// </summary>
  39. public ITexture HostTexture { get; private set; }
  40. /// <summary>
  41. /// Intrusive linked list node used on the auto deletion texture cache.
  42. /// </summary>
  43. public LinkedListNode<Texture> CacheNode { get; set; }
  44. /// <summary>
  45. /// Event to fire when texture data is modified by the GPU.
  46. /// </summary>
  47. public event Action<Texture> Modified;
  48. /// <summary>
  49. /// Event to fire when texture data is disposed.
  50. /// </summary>
  51. public event Action<Texture> Disposed;
  52. /// <summary>
  53. /// Start address of the texture in guest memory.
  54. /// </summary>
  55. public ulong Address => Info.Address;
  56. /// <summary>
  57. /// End address of the texture in guest memory.
  58. /// </summary>
  59. public ulong EndAddress => Info.Address + Size;
  60. /// <summary>
  61. /// Texture size in bytes.
  62. /// </summary>
  63. public ulong Size => (ulong)_sizeInfo.TotalSize;
  64. private int _referenceCount;
  65. private int _sequenceNumber;
  66. /// <summary>
  67. /// Constructs a new instance of the cached GPU texture.
  68. /// </summary>
  69. /// <param name="context">GPU context that the texture belongs to</param>
  70. /// <param name="info">Texture information</param>
  71. /// <param name="sizeInfo">Size information of the texture</param>
  72. /// <param name="firstLayer">The first layer of the texture, or 0 if the texture has no parent</param>
  73. /// <param name="firstLevel">The first mipmap level of the texture, or 0 if the texture has no parent</param>
  74. private Texture(
  75. GpuContext context,
  76. TextureInfo info,
  77. SizeInfo sizeInfo,
  78. int firstLayer,
  79. int firstLevel)
  80. {
  81. InitializeTexture(context, info, sizeInfo);
  82. _firstLayer = firstLayer;
  83. _firstLevel = firstLevel;
  84. _hasData = true;
  85. }
  86. /// <summary>
  87. /// Constructs a new instance of the cached GPU texture.
  88. /// </summary>
  89. /// <param name="context">GPU context that the texture belongs to</param>
  90. /// <param name="info">Texture information</param>
  91. /// <param name="sizeInfo">Size information of the texture</param>
  92. public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
  93. {
  94. InitializeTexture(context, info, sizeInfo);
  95. TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, context.Capabilities);
  96. HostTexture = _context.Renderer.CreateTexture(createInfo);
  97. }
  98. /// <summary>
  99. /// Common texture initialization method.
  100. /// This sets the context, info and sizeInfo fields.
  101. /// Other fields are initialized with their default values.
  102. /// </summary>
  103. /// <param name="context">GPU context that the texture belongs to</param>
  104. /// <param name="info">Texture information</param>
  105. /// <param name="sizeInfo">Size information of the texture</param>
  106. private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
  107. {
  108. _context = context;
  109. _sizeInfo = sizeInfo;
  110. SetInfo(info);
  111. _viewStorage = this;
  112. _views = new List<Texture>();
  113. }
  114. /// <summary>
  115. /// Create a texture view from this texture.
  116. /// A texture view is defined as a child texture, from a sub-range of their parent texture.
  117. /// For example, the initial layer and mipmap level of the view can be defined, so the texture
  118. /// will start at the given layer/level of the parent texture.
  119. /// </summary>
  120. /// <param name="info">Child texture information</param>
  121. /// <param name="sizeInfo">Child texture size information</param>
  122. /// <param name="firstLayer">Start layer of the child texture on the parent texture</param>
  123. /// <param name="firstLevel">Start mipmap level of the child texture on the parent texture</param>
  124. /// <returns>The child texture</returns>
  125. public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, int firstLayer, int firstLevel)
  126. {
  127. Texture texture = new Texture(
  128. _context,
  129. info,
  130. sizeInfo,
  131. _firstLayer + firstLayer,
  132. _firstLevel + firstLevel);
  133. TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, _context.Capabilities);
  134. texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel);
  135. _viewStorage.AddView(texture);
  136. return texture;
  137. }
  138. /// <summary>
  139. /// Adds a child texture to this texture.
  140. /// </summary>
  141. /// <param name="texture">The child texture</param>
  142. private void AddView(Texture texture)
  143. {
  144. _views.Add(texture);
  145. texture._viewStorage = this;
  146. }
  147. /// <summary>
  148. /// Removes a child texture from this texture.
  149. /// </summary>
  150. /// <param name="texture">The child texture</param>
  151. private void RemoveView(Texture texture)
  152. {
  153. _views.Remove(texture);
  154. texture._viewStorage = null;
  155. DeleteIfNotUsed();
  156. }
  157. /// <summary>
  158. /// Changes the texture size.
  159. /// </summary>
  160. /// <remarks>
  161. /// This operation may also change the size of all mipmap levels, including from the parent
  162. /// and other possible child textures, to ensure that all sizes are consistent.
  163. /// </remarks>
  164. /// <param name="width">The new texture width</param>
  165. /// <param name="height">The new texture height</param>
  166. /// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
  167. public void ChangeSize(int width, int height, int depthOrLayers)
  168. {
  169. width <<= _firstLevel;
  170. height <<= _firstLevel;
  171. if (Info.Target == Target.Texture3D)
  172. {
  173. depthOrLayers <<= _firstLevel;
  174. }
  175. else
  176. {
  177. depthOrLayers = _viewStorage.Info.DepthOrLayers;
  178. }
  179. _viewStorage.RecreateStorageOrView(width, height, depthOrLayers);
  180. foreach (Texture view in _viewStorage._views)
  181. {
  182. int viewWidth = Math.Max(1, width >> view._firstLevel);
  183. int viewHeight = Math.Max(1, height >> view._firstLevel);
  184. int viewDepthOrLayers;
  185. if (view.Info.Target == Target.Texture3D)
  186. {
  187. viewDepthOrLayers = Math.Max(1, depthOrLayers >> view._firstLevel);
  188. }
  189. else
  190. {
  191. viewDepthOrLayers = view.Info.DepthOrLayers;
  192. }
  193. view.RecreateStorageOrView(viewWidth, viewHeight, viewDepthOrLayers);
  194. }
  195. }
  196. /// <summary>
  197. /// Recreates the texture storage (or view, in the case of child textures) of this texture.
  198. /// This allows recreating the texture with a new size.
  199. /// A copy is automatically performed from the old to the new texture.
  200. /// </summary>
  201. /// <param name="width">The new texture width</param>
  202. /// <param name="height">The new texture height</param>
  203. /// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
  204. private void RecreateStorageOrView(int width, int height, int depthOrLayers)
  205. {
  206. SetInfo(new TextureInfo(
  207. Info.Address,
  208. width,
  209. height,
  210. depthOrLayers,
  211. Info.Levels,
  212. Info.SamplesInX,
  213. Info.SamplesInY,
  214. Info.Stride,
  215. Info.IsLinear,
  216. Info.GobBlocksInY,
  217. Info.GobBlocksInZ,
  218. Info.GobBlocksInTileX,
  219. Info.Target,
  220. Info.FormatInfo,
  221. Info.DepthStencilMode,
  222. Info.SwizzleR,
  223. Info.SwizzleG,
  224. Info.SwizzleB,
  225. Info.SwizzleA));
  226. TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities);
  227. if (_viewStorage != this)
  228. {
  229. ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, _firstLayer, _firstLevel));
  230. }
  231. else
  232. {
  233. ITexture newStorage = _context.Renderer.CreateTexture(createInfo);
  234. HostTexture.CopyTo(newStorage, 0, 0);
  235. ReplaceStorage(newStorage);
  236. }
  237. }
  238. /// <summary>
  239. /// Synchronizes guest and host memory.
  240. /// This will overwrite the texture data with the texture data on the guest memory, if a CPU
  241. /// modification is detected.
  242. /// Be aware that this can cause texture data written by the GPU to be lost, this is just a
  243. /// one way copy (from CPU owned to GPU owned memory).
  244. /// </summary>
  245. public void SynchronizeMemory()
  246. {
  247. // Texture buffers are not handled here, instead they are invalidated (if modified)
  248. // when the texture is bound. This is handled by the buffer manager.
  249. if ((_sequenceNumber == _context.SequenceNumber && _hasData) || Info.Target == Target.TextureBuffer)
  250. {
  251. return;
  252. }
  253. _sequenceNumber = _context.SequenceNumber;
  254. (ulong, ulong)[] modifiedRanges = _context.PhysicalMemory.GetModifiedRanges(Address, Size, ResourceName.Texture);
  255. if (modifiedRanges.Length == 0 && _hasData)
  256. {
  257. return;
  258. }
  259. ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Address, Size);
  260. // If the texture was modified by the host GPU, we do partial invalidation
  261. // of the texture by getting GPU data and merging in the pages of memory
  262. // that were modified.
  263. // Note that if ASTC is not supported by the GPU we can't read it back since
  264. // it will use a different format. Since applications shouldn't be writing
  265. // ASTC textures from the GPU anyway, ignoring it should be safe.
  266. if (_context.Methods.TextureManager.IsTextureModified(this) && !Info.FormatInfo.Format.IsAstc())
  267. {
  268. Span<byte> gpuData = GetTextureDataFromGpu();
  269. ulong endAddress = Address + Size;
  270. for (int i = 0; i < modifiedRanges.Length; i++)
  271. {
  272. (ulong modifiedAddress, ulong modifiedSize) = modifiedRanges[i];
  273. ulong endModifiedAddress = modifiedAddress + modifiedSize;
  274. if (modifiedAddress < Address)
  275. {
  276. modifiedAddress = Address;
  277. }
  278. if (endModifiedAddress > endAddress)
  279. {
  280. endModifiedAddress = endAddress;
  281. }
  282. modifiedSize = endModifiedAddress - modifiedAddress;
  283. int offset = (int)(modifiedAddress - Address);
  284. int length = (int)modifiedSize;
  285. data.Slice(offset, length).CopyTo(gpuData.Slice(offset, length));
  286. }
  287. data = gpuData;
  288. }
  289. data = ConvertToHostCompatibleFormat(data);
  290. HostTexture.SetData(data);
  291. _hasData = true;
  292. }
  293. /// <summary>
  294. /// Converts texture data to a format and layout that is supported by the host GPU.
  295. /// </summary>
  296. /// <param name="data">Data to be converted</param>
  297. /// <returns>Converted data</returns>
  298. private ReadOnlySpan<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data)
  299. {
  300. if (Info.IsLinear)
  301. {
  302. data = LayoutConverter.ConvertLinearStridedToLinear(
  303. Info.Width,
  304. Info.Height,
  305. Info.FormatInfo.BlockWidth,
  306. Info.FormatInfo.BlockHeight,
  307. Info.Stride,
  308. Info.FormatInfo.BytesPerPixel,
  309. data);
  310. }
  311. else
  312. {
  313. data = LayoutConverter.ConvertBlockLinearToLinear(
  314. Info.Width,
  315. Info.Height,
  316. _depth,
  317. Info.Levels,
  318. _layers,
  319. Info.FormatInfo.BlockWidth,
  320. Info.FormatInfo.BlockHeight,
  321. Info.FormatInfo.BytesPerPixel,
  322. Info.GobBlocksInY,
  323. Info.GobBlocksInZ,
  324. Info.GobBlocksInTileX,
  325. _sizeInfo,
  326. data);
  327. }
  328. if (!_context.Capabilities.SupportsAstcCompression && Info.FormatInfo.Format.IsAstc())
  329. {
  330. if (!AstcDecoder.TryDecodeToRgba8(
  331. data.ToArray(),
  332. Info.FormatInfo.BlockWidth,
  333. Info.FormatInfo.BlockHeight,
  334. Info.Width,
  335. Info.Height,
  336. _depth,
  337. Info.Levels,
  338. out Span<byte> decoded))
  339. {
  340. string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
  341. Logger.PrintDebug(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.Address:X} ({texInfo}).");
  342. }
  343. data = decoded;
  344. }
  345. return data;
  346. }
  347. /// <summary>
  348. /// Flushes the texture data.
  349. /// This causes the texture data to be written back to guest memory.
  350. /// If the texture was written by the GPU, this includes all modification made by the GPU
  351. /// up to this point.
  352. /// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
  353. /// This may cause data corruption if the memory is already being used for something else on the CPU side.
  354. /// </summary>
  355. public void Flush()
  356. {
  357. _context.PhysicalMemory.Write(Address, GetTextureDataFromGpu());
  358. }
  359. /// <summary>
  360. /// Gets data from the host GPU.
  361. /// </summary>
  362. /// <remarks>
  363. /// This method should be used to retrieve data that was modified by the host GPU.
  364. /// This is not cheap, avoid doing that unless strictly needed.
  365. /// </remarks>
  366. /// <returns>Host texture data</returns>
  367. private Span<byte> GetTextureDataFromGpu()
  368. {
  369. Span<byte> data = HostTexture.GetData();
  370. if (Info.IsLinear)
  371. {
  372. data = LayoutConverter.ConvertLinearToLinearStrided(
  373. Info.Width,
  374. Info.Height,
  375. Info.FormatInfo.BlockWidth,
  376. Info.FormatInfo.BlockHeight,
  377. Info.Stride,
  378. Info.FormatInfo.BytesPerPixel,
  379. data);
  380. }
  381. else
  382. {
  383. data = LayoutConverter.ConvertLinearToBlockLinear(
  384. Info.Width,
  385. Info.Height,
  386. _depth,
  387. Info.Levels,
  388. _layers,
  389. Info.FormatInfo.BlockWidth,
  390. Info.FormatInfo.BlockHeight,
  391. Info.FormatInfo.BytesPerPixel,
  392. Info.GobBlocksInY,
  393. Info.GobBlocksInZ,
  394. Info.GobBlocksInTileX,
  395. _sizeInfo,
  396. data);
  397. }
  398. return data;
  399. }
  400. /// <summary>
  401. /// Performs a comparison of this texture information, with the specified texture information.
  402. /// This performs a strict comparison, used to check if two textures are equal.
  403. /// </summary>
  404. /// <param name="info">Texture information to compare with</param>
  405. /// <param name="flags">Comparison flags</param>
  406. /// <returns>True if the textures are strictly equal or similar, false otherwise</returns>
  407. public bool IsPerfectMatch(TextureInfo info, TextureSearchFlags flags)
  408. {
  409. if (!FormatMatches(info, (flags & TextureSearchFlags.Strict) != 0))
  410. {
  411. return false;
  412. }
  413. if (!LayoutMatches(info))
  414. {
  415. return false;
  416. }
  417. if (!SizeMatches(info, (flags & TextureSearchFlags.Strict) == 0))
  418. {
  419. return false;
  420. }
  421. if ((flags & TextureSearchFlags.Sampler) != 0)
  422. {
  423. if (!SamplerParamsMatches(info))
  424. {
  425. return false;
  426. }
  427. }
  428. if ((flags & TextureSearchFlags.IgnoreMs) != 0)
  429. {
  430. bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;
  431. if (!msTargetCompatible && !TargetAndSamplesCompatible(info))
  432. {
  433. return false;
  434. }
  435. }
  436. else if (!TargetAndSamplesCompatible(info))
  437. {
  438. return false;
  439. }
  440. return Info.Address == info.Address && Info.Levels == info.Levels;
  441. }
  442. /// <summary>
  443. /// Checks if the texture format matches with the specified texture information.
  444. /// </summary>
  445. /// <param name="info">Texture information to compare with</param>
  446. /// <param name="strict">True to perform a strict comparison (formats must be exactly equal)</param>
  447. /// <returns>True if the format matches, with the given comparison rules</returns>
  448. private bool FormatMatches(TextureInfo info, bool strict)
  449. {
  450. // D32F and R32F texture have the same representation internally,
  451. // however the R32F format is used to sample from depth textures.
  452. if (Info.FormatInfo.Format == Format.D32Float && info.FormatInfo.Format == Format.R32Float && !strict)
  453. {
  454. return true;
  455. }
  456. return Info.FormatInfo.Format == info.FormatInfo.Format;
  457. }
  458. /// <summary>
  459. /// Checks if the texture layout specified matches with this texture layout.
  460. /// The layout information is composed of the Stride for linear textures, or GOB block size
  461. /// for block linear textures.
  462. /// </summary>
  463. /// <param name="info">Texture information to compare with</param>
  464. /// <returns>True if the layout matches, false otherwise</returns>
  465. private bool LayoutMatches(TextureInfo info)
  466. {
  467. if (Info.IsLinear != info.IsLinear)
  468. {
  469. return false;
  470. }
  471. // For linear textures, gob block sizes are ignored.
  472. // For block linear textures, the stride is ignored.
  473. if (info.IsLinear)
  474. {
  475. return Info.Stride == info.Stride;
  476. }
  477. else
  478. {
  479. return Info.GobBlocksInY == info.GobBlocksInY &&
  480. Info.GobBlocksInZ == info.GobBlocksInZ;
  481. }
  482. }
  483. /// <summary>
  484. /// Checks if the texture sizes of the supplied texture information matches this texture.
  485. /// </summary>
  486. /// <param name="info">Texture information to compare with</param>
  487. /// <returns>True if the size matches, false otherwise</returns>
  488. public bool SizeMatches(TextureInfo info)
  489. {
  490. return SizeMatches(info, alignSizes: false);
  491. }
  492. /// <summary>
  493. /// Checks if the texture sizes of the supplied texture information matches the given level of
  494. /// this texture.
  495. /// </summary>
  496. /// <param name="info">Texture information to compare with</param>
  497. /// <param name="level">Mipmap level of this texture to compare with</param>
  498. /// <returns>True if the size matches with the level, false otherwise</returns>
  499. public bool SizeMatches(TextureInfo info, int level)
  500. {
  501. return Math.Max(1, Info.Width >> level) == info.Width &&
  502. Math.Max(1, Info.Height >> level) == info.Height &&
  503. Math.Max(1, Info.GetDepth() >> level) == info.GetDepth();
  504. }
  505. /// <summary>
  506. /// Checks if the texture sizes of the supplied texture information matches this texture.
  507. /// </summary>
  508. /// <param name="info">Texture information to compare with</param>
  509. /// <param name="alignSizes">True to align the sizes according to the texture layout for comparison</param>
  510. /// <returns>True if the sizes matches, false otherwise</returns>
  511. private bool SizeMatches(TextureInfo info, bool alignSizes)
  512. {
  513. if (Info.GetLayers() != info.GetLayers())
  514. {
  515. return false;
  516. }
  517. if (alignSizes)
  518. {
  519. Size size0 = GetAlignedSize(Info);
  520. Size size1 = GetAlignedSize(info);
  521. return size0.Width == size1.Width &&
  522. size0.Height == size1.Height &&
  523. size0.Depth == size1.Depth;
  524. }
  525. else
  526. {
  527. return Info.Width == info.Width &&
  528. Info.Height == info.Height &&
  529. Info.GetDepth() == info.GetDepth();
  530. }
  531. }
  532. /// <summary>
  533. /// Checks if the texture shader sampling parameters matches.
  534. /// </summary>
  535. /// <param name="info">Texture information to compare with</param>
  536. /// <returns>True if the texture shader sampling parameters matches, false otherwise</returns>
  537. private bool SamplerParamsMatches(TextureInfo info)
  538. {
  539. return Info.DepthStencilMode == info.DepthStencilMode &&
  540. Info.SwizzleR == info.SwizzleR &&
  541. Info.SwizzleG == info.SwizzleG &&
  542. Info.SwizzleB == info.SwizzleB &&
  543. Info.SwizzleA == info.SwizzleA;
  544. }
  545. /// <summary>
  546. /// Check if the texture target and samples count (for multisampled textures) matches.
  547. /// </summary>
  548. /// <param name="info">Texture information to compare with</param>
  549. /// <returns>True if the texture target and samples count matches, false otherwise</returns>
  550. private bool TargetAndSamplesCompatible(TextureInfo info)
  551. {
  552. return Info.Target == info.Target &&
  553. Info.SamplesInX == info.SamplesInX &&
  554. Info.SamplesInY == info.SamplesInY;
  555. }
  556. /// <summary>
  557. /// Check if it's possible to create a view, with the given parameters, from this texture.
  558. /// </summary>
  559. /// <param name="info">Texture view information</param>
  560. /// <param name="size">Texture view size</param>
  561. /// <param name="firstLayer">Texture view initial layer on this texture</param>
  562. /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
  563. /// <returns>True if a view with the given parameters can be created from this texture, false otherwise</returns>
  564. public bool IsViewCompatible(
  565. TextureInfo info,
  566. ulong size,
  567. out int firstLayer,
  568. out int firstLevel)
  569. {
  570. return IsViewCompatible(info, size, isCopy: false, out firstLayer, out firstLevel);
  571. }
  572. /// <summary>
  573. /// Check if it's possible to create a view, with the given parameters, from this texture.
  574. /// </summary>
  575. /// <param name="info">Texture view information</param>
  576. /// <param name="size">Texture view size</param>
  577. /// <param name="isCopy">True to check for copy compability, instead of view compatibility</param>
  578. /// <param name="firstLayer">Texture view initial layer on this texture</param>
  579. /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
  580. /// <returns>True if a view with the given parameters can be created from this texture, false otherwise</returns>
  581. public bool IsViewCompatible(
  582. TextureInfo info,
  583. ulong size,
  584. bool isCopy,
  585. out int firstLayer,
  586. out int firstLevel)
  587. {
  588. // Out of range.
  589. if (info.Address < Address || info.Address + size > EndAddress)
  590. {
  591. firstLayer = 0;
  592. firstLevel = 0;
  593. return false;
  594. }
  595. int offset = (int)(info.Address - Address);
  596. if (!_sizeInfo.FindView(offset, (int)size, out firstLayer, out firstLevel))
  597. {
  598. return false;
  599. }
  600. if (!ViewLayoutCompatible(info, firstLevel))
  601. {
  602. return false;
  603. }
  604. if (!ViewFormatCompatible(info))
  605. {
  606. return false;
  607. }
  608. if (!ViewSizeMatches(info, firstLevel, isCopy))
  609. {
  610. return false;
  611. }
  612. if (!ViewTargetCompatible(info, isCopy))
  613. {
  614. return false;
  615. }
  616. return Info.SamplesInX == info.SamplesInX &&
  617. Info.SamplesInY == info.SamplesInY;
  618. }
  619. /// <summary>
  620. /// Check if it's possible to create a view with the specified layout.
  621. /// The layout information is composed of the Stride for linear textures, or GOB block size
  622. /// for block linear textures.
  623. /// </summary>
  624. /// <param name="info">Texture information of the texture view</param>
  625. /// <param name="level">Start level of the texture view, in relation with this texture</param>
  626. /// <returns>True if the layout is compatible, false otherwise</returns>
  627. private bool ViewLayoutCompatible(TextureInfo info, int level)
  628. {
  629. if (Info.IsLinear != info.IsLinear)
  630. {
  631. return false;
  632. }
  633. // For linear textures, gob block sizes are ignored.
  634. // For block linear textures, the stride is ignored.
  635. if (info.IsLinear)
  636. {
  637. int width = Math.Max(1, Info.Width >> level);
  638. int stride = width * Info.FormatInfo.BytesPerPixel;
  639. stride = BitUtils.AlignUp(stride, 32);
  640. return stride == info.Stride;
  641. }
  642. else
  643. {
  644. int height = Math.Max(1, Info.Height >> level);
  645. int depth = Math.Max(1, Info.GetDepth() >> level);
  646. (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
  647. height,
  648. depth,
  649. Info.FormatInfo.BlockHeight,
  650. Info.GobBlocksInY,
  651. Info.GobBlocksInZ);
  652. return gobBlocksInY == info.GobBlocksInY &&
  653. gobBlocksInZ == info.GobBlocksInZ;
  654. }
  655. }
  656. /// <summary>
  657. /// Checks if the view format is compatible with this texture format.
  658. /// In general, the formats are considered compatible if the bytes per pixel values are equal,
  659. /// but there are more complex rules for some formats, like compressed or depth-stencil formats.
  660. /// This follows the host API copy compatibility rules.
  661. /// </summary>
  662. /// <param name="info">Texture information of the texture view</param>
  663. /// <returns>True if the formats are compatible, false otherwise</returns>
  664. private bool ViewFormatCompatible(TextureInfo info)
  665. {
  666. return TextureCompatibility.FormatCompatible(Info.FormatInfo, info.FormatInfo);
  667. }
  668. /// <summary>
  669. /// Checks if the size of a given texture view is compatible with this texture.
  670. /// </summary>
  671. /// <param name="info">Texture information of the texture view</param>
  672. /// <param name="level">Mipmap level of the texture view in relation to this texture</param>
  673. /// <param name="isCopy">True to check for copy compatibility rather than view compatibility</param>
  674. /// <returns>True if the sizes are compatible, false otherwise</returns>
  675. private bool ViewSizeMatches(TextureInfo info, int level, bool isCopy)
  676. {
  677. Size size = GetAlignedSize(Info, level);
  678. Size otherSize = GetAlignedSize(info);
  679. // For copies, we can copy a subset of the 3D texture slices,
  680. // so the depth may be different in this case.
  681. if (!isCopy && info.Target == Target.Texture3D && size.Depth != otherSize.Depth)
  682. {
  683. return false;
  684. }
  685. return size.Width == otherSize.Width &&
  686. size.Height == otherSize.Height;
  687. }
  688. /// <summary>
  689. /// Check if the target of the specified texture view information is compatible with this
  690. /// texture.
  691. /// This follows the host API target compatibility rules.
  692. /// </summary>
  693. /// <param name="info">Texture information of the texture view</param>
  694. /// <param name="isCopy">True to check for copy rather than view compatibility</param>
  695. /// <returns>True if the targets are compatible, false otherwise</returns>
  696. private bool ViewTargetCompatible(TextureInfo info, bool isCopy)
  697. {
  698. switch (Info.Target)
  699. {
  700. case Target.Texture1D:
  701. case Target.Texture1DArray:
  702. return info.Target == Target.Texture1D ||
  703. info.Target == Target.Texture1DArray;
  704. case Target.Texture2D:
  705. return info.Target == Target.Texture2D ||
  706. info.Target == Target.Texture2DArray;
  707. case Target.Texture2DArray:
  708. case Target.Cubemap:
  709. case Target.CubemapArray:
  710. return info.Target == Target.Texture2D ||
  711. info.Target == Target.Texture2DArray ||
  712. info.Target == Target.Cubemap ||
  713. info.Target == Target.CubemapArray;
  714. case Target.Texture2DMultisample:
  715. case Target.Texture2DMultisampleArray:
  716. return info.Target == Target.Texture2DMultisample ||
  717. info.Target == Target.Texture2DMultisampleArray;
  718. case Target.Texture3D:
  719. return info.Target == Target.Texture3D ||
  720. (info.Target == Target.Texture2D && isCopy);
  721. }
  722. return false;
  723. }
  724. /// <summary>
  725. /// Gets the aligned sizes of the specified texture information.
  726. /// The alignment depends on the texture layout and format bytes per pixel.
  727. /// </summary>
  728. /// <param name="info">Texture information to calculate the aligned size from</param>
  729. /// <param name="level">Mipmap level for texture views</param>
  730. /// <returns>The aligned texture size</returns>
  731. private static Size GetAlignedSize(TextureInfo info, int level = 0)
  732. {
  733. int width = Math.Max(1, info.Width >> level);
  734. int height = Math.Max(1, info.Height >> level);
  735. if (info.IsLinear)
  736. {
  737. return SizeCalculator.GetLinearAlignedSize(
  738. width,
  739. height,
  740. info.FormatInfo.BlockWidth,
  741. info.FormatInfo.BlockHeight,
  742. info.FormatInfo.BytesPerPixel);
  743. }
  744. else
  745. {
  746. int depth = Math.Max(1, info.GetDepth() >> level);
  747. return SizeCalculator.GetBlockLinearAlignedSize(
  748. width,
  749. height,
  750. depth,
  751. info.FormatInfo.BlockWidth,
  752. info.FormatInfo.BlockHeight,
  753. info.FormatInfo.BytesPerPixel,
  754. info.GobBlocksInY,
  755. info.GobBlocksInZ,
  756. info.GobBlocksInTileX);
  757. }
  758. }
  759. /// <summary>
  760. /// Gets a texture of the specified target type from this texture.
  761. /// This can be used to get an array texture from a non-array texture and vice-versa.
  762. /// If this texture and the requested targets are equal, then this texture Host texture is returned directly.
  763. /// </summary>
  764. /// <param name="target">The desired target type</param>
  765. /// <returns>A view of this texture with the requested target, or null if the target is invalid for this texture</returns>
  766. public ITexture GetTargetTexture(Target target)
  767. {
  768. if (target == Info.Target)
  769. {
  770. return HostTexture;
  771. }
  772. if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
  773. {
  774. TextureCreateInfo createInfo = new TextureCreateInfo(
  775. Info.Width,
  776. Info.Height,
  777. target == Target.CubemapArray ? 6 : 1,
  778. Info.Levels,
  779. Info.Samples,
  780. Info.FormatInfo.BlockWidth,
  781. Info.FormatInfo.BlockHeight,
  782. Info.FormatInfo.BytesPerPixel,
  783. Info.FormatInfo.Format,
  784. Info.DepthStencilMode,
  785. target,
  786. Info.SwizzleR,
  787. Info.SwizzleG,
  788. Info.SwizzleB,
  789. Info.SwizzleA);
  790. ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0);
  791. _arrayViewTexture = viewTexture;
  792. _arrayViewTarget = target;
  793. return viewTexture;
  794. }
  795. else if (_arrayViewTarget == target)
  796. {
  797. return _arrayViewTexture;
  798. }
  799. return null;
  800. }
  801. /// <summary>
  802. /// Check if this texture and the specified target have the same number of dimensions.
  803. /// For the purposes of this comparison, 2D and 2D Multisample textures are not considered to have
  804. /// the same number of dimensions. Same for Cubemap and 3D textures.
  805. /// </summary>
  806. /// <param name="target">The target to compare with</param>
  807. /// <returns>True if both targets have the same number of dimensions, false otherwise</returns>
  808. private bool IsSameDimensionsTarget(Target target)
  809. {
  810. switch (Info.Target)
  811. {
  812. case Target.Texture1D:
  813. case Target.Texture1DArray:
  814. return target == Target.Texture1D ||
  815. target == Target.Texture1DArray;
  816. case Target.Texture2D:
  817. case Target.Texture2DArray:
  818. return target == Target.Texture2D ||
  819. target == Target.Texture2DArray;
  820. case Target.Cubemap:
  821. case Target.CubemapArray:
  822. return target == Target.Cubemap ||
  823. target == Target.CubemapArray;
  824. case Target.Texture2DMultisample:
  825. case Target.Texture2DMultisampleArray:
  826. return target == Target.Texture2DMultisample ||
  827. target == Target.Texture2DMultisampleArray;
  828. case Target.Texture3D:
  829. return target == Target.Texture3D;
  830. }
  831. return false;
  832. }
  833. /// <summary>
  834. /// Replaces view texture information.
  835. /// This should only be used for child textures with a parent.
  836. /// </summary>
  837. /// <param name="parent">The parent texture</param>
  838. /// <param name="info">The new view texture information</param>
  839. /// <param name="hostTexture">The new host texture</param>
  840. public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture)
  841. {
  842. ReplaceStorage(hostTexture);
  843. parent._viewStorage.AddView(this);
  844. SetInfo(info);
  845. }
  846. /// <summary>
  847. /// Sets the internal texture information structure.
  848. /// </summary>
  849. /// <param name="info">The new texture information</param>
  850. private void SetInfo(TextureInfo info)
  851. {
  852. Info = info;
  853. _depth = info.GetDepth();
  854. _layers = info.GetLayers();
  855. }
  856. /// <summary>
  857. /// Signals that the texture has been modified.
  858. /// </summary>
  859. public void SignalModified()
  860. {
  861. Modified?.Invoke(this);
  862. }
  863. /// <summary>
  864. /// Replaces the host texture, while disposing of the old one if needed.
  865. /// </summary>
  866. /// <param name="hostTexture">The new host texture</param>
  867. private void ReplaceStorage(ITexture hostTexture)
  868. {
  869. DisposeTextures();
  870. HostTexture = hostTexture;
  871. }
  872. /// <summary>
  873. /// Checks if the texture overlaps with a memory range.
  874. /// </summary>
  875. /// <param name="address">Start address of the range</param>
  876. /// <param name="size">Size of the range</param>
  877. /// <returns>True if the texture overlaps with the range, false otherwise</returns>
  878. public bool OverlapsWith(ulong address, ulong size)
  879. {
  880. return Address < address + size && address < EndAddress;
  881. }
  882. /// <summary>
  883. /// Increments the texture reference count.
  884. /// </summary>
  885. public void IncrementReferenceCount()
  886. {
  887. _referenceCount++;
  888. }
  889. /// <summary>
  890. /// Decrements the texture reference count.
  891. /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
  892. /// </summary>
  893. public void DecrementReferenceCount()
  894. {
  895. int newRefCount = --_referenceCount;
  896. if (newRefCount == 0)
  897. {
  898. if (_viewStorage != this)
  899. {
  900. _viewStorage.RemoveView(this);
  901. }
  902. _context.Methods.TextureManager.RemoveTextureFromCache(this);
  903. }
  904. Debug.Assert(newRefCount >= 0);
  905. DeleteIfNotUsed();
  906. }
  907. /// <summary>
  908. /// Delete the texture if it is not used anymore.
  909. /// The texture is considered unused when the reference count is zero,
  910. /// and it has no child views.
  911. /// </summary>
  912. private void DeleteIfNotUsed()
  913. {
  914. // We can delete the texture as long it is not being used
  915. // in any cache (the reference count is 0 in this case), and
  916. // also all views that may be created from this texture were
  917. // already deleted (views count is 0).
  918. if (_referenceCount == 0 && _views.Count == 0)
  919. {
  920. DisposeTextures();
  921. }
  922. }
  923. /// <summary>
  924. /// Performs texture disposal, deleting the texture.
  925. /// </summary>
  926. private void DisposeTextures()
  927. {
  928. HostTexture.Dispose();
  929. _arrayViewTexture?.Dispose();
  930. _arrayViewTexture = null;
  931. Disposed?.Invoke(this);
  932. }
  933. /// <summary>
  934. /// Performs texture disposal, deleting the texture.
  935. /// </summary>
  936. public void Dispose()
  937. {
  938. DisposeTextures();
  939. }
  940. }
  941. }