TextureCopyIncompatible.cs 9.0 KB

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