TextureManager.cs 20 KB

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