TextureManager.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. using Ryujinx.Common;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.GAL.Texture;
  4. using Ryujinx.Graphics.Gpu.Image;
  5. using Ryujinx.Graphics.Gpu.Memory;
  6. using Ryujinx.Graphics.Gpu.State;
  7. using Ryujinx.Graphics.Texture;
  8. using System;
  9. namespace Ryujinx.Graphics.Gpu.Image
  10. {
  11. class TextureManager
  12. {
  13. private const int OverlapsBufferInitialCapacity = 10;
  14. private const int OverlapsBufferMaxCapacity = 10000;
  15. private GpuContext _context;
  16. private TextureBindingsManager _cpBindingsManager;
  17. private TextureBindingsManager _gpBindingsManager;
  18. private Texture[] _rtColors;
  19. private Texture _rtDepthStencil;
  20. private ITexture[] _rtHostColors;
  21. private ITexture _rtHostDs;
  22. private RangeList<Texture> _textures;
  23. private Texture[] _textureOverlaps;
  24. private AutoDeleteCache _cache;
  25. public TextureManager(GpuContext context)
  26. {
  27. _context = context;
  28. TexturePoolCache texturePoolCache = new TexturePoolCache(context);
  29. _cpBindingsManager = new TextureBindingsManager(context, texturePoolCache, isCompute: true);
  30. _gpBindingsManager = new TextureBindingsManager(context, texturePoolCache, isCompute: false);
  31. _rtColors = new Texture[Constants.TotalRenderTargets];
  32. _rtHostColors = new ITexture[Constants.TotalRenderTargets];
  33. _textures = new RangeList<Texture>();
  34. _textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
  35. _cache = new AutoDeleteCache();
  36. }
  37. public void SetComputeTextures(TextureBindingInfo[] bindings)
  38. {
  39. _cpBindingsManager.SetTextures(0, bindings);
  40. }
  41. public void SetGraphicsTextures(int stage, TextureBindingInfo[] bindings)
  42. {
  43. _gpBindingsManager.SetTextures(stage, bindings);
  44. }
  45. public void SetComputeImages(TextureBindingInfo[] bindings)
  46. {
  47. _cpBindingsManager.SetImages(0, bindings);
  48. }
  49. public void SetGraphicsImages(int stage, TextureBindingInfo[] bindings)
  50. {
  51. _gpBindingsManager.SetImages(stage, bindings);
  52. }
  53. public void SetComputeTextureBufferIndex(int index)
  54. {
  55. _cpBindingsManager.SetTextureBufferIndex(index);
  56. }
  57. public void SetGraphicsTextureBufferIndex(int index)
  58. {
  59. _gpBindingsManager.SetTextureBufferIndex(index);
  60. }
  61. public void SetComputeSamplerPool(ulong gpuVa, int maximumId)
  62. {
  63. _cpBindingsManager.SetSamplerPool(gpuVa, maximumId);
  64. }
  65. public void SetGraphicsSamplerPool(ulong gpuVa, int maximumId)
  66. {
  67. _gpBindingsManager.SetSamplerPool(gpuVa, maximumId);
  68. }
  69. public void SetComputeTexturePool(ulong gpuVa, int maximumId)
  70. {
  71. _cpBindingsManager.SetTexturePool(gpuVa, maximumId);
  72. }
  73. public void SetGraphicsTexturePool(ulong gpuVa, int maximumId)
  74. {
  75. _gpBindingsManager.SetTexturePool(gpuVa, maximumId);
  76. }
  77. public void SetRenderTargetColor(int index, Texture color)
  78. {
  79. _rtColors[index] = color;
  80. }
  81. public void SetRenderTargetDepthStencil(Texture depthStencil)
  82. {
  83. _rtDepthStencil = depthStencil;
  84. }
  85. public void CommitComputeBindings()
  86. {
  87. // Every time we switch between graphics and compute work,
  88. // we must rebind everything.
  89. // Since compute work happens less often, we always do that
  90. // before and after the compute dispatch.
  91. _cpBindingsManager.Rebind();
  92. _cpBindingsManager.CommitBindings();
  93. _gpBindingsManager.Rebind();
  94. }
  95. public void CommitGraphicsBindings()
  96. {
  97. _gpBindingsManager.CommitBindings();
  98. UpdateRenderTargets();
  99. }
  100. private void UpdateRenderTargets()
  101. {
  102. bool anyChanged = false;
  103. if (_rtHostDs != _rtDepthStencil?.HostTexture)
  104. {
  105. _rtHostDs = _rtDepthStencil?.HostTexture;
  106. anyChanged = true;
  107. }
  108. for (int index = 0; index < _rtColors.Length; index++)
  109. {
  110. ITexture hostTexture = _rtColors[index]?.HostTexture;
  111. if (_rtHostColors[index] != hostTexture)
  112. {
  113. _rtHostColors[index] = hostTexture;
  114. anyChanged = true;
  115. }
  116. }
  117. if (anyChanged)
  118. {
  119. _context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
  120. }
  121. }
  122. public Texture FindOrCreateTexture(CopyTexture copyTexture)
  123. {
  124. ulong address = _context.MemoryManager.Translate(copyTexture.Address.Pack());
  125. if (address == MemoryManager.BadAddress)
  126. {
  127. return null;
  128. }
  129. int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
  130. int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
  131. FormatInfo formatInfo = copyTexture.Format.Convert();
  132. int width;
  133. if (copyTexture.LinearLayout)
  134. {
  135. width = copyTexture.Stride / formatInfo.BytesPerPixel;
  136. }
  137. else
  138. {
  139. width = copyTexture.Width;
  140. }
  141. TextureInfo info = new TextureInfo(
  142. address,
  143. width,
  144. copyTexture.Height,
  145. copyTexture.Depth,
  146. 1,
  147. 1,
  148. 1,
  149. copyTexture.Stride,
  150. copyTexture.LinearLayout,
  151. gobBlocksInY,
  152. gobBlocksInZ,
  153. 1,
  154. Target.Texture2D,
  155. formatInfo);
  156. Texture texture = FindOrCreateTexture(info, TextureSearchFlags.IgnoreMs);
  157. texture.SynchronizeMemory();
  158. return texture;
  159. }
  160. public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY)
  161. {
  162. ulong address = _context.MemoryManager.Translate(colorState.Address.Pack());
  163. if (address == MemoryManager.BadAddress)
  164. {
  165. return null;
  166. }
  167. bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
  168. int gobBlocksInY = colorState.MemoryLayout.UnpackGobBlocksInY();
  169. int gobBlocksInZ = colorState.MemoryLayout.UnpackGobBlocksInZ();
  170. Target target;
  171. if (colorState.MemoryLayout.UnpackIsTarget3D())
  172. {
  173. target = Target.Texture3D;
  174. }
  175. else if ((samplesInX | samplesInY) != 1)
  176. {
  177. target = colorState.Depth > 1
  178. ? Target.Texture2DMultisampleArray
  179. : Target.Texture2DMultisample;
  180. }
  181. else
  182. {
  183. target = colorState.Depth > 1
  184. ? Target.Texture2DArray
  185. : Target.Texture2D;
  186. }
  187. FormatInfo formatInfo = colorState.Format.Convert();
  188. int width, stride;
  189. // For linear textures, the width value is actually the stride.
  190. // We can easily get the width by dividing the stride by the bpp,
  191. // since the stride is the total number of bytes occupied by a
  192. // line. The stride should also meet alignment constraints however,
  193. // so the width we get here is the aligned width.
  194. if (isLinear)
  195. {
  196. width = colorState.WidthOrStride / formatInfo.BytesPerPixel;
  197. stride = colorState.WidthOrStride;
  198. }
  199. else
  200. {
  201. width = colorState.WidthOrStride;
  202. stride = 0;
  203. }
  204. TextureInfo info = new TextureInfo(
  205. address,
  206. width,
  207. colorState.Height,
  208. colorState.Depth,
  209. 1,
  210. samplesInX,
  211. samplesInY,
  212. stride,
  213. isLinear,
  214. gobBlocksInY,
  215. gobBlocksInZ,
  216. 1,
  217. target,
  218. formatInfo);
  219. Texture texture = FindOrCreateTexture(info);
  220. texture.SynchronizeMemory();
  221. return texture;
  222. }
  223. public Texture FindOrCreateTexture(RtDepthStencilState dsState, Size3D size, int samplesInX, int samplesInY)
  224. {
  225. ulong address = _context.MemoryManager.Translate(dsState.Address.Pack());
  226. if (address == MemoryManager.BadAddress)
  227. {
  228. return null;
  229. }
  230. int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
  231. int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
  232. Target target = (samplesInX | samplesInY) != 1
  233. ? Target.Texture2DMultisample
  234. : Target.Texture2D;
  235. FormatInfo formatInfo = dsState.Format.Convert();
  236. TextureInfo info = new TextureInfo(
  237. address,
  238. size.Width,
  239. size.Height,
  240. size.Depth,
  241. 1,
  242. samplesInX,
  243. samplesInY,
  244. 0,
  245. false,
  246. gobBlocksInY,
  247. gobBlocksInZ,
  248. 1,
  249. target,
  250. formatInfo);
  251. Texture texture = FindOrCreateTexture(info);
  252. texture.SynchronizeMemory();
  253. return texture;
  254. }
  255. public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None)
  256. {
  257. bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0;
  258. // Try to find a perfect texture match, with the same address and parameters.
  259. int sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps);
  260. for (int index = 0; index < sameAddressOverlapsCount; index++)
  261. {
  262. Texture overlap = _textureOverlaps[index];
  263. if (overlap.IsPerfectMatch(info, flags))
  264. {
  265. if (!isSamplerTexture)
  266. {
  267. // If not a sampler texture, it is managed by the auto delete
  268. // cache, ensure that it is on the "top" of the list to avoid
  269. // deletion.
  270. _cache.Lift(overlap);
  271. }
  272. else if (!overlap.SizeMatches(info))
  273. {
  274. // If this is used for sampling, the size must match,
  275. // otherwise the shader would sample garbage data.
  276. // To fix that, we create a new texture with the correct
  277. // size, and copy the data from the old one to the new one.
  278. overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
  279. }
  280. return overlap;
  281. }
  282. }
  283. // Calculate texture sizes, used to find all overlapping textures.
  284. SizeInfo sizeInfo;
  285. if (info.IsLinear)
  286. {
  287. sizeInfo = SizeCalculator.GetLinearTextureSize(
  288. info.Stride,
  289. info.Height,
  290. info.FormatInfo.BlockHeight);
  291. }
  292. else
  293. {
  294. sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
  295. info.Width,
  296. info.Height,
  297. info.GetDepth(),
  298. info.Levels,
  299. info.GetLayers(),
  300. info.FormatInfo.BlockWidth,
  301. info.FormatInfo.BlockHeight,
  302. info.FormatInfo.BytesPerPixel,
  303. info.GobBlocksInY,
  304. info.GobBlocksInZ,
  305. info.GobBlocksInTileX);
  306. }
  307. // Find view compatible matches.
  308. ulong size = (ulong)sizeInfo.TotalSize;
  309. int overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps);
  310. Texture texture = null;
  311. for (int index = 0; index < overlapsCount; index++)
  312. {
  313. Texture overlap = _textureOverlaps[index];
  314. if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel))
  315. {
  316. if (!isSamplerTexture)
  317. {
  318. info = AdjustSizes(overlap, info, firstLevel);
  319. }
  320. texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);
  321. // The size only matters (and is only really reliable) when the
  322. // texture is used on a sampler, because otherwise the size will be
  323. // aligned.
  324. if (!overlap.SizeMatches(info, firstLevel) && isSamplerTexture)
  325. {
  326. texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
  327. }
  328. break;
  329. }
  330. }
  331. // No match, create a new texture.
  332. if (texture == null)
  333. {
  334. texture = new Texture(_context, info, sizeInfo);
  335. // We need to synchronize before copying the old view data to the texture,
  336. // otherwise the copied data would be overwritten by a future synchronization.
  337. texture.SynchronizeMemory();
  338. for (int index = 0; index < overlapsCount; index++)
  339. {
  340. Texture overlap = _textureOverlaps[index];
  341. if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel))
  342. {
  343. TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel);
  344. TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities);
  345. ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel);
  346. overlap.HostTexture.CopyTo(newView, 0, 0);
  347. overlap.ReplaceView(texture, overlapInfo, newView);
  348. }
  349. }
  350. // If the texture is a 3D texture, we need to additionally copy any slice
  351. // of the 3D texture to the newly created 3D texture.
  352. if (info.Target == Target.Texture3D)
  353. {
  354. for (int index = 0; index < overlapsCount; index++)
  355. {
  356. Texture overlap = _textureOverlaps[index];
  357. if (texture.IsViewCompatible(
  358. overlap.Info,
  359. overlap.Size,
  360. isCopy: true,
  361. out int firstLayer,
  362. out int firstLevel))
  363. {
  364. overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel);
  365. }
  366. }
  367. }
  368. }
  369. // Sampler textures are managed by the texture pool, all other textures
  370. // are managed by the auto delete cache.
  371. if (!isSamplerTexture)
  372. {
  373. _cache.Add(texture);
  374. }
  375. _textures.Add(texture);
  376. ShrinkOverlapsBufferIfNeeded();
  377. return texture;
  378. }
  379. private void ShrinkOverlapsBufferIfNeeded()
  380. {
  381. if (_textureOverlaps.Length > OverlapsBufferMaxCapacity)
  382. {
  383. Array.Resize(ref _textureOverlaps, OverlapsBufferMaxCapacity);
  384. }
  385. }
  386. private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
  387. {
  388. // When the texture is used as view of another texture, we must
  389. // ensure that the sizes are valid, otherwise data uploads would fail
  390. // (and the size wouldn't match the real size used on the host API).
  391. // Given a parent texture from where the view is created, we have the
  392. // following rules:
  393. // - The view size must be equal to the parent size, divided by (2 ^ l),
  394. // where l is the first mipmap level of the view. The division result must
  395. // be rounded down, and the result must be clamped to 1.
  396. // - If the parent format is compressed, and the view format isn't, the
  397. // view size is calculated as above, but the width and height of the
  398. // view must be also divided by the compressed format block width and height.
  399. // - If the parent format is not compressed, and the view is, the view
  400. // size is calculated as described on the first point, but the width and height
  401. // of the view must be also multiplied by the block width and height.
  402. int width = Math.Max(1, parent.Info.Width >> firstLevel);
  403. int height = Math.Max(1, parent.Info.Height >> firstLevel);
  404. if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed)
  405. {
  406. width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
  407. height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
  408. }
  409. else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed)
  410. {
  411. width *= info.FormatInfo.BlockWidth;
  412. height *= info.FormatInfo.BlockHeight;
  413. }
  414. int depthOrLayers;
  415. if (info.Target == Target.Texture3D)
  416. {
  417. depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
  418. }
  419. else
  420. {
  421. depthOrLayers = info.DepthOrLayers;
  422. }
  423. return new TextureInfo(
  424. info.Address,
  425. width,
  426. height,
  427. depthOrLayers,
  428. info.Levels,
  429. info.SamplesInX,
  430. info.SamplesInY,
  431. info.Stride,
  432. info.IsLinear,
  433. info.GobBlocksInY,
  434. info.GobBlocksInZ,
  435. info.GobBlocksInTileX,
  436. info.Target,
  437. info.FormatInfo,
  438. info.DepthStencilMode,
  439. info.SwizzleR,
  440. info.SwizzleG,
  441. info.SwizzleB,
  442. info.SwizzleA);
  443. }
  444. public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps)
  445. {
  446. FormatInfo formatInfo = info.FormatInfo;
  447. if (!caps.SupportsAstcCompression)
  448. {
  449. if (formatInfo.Format.IsAstcUnorm())
  450. {
  451. formatInfo = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
  452. }
  453. else if (formatInfo.Format.IsAstcSrgb())
  454. {
  455. formatInfo = new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4);
  456. }
  457. }
  458. int width = info.Width / info.SamplesInX;
  459. int height = info.Height / info.SamplesInY;
  460. int depth = info.GetDepth() * info.GetLayers();
  461. return new TextureCreateInfo(
  462. width,
  463. height,
  464. depth,
  465. info.Levels,
  466. info.Samples,
  467. formatInfo.BlockWidth,
  468. formatInfo.BlockHeight,
  469. formatInfo.BytesPerPixel,
  470. formatInfo.Format,
  471. info.DepthStencilMode,
  472. info.Target,
  473. info.SwizzleR,
  474. info.SwizzleG,
  475. info.SwizzleB,
  476. info.SwizzleA);
  477. }
  478. public void Flush()
  479. {
  480. foreach (Texture texture in _cache)
  481. {
  482. if (texture.Info.IsLinear && texture.Modified)
  483. {
  484. texture.Flush();
  485. texture.Modified = false;
  486. }
  487. }
  488. }
  489. public void RemoveTextureFromCache(Texture texture)
  490. {
  491. _textures.Remove(texture);
  492. }
  493. }
  494. }