/ src / Ryujinx.Graphics.OpenGL / Image / TextureCopyMS.cs
TextureCopyMS.cs
  1  using OpenTK.Graphics.OpenGL;
  2  using Ryujinx.Graphics.GAL;
  3  using System;
  4  using System.Numerics;
  5  
  6  namespace Ryujinx.Graphics.OpenGL.Image
  7  {
  8      class TextureCopyMS
  9      {
 10          private const string ComputeShaderMSToNonMS = @"#version 450 core
 11  
 12  layout (binding = 0, $FORMAT$) uniform uimage2DMS imgIn;
 13  layout (binding = 1, $FORMAT$) uniform uimage2D imgOut;
 14  
 15  layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
 16  
 17  void main()
 18  {
 19      uvec2 coords = gl_GlobalInvocationID.xy;
 20      ivec2 imageSz = imageSize(imgOut);
 21      if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
 22      {
 23          return;
 24      }
 25      int inSamples = imageSamples(imgIn);
 26      int samplesInXLog2 = 0;
 27      int samplesInYLog2 = 0;
 28      switch (inSamples)
 29      {
 30          case 2:
 31              samplesInXLog2 = 1;
 32              break;
 33          case 4:
 34              samplesInXLog2 = 1;
 35              samplesInYLog2 = 1;
 36              break;
 37          case 8:
 38              samplesInXLog2 = 2;
 39              samplesInYLog2 = 1;
 40              break;
 41          case 16:
 42              samplesInXLog2 = 2;
 43              samplesInYLog2 = 2;
 44              break;
 45      }
 46      int samplesInX = 1 << samplesInXLog2;
 47      int samplesInY = 1 << samplesInYLog2;
 48      int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
 49      uvec4 value = imageLoad(imgIn, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx);
 50      imageStore(imgOut, ivec2(coords), value);
 51  }";
 52  
 53          private const string ComputeShaderNonMSToMS = @"#version 450 core
 54  
 55  layout (binding = 0, $FORMAT$) uniform uimage2D imgIn;
 56  layout (binding = 1, $FORMAT$) uniform uimage2DMS imgOut;
 57  
 58  layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
 59  
 60  void main()
 61  {
 62      uvec2 coords = gl_GlobalInvocationID.xy;
 63      ivec2 imageSz = imageSize(imgIn);
 64      if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
 65      {
 66          return;
 67      }
 68      int outSamples = imageSamples(imgOut);
 69      int samplesInXLog2 = 0;
 70      int samplesInYLog2 = 0;
 71      switch (outSamples)
 72      {
 73          case 2:
 74              samplesInXLog2 = 1;
 75              break;
 76          case 4:
 77              samplesInXLog2 = 1;
 78              samplesInYLog2 = 1;
 79              break;
 80          case 8:
 81              samplesInXLog2 = 2;
 82              samplesInYLog2 = 1;
 83              break;
 84          case 16:
 85              samplesInXLog2 = 2;
 86              samplesInYLog2 = 2;
 87              break;
 88      }
 89      int samplesInX = 1 << samplesInXLog2;
 90      int samplesInY = 1 << samplesInYLog2;
 91      int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
 92      uvec4 value = imageLoad(imgIn, ivec2(coords));
 93      imageStore(imgOut, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx, value);
 94  }";
 95  
 96          private readonly OpenGLRenderer _renderer;
 97          private readonly int[] _msToNonMSProgramHandles;
 98          private readonly int[] _nonMSToMSProgramHandles;
 99  
100          public TextureCopyMS(OpenGLRenderer renderer)
101          {
102              _renderer = renderer;
103              _msToNonMSProgramHandles = new int[5];
104              _nonMSToMSProgramHandles = new int[5];
105          }
106  
107          public void CopyMSToNonMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
108          {
109              TextureCreateInfo srcInfo = src.Info;
110              TextureCreateInfo dstInfo = dst.Info;
111  
112              int srcHandle = CreateViewIfNeeded(src);
113              int dstHandle = CreateViewIfNeeded(dst);
114  
115              int dstWidth = dstInfo.Width;
116              int dstHeight = dstInfo.Height;
117  
118              GL.UseProgram(GetMSToNonMSShader(srcInfo.BytesPerPixel));
119  
120              for (int z = 0; z < depth; z++)
121              {
122                  GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
123                  GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
124  
125                  GL.DispatchCompute((dstWidth + 31) / 32, (dstHeight + 31) / 32, 1);
126              }
127  
128              Pipeline pipeline = (Pipeline)_renderer.Pipeline;
129  
130              pipeline.RestoreProgram();
131              pipeline.RestoreImages1And2();
132  
133              DestroyViewIfNeeded(src, srcHandle);
134              DestroyViewIfNeeded(dst, dstHandle);
135          }
136  
137          public void CopyNonMSToMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
138          {
139              TextureCreateInfo srcInfo = src.Info;
140              TextureCreateInfo dstInfo = dst.Info;
141  
142              int srcHandle = CreateViewIfNeeded(src);
143              int dstHandle = CreateViewIfNeeded(dst);
144  
145              int srcWidth = srcInfo.Width;
146              int srcHeight = srcInfo.Height;
147  
148              GL.UseProgram(GetNonMSToMSShader(srcInfo.BytesPerPixel));
149  
150              for (int z = 0; z < depth; z++)
151              {
152                  GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
153                  GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
154  
155                  GL.DispatchCompute((srcWidth + 31) / 32, (srcHeight + 31) / 32, 1);
156              }
157  
158              Pipeline pipeline = (Pipeline)_renderer.Pipeline;
159  
160              pipeline.RestoreProgram();
161              pipeline.RestoreImages1And2();
162  
163              DestroyViewIfNeeded(src, srcHandle);
164              DestroyViewIfNeeded(dst, dstHandle);
165          }
166  
167          private static SizedInternalFormat GetFormat(int bytesPerPixel)
168          {
169              return bytesPerPixel switch
170              {
171                  1 => SizedInternalFormat.R8ui,
172                  2 => SizedInternalFormat.R16ui,
173                  4 => SizedInternalFormat.R32ui,
174                  8 => SizedInternalFormat.Rg32ui,
175                  16 => SizedInternalFormat.Rgba32ui,
176                  _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}."),
177              };
178          }
179  
180          private static int CreateViewIfNeeded(ITextureInfo texture)
181          {
182              // Binding sRGB textures as images doesn't work on NVIDIA,
183              // we need to create and bind a RGBA view for it to work.
184              if (texture.Info.Format == Format.R8G8B8A8Srgb)
185              {
186                  int handle = GL.GenTexture();
187  
188                  GL.TextureView(
189                      handle,
190                      texture.Info.Target.Convert(),
191                      texture.Storage.Handle,
192                      PixelInternalFormat.Rgba8,
193                      texture.FirstLevel,
194                      1,
195                      texture.FirstLayer,
196                      texture.Info.GetLayers());
197  
198                  return handle;
199              }
200  
201              return texture.Handle;
202          }
203  
204          private static void DestroyViewIfNeeded(ITextureInfo info, int handle)
205          {
206              if (info.Handle != handle)
207              {
208                  GL.DeleteTexture(handle);
209              }
210          }
211  
212          private int GetMSToNonMSShader(int bytesPerPixel)
213          {
214              return GetShader(ComputeShaderMSToNonMS, _msToNonMSProgramHandles, bytesPerPixel);
215          }
216  
217          private int GetNonMSToMSShader(int bytesPerPixel)
218          {
219              return GetShader(ComputeShaderNonMSToMS, _nonMSToMSProgramHandles, bytesPerPixel);
220          }
221  
222          private static int GetShader(string code, int[] programHandles, int bytesPerPixel)
223          {
224              int index = BitOperations.Log2((uint)bytesPerPixel);
225  
226              if (programHandles[index] == 0)
227              {
228                  int csHandle = GL.CreateShader(ShaderType.ComputeShader);
229  
230                  string format = new[] { "r8ui", "r16ui", "r32ui", "rg32ui", "rgba32ui" }[index];
231  
232                  GL.ShaderSource(csHandle, code.Replace("$FORMAT$", format));
233                  GL.CompileShader(csHandle);
234  
235                  int programHandle = GL.CreateProgram();
236  
237                  GL.AttachShader(programHandle, csHandle);
238                  GL.LinkProgram(programHandle);
239                  GL.DetachShader(programHandle, csHandle);
240                  GL.DeleteShader(csHandle);
241  
242                  GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status);
243  
244                  if (status == 0)
245                  {
246                      throw new Exception(GL.GetProgramInfoLog(programHandle));
247                  }
248  
249                  programHandles[index] = programHandle;
250              }
251  
252              return programHandles[index];
253          }
254  
255          public void Dispose()
256          {
257              for (int i = 0; i < _msToNonMSProgramHandles.Length; i++)
258              {
259                  if (_msToNonMSProgramHandles[i] != 0)
260                  {
261                      GL.DeleteProgram(_msToNonMSProgramHandles[i]);
262                      _msToNonMSProgramHandles[i] = 0;
263                  }
264              }
265  
266              for (int i = 0; i < _nonMSToMSProgramHandles.Length; i++)
267              {
268                  if (_nonMSToMSProgramHandles[i] != 0)
269                  {
270                      GL.DeleteProgram(_nonMSToMSProgramHandles[i]);
271                      _nonMSToMSProgramHandles[i] = 0;
272                  }
273              }
274          }
275      }
276  }