TextureCopyIncompatible.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. using OpenTK.Graphics.OpenGL;
  2. using Ryujinx.Graphics.GAL;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.Numerics;
  7. namespace Ryujinx.Graphics.OpenGL.Image
  8. {
  9. class TextureCopyIncompatible
  10. {
  11. private const string ComputeShaderShortening = @"#version 450 core
  12. layout (binding = 0, $SRC_FORMAT$) uniform uimage2D src;
  13. layout (binding = 1, $DST_FORMAT$) uniform uimage2D dst;
  14. layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
  15. void main()
  16. {
  17. uvec2 coords = gl_GlobalInvocationID.xy;
  18. ivec2 imageSz = imageSize(src);
  19. if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
  20. {
  21. return;
  22. }
  23. uint coordsShifted = coords.x << $RATIO_LOG2$;
  24. uvec2 dstCoords0 = uvec2(coordsShifted, coords.y);
  25. uvec2 dstCoords1 = uvec2(coordsShifted + 1, coords.y);
  26. uvec2 dstCoords2 = uvec2(coordsShifted + 2, coords.y);
  27. uvec2 dstCoords3 = uvec2(coordsShifted + 3, coords.y);
  28. uvec4 rgba = imageLoad(src, ivec2(coords));
  29. imageStore(dst, ivec2(dstCoords0), rgba.rrrr);
  30. imageStore(dst, ivec2(dstCoords1), rgba.gggg);
  31. imageStore(dst, ivec2(dstCoords2), rgba.bbbb);
  32. imageStore(dst, ivec2(dstCoords3), rgba.aaaa);
  33. }";
  34. private const string ComputeShaderWidening = @"#version 450 core
  35. layout (binding = 0, $SRC_FORMAT$) uniform uimage2D src;
  36. layout (binding = 1, $DST_FORMAT$) uniform uimage2D dst;
  37. layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
  38. void main()
  39. {
  40. uvec2 coords = gl_GlobalInvocationID.xy;
  41. ivec2 imageSz = imageSize(dst);
  42. if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
  43. {
  44. return;
  45. }
  46. uvec2 srcCoords = uvec2(coords.x << $RATIO_LOG2$, coords.y);
  47. uint r = imageLoad(src, ivec2(srcCoords) + ivec2(0, 0)).r;
  48. uint g = imageLoad(src, ivec2(srcCoords) + ivec2(1, 0)).r;
  49. uint b = imageLoad(src, ivec2(srcCoords) + ivec2(2, 0)).r;
  50. uint a = imageLoad(src, ivec2(srcCoords) + ivec2(3, 0)).r;
  51. imageStore(dst, ivec2(coords), uvec4(r, g, b, a));
  52. }";
  53. private readonly OpenGLRenderer _renderer;
  54. private readonly Dictionary<int, int> _shorteningProgramHandles;
  55. private readonly Dictionary<int, int> _wideningProgramHandles;
  56. public TextureCopyIncompatible(OpenGLRenderer renderer)
  57. {
  58. _renderer = renderer;
  59. _shorteningProgramHandles = new Dictionary<int, int>();
  60. _wideningProgramHandles = new Dictionary<int, int>();
  61. }
  62. public void CopyIncompatibleFormats(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int depth, int levels)
  63. {
  64. TextureCreateInfo srcInfo = src.Info;
  65. TextureCreateInfo dstInfo = dst.Info;
  66. int srcBpp = src.Info.BytesPerPixel;
  67. int dstBpp = dst.Info.BytesPerPixel;
  68. // Calculate ideal component size, given our constraints:
  69. // - Component size must not exceed bytes per pixel of source and destination image formats.
  70. // - Maximum component size is 4 (R32).
  71. int componentSize = Math.Min(Math.Min(srcBpp, dstBpp), 4);
  72. int srcComponentsCount = srcBpp / componentSize;
  73. int dstComponentsCount = dstBpp / componentSize;
  74. var srcFormat = GetFormat(componentSize, srcComponentsCount);
  75. var dstFormat = GetFormat(componentSize, dstComponentsCount);
  76. GL.UseProgram(srcBpp < dstBpp
  77. ? GetWideningShader(componentSize, srcComponentsCount, dstComponentsCount)
  78. : GetShorteningShader(componentSize, srcComponentsCount, dstComponentsCount));
  79. for (int l = 0; l < levels; l++)
  80. {
  81. int srcWidth = Math.Max(1, src.Info.Width >> l);
  82. int srcHeight = Math.Max(1, src.Info.Height >> l);
  83. int dstWidth = Math.Max(1, dst.Info.Width >> l);
  84. int dstHeight = Math.Max(1, dst.Info.Height >> l);
  85. int width = Math.Min(srcWidth, dstWidth);
  86. int height = Math.Min(srcHeight, dstHeight);
  87. for (int z = 0; z < depth; z++)
  88. {
  89. GL.BindImageTexture(0, src.Handle, srcLevel + l, false, srcLayer + z, TextureAccess.ReadOnly, srcFormat);
  90. GL.BindImageTexture(1, dst.Handle, dstLevel + l, false, dstLayer + z, TextureAccess.WriteOnly, dstFormat);
  91. GL.DispatchCompute((width + 31) / 32, (height + 31) / 32, 1);
  92. }
  93. }
  94. Pipeline pipeline = (Pipeline)_renderer.Pipeline;
  95. pipeline.RestoreProgram();
  96. pipeline.RestoreImages1And2();
  97. }
  98. private static SizedInternalFormat GetFormat(int componentSize, int componentsCount)
  99. {
  100. if (componentSize == 1)
  101. {
  102. return componentsCount switch
  103. {
  104. 1 => SizedInternalFormat.R8ui,
  105. 2 => SizedInternalFormat.Rg8ui,
  106. 4 => SizedInternalFormat.Rgba8ui,
  107. _ => throw new ArgumentException($"Invalid components count {componentsCount}.")
  108. };
  109. }
  110. else if (componentSize == 2)
  111. {
  112. return componentsCount switch
  113. {
  114. 1 => SizedInternalFormat.R16ui,
  115. 2 => SizedInternalFormat.Rg16ui,
  116. 4 => SizedInternalFormat.Rgba16ui,
  117. _ => throw new ArgumentException($"Invalid components count {componentsCount}.")
  118. };
  119. }
  120. else if (componentSize == 4)
  121. {
  122. return componentsCount switch
  123. {
  124. 1 => SizedInternalFormat.R32ui,
  125. 2 => SizedInternalFormat.Rg32ui,
  126. 4 => SizedInternalFormat.Rgba32ui,
  127. _ => throw new ArgumentException($"Invalid components count {componentsCount}.")
  128. };
  129. }
  130. else
  131. {
  132. throw new ArgumentException($"Invalid component size {componentSize}.");
  133. }
  134. }
  135. private int GetShorteningShader(int componentSize, int srcComponentsCount, int dstComponentsCount)
  136. {
  137. return GetShader(ComputeShaderShortening, _shorteningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount);
  138. }
  139. private int GetWideningShader(int componentSize, int srcComponentsCount, int dstComponentsCount)
  140. {
  141. return GetShader(ComputeShaderWidening, _wideningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount);
  142. }
  143. private int GetShader(
  144. string code,
  145. Dictionary<int, int> programHandles,
  146. int componentSize,
  147. int srcComponentsCount,
  148. int dstComponentsCount)
  149. {
  150. int componentSizeLog2 = BitOperations.Log2((uint)componentSize);
  151. int srcIndex = componentSizeLog2 + BitOperations.Log2((uint)srcComponentsCount) * 3;
  152. int dstIndex = componentSizeLog2 + BitOperations.Log2((uint)dstComponentsCount) * 3;
  153. int key = srcIndex | (dstIndex << 8);
  154. if (!programHandles.TryGetValue(key, out int programHandle))
  155. {
  156. int csHandle = GL.CreateShader(ShaderType.ComputeShader);
  157. string[] formatTable = new[] { "r8ui", "r16ui", "r32ui", "rg8ui", "rg16ui", "rg32ui", "rgba8ui", "rgba16ui", "rgba32ui" };
  158. string srcFormat = formatTable[srcIndex];
  159. string dstFormat = formatTable[dstIndex];
  160. int srcBpp = srcComponentsCount * componentSize;
  161. int dstBpp = dstComponentsCount * componentSize;
  162. int ratio = srcBpp < dstBpp ? dstBpp / srcBpp : srcBpp / dstBpp;
  163. int ratioLog2 = BitOperations.Log2((uint)ratio);
  164. GL.ShaderSource(csHandle, code
  165. .Replace("$SRC_FORMAT$", srcFormat)
  166. .Replace("$DST_FORMAT$", dstFormat)
  167. .Replace("$RATIO_LOG2$", ratioLog2.ToString(CultureInfo.InvariantCulture)));
  168. GL.CompileShader(csHandle);
  169. programHandle = GL.CreateProgram();
  170. GL.AttachShader(programHandle, csHandle);
  171. GL.LinkProgram(programHandle);
  172. GL.DetachShader(programHandle, csHandle);
  173. GL.DeleteShader(csHandle);
  174. GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status);
  175. if (status == 0)
  176. {
  177. throw new Exception(GL.GetProgramInfoLog(programHandle));
  178. }
  179. programHandles.Add(key, programHandle);
  180. }
  181. return programHandle;
  182. }
  183. public void Dispose()
  184. {
  185. foreach (int handle in _shorteningProgramHandles.Values)
  186. {
  187. GL.DeleteProgram(handle);
  188. }
  189. _shorteningProgramHandles.Clear();
  190. foreach (int handle in _wideningProgramHandles.Values)
  191. {
  192. GL.DeleteProgram(handle);
  193. }
  194. _wideningProgramHandles.Clear();
  195. }
  196. }
  197. }