TextureManager.cs 22 KB

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