MethodCopyBuffer.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using Ryujinx.Common;
  2. using Ryujinx.Graphics.Gpu.State;
  3. using Ryujinx.Graphics.Texture;
  4. using System;
  5. namespace Ryujinx.Graphics.Gpu.Engine
  6. {
  7. partial class Methods
  8. {
  9. private const int StrideAlignment = 32;
  10. private const int GobAlignment = 64;
  11. /// <summary>
  12. /// Determine if a buffer-to-texture region covers the entirety of a texture.
  13. /// </summary>
  14. /// <param name="cbp">Copy command parameters</param>
  15. /// <param name="tex">Texture to compare</param>
  16. /// <param name="linear">True if the texture is linear, false if block linear</param>
  17. /// <param name="stride">Texture stride</param>
  18. /// <returns></returns>
  19. private bool IsTextureCopyComplete(CopyBufferParams cbp, CopyBufferTexture tex, bool linear, int stride)
  20. {
  21. if (linear)
  22. {
  23. return tex.RegionX == 0 &&
  24. tex.RegionY == 0 &&
  25. stride == BitUtils.AlignUp(cbp.XCount, StrideAlignment);
  26. }
  27. else
  28. {
  29. return tex.RegionX == 0 &&
  30. tex.RegionY == 0 &&
  31. tex.Width == BitUtils.AlignUp(cbp.XCount, GobAlignment) &&
  32. tex.Height == cbp.YCount;
  33. }
  34. }
  35. /// <summary>
  36. /// Performs a buffer to buffer, or buffer to texture copy.
  37. /// </summary>
  38. /// <param name="state">Current GPU state</param>
  39. /// <param name="argument">Method call argument</param>
  40. private void CopyBuffer(GpuState state, int argument)
  41. {
  42. var cbp = state.Get<CopyBufferParams>(MethodOffset.CopyBufferParams);
  43. var swizzle = state.Get<CopyBufferSwizzle>(MethodOffset.CopyBufferSwizzle);
  44. bool srcLinear = (argument & (1 << 7)) != 0;
  45. bool dstLinear = (argument & (1 << 8)) != 0;
  46. bool copy2D = (argument & (1 << 9)) != 0;
  47. int size = cbp.XCount;
  48. if (size == 0)
  49. {
  50. return;
  51. }
  52. if (copy2D)
  53. {
  54. // Buffer to texture copy.
  55. var dst = state.Get<CopyBufferTexture>(MethodOffset.CopyBufferDstTexture);
  56. var src = state.Get<CopyBufferTexture>(MethodOffset.CopyBufferSrcTexture);
  57. var srcCalculator = new OffsetCalculator(
  58. src.Width,
  59. src.Height,
  60. cbp.SrcStride,
  61. srcLinear,
  62. src.MemoryLayout.UnpackGobBlocksInY(),
  63. src.MemoryLayout.UnpackGobBlocksInZ(),
  64. 1);
  65. var dstCalculator = new OffsetCalculator(
  66. dst.Width,
  67. dst.Height,
  68. cbp.DstStride,
  69. dstLinear,
  70. dst.MemoryLayout.UnpackGobBlocksInY(),
  71. dst.MemoryLayout.UnpackGobBlocksInZ(),
  72. 1);
  73. ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack());
  74. ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack());
  75. (int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, cbp.XCount, cbp.YCount);
  76. (int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, cbp.XCount, cbp.YCount);
  77. ReadOnlySpan<byte> srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true);
  78. Span<byte> dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray();
  79. bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, cbp.SrcStride);
  80. bool completeDest = IsTextureCopyComplete(cbp, dst, dstLinear, cbp.DstStride);
  81. if (completeSource && completeDest)
  82. {
  83. Image.Texture target = TextureManager.FindTexture(dst, cbp, swizzle, dstLinear);
  84. if (target != null)
  85. {
  86. ReadOnlySpan<byte> data;
  87. if (srcLinear)
  88. {
  89. data = LayoutConverter.ConvertLinearStridedToLinear(
  90. target.Info.Width,
  91. target.Info.Height,
  92. 1,
  93. 1,
  94. cbp.SrcStride,
  95. target.Info.FormatInfo.BytesPerPixel,
  96. srcSpan);
  97. }
  98. else
  99. {
  100. data = LayoutConverter.ConvertBlockLinearToLinear(
  101. src.Width,
  102. src.Height,
  103. 1,
  104. target.Info.Levels,
  105. 1,
  106. 1,
  107. 1,
  108. 1,
  109. src.MemoryLayout.UnpackGobBlocksInY(),
  110. src.MemoryLayout.UnpackGobBlocksInZ(),
  111. 1,
  112. new SizeInfo((int)target.Size),
  113. srcSpan);
  114. }
  115. target.SetData(data);
  116. target.SignalModified();
  117. return;
  118. }
  119. else if (srcCalculator.LayoutMatches(dstCalculator))
  120. {
  121. srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely.
  122. _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
  123. return;
  124. }
  125. }
  126. unsafe bool Convert<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan) where T : unmanaged
  127. {
  128. fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan)
  129. {
  130. byte* dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset.
  131. byte* srcBase = srcPtr - srcBaseOffset;
  132. for (int y = 0; y < cbp.YCount; y++)
  133. {
  134. srcCalculator.SetY(src.RegionY + y);
  135. dstCalculator.SetY(dst.RegionY + y);
  136. for (int x = 0; x < cbp.XCount; x++)
  137. {
  138. int srcOffset = srcCalculator.GetOffset(src.RegionX + x);
  139. int dstOffset = dstCalculator.GetOffset(dst.RegionX + x);
  140. *(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset);
  141. }
  142. }
  143. }
  144. return true;
  145. }
  146. Convert<byte>(dstSpan, srcSpan);
  147. _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
  148. }
  149. else
  150. {
  151. // Buffer to buffer copy.
  152. BufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
  153. }
  154. }
  155. }
  156. }