/ src / Ryujinx.Graphics.OpenGL / Image / TextureCopy.cs
TextureCopy.cs
  1  using OpenTK.Graphics.OpenGL;
  2  using Ryujinx.Common;
  3  using Ryujinx.Graphics.GAL;
  4  using System;
  5  
  6  namespace Ryujinx.Graphics.OpenGL.Image
  7  {
  8      class TextureCopy : IDisposable
  9      {
 10          private readonly OpenGLRenderer _renderer;
 11  
 12          private int _srcFramebuffer;
 13          private int _dstFramebuffer;
 14  
 15          private int _copyPboHandle;
 16          private int _copyPboSize;
 17  
 18          public IntermediatePool IntermediatePool { get; }
 19  
 20          public TextureCopy(OpenGLRenderer renderer)
 21          {
 22              _renderer = renderer;
 23              IntermediatePool = new IntermediatePool(renderer);
 24          }
 25  
 26          public void Copy(
 27              TextureView src,
 28              TextureView dst,
 29              Extents2D srcRegion,
 30              Extents2D dstRegion,
 31              bool linearFilter,
 32              int srcLayer = 0,
 33              int dstLayer = 0,
 34              int srcLevel = 0,
 35              int dstLevel = 0)
 36          {
 37              int levels = Math.Min(src.Info.Levels - srcLevel, dst.Info.Levels - dstLevel);
 38              int layers = Math.Min(src.Info.GetLayers() - srcLayer, dst.Info.GetLayers() - dstLayer);
 39  
 40              Copy(src, dst, srcRegion, dstRegion, linearFilter, srcLayer, dstLayer, srcLevel, dstLevel, layers, levels);
 41          }
 42  
 43          public void Copy(
 44              TextureView src,
 45              TextureView dst,
 46              Extents2D srcRegion,
 47              Extents2D dstRegion,
 48              bool linearFilter,
 49              int srcLayer,
 50              int dstLayer,
 51              int srcLevel,
 52              int dstLevel,
 53              int layers,
 54              int levels)
 55          {
 56              TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src;
 57  
 58              (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers();
 59  
 60              GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
 61              GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
 62  
 63              if (srcLevel != 0)
 64              {
 65                  srcRegion = srcRegion.Reduce(srcLevel);
 66              }
 67  
 68              if (dstLevel != 0)
 69              {
 70                  dstRegion = dstRegion.Reduce(dstLevel);
 71              }
 72  
 73              for (int level = 0; level < levels; level++)
 74              {
 75                  for (int layer = 0; layer < layers; layer++)
 76                  {
 77                      if ((srcLayer | dstLayer) != 0 || layers > 1)
 78                      {
 79                          Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level, srcLayer + layer);
 80                          Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level, dstLayer + layer);
 81                      }
 82                      else
 83                      {
 84                          Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level);
 85                          Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level);
 86                      }
 87  
 88                      ClearBufferMask mask = GetMask(src.Format);
 89  
 90                      if ((mask & (ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit)) != 0 || src.Format.IsInteger())
 91                      {
 92                          linearFilter = false;
 93                      }
 94  
 95                      BlitFramebufferFilter filter = linearFilter
 96                          ? BlitFramebufferFilter.Linear
 97                          : BlitFramebufferFilter.Nearest;
 98  
 99                      GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
100                      GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
101  
102                      GL.Disable(EnableCap.RasterizerDiscard);
103                      GL.Disable(IndexedEnableCap.ScissorTest, 0);
104  
105                      GL.BlitFramebuffer(
106                          srcRegion.X1,
107                          srcRegion.Y1,
108                          srcRegion.X2,
109                          srcRegion.Y2,
110                          dstRegion.X1,
111                          dstRegion.Y1,
112                          dstRegion.X2,
113                          dstRegion.Y2,
114                          mask,
115                          filter);
116                  }
117  
118                  if (level < levels - 1)
119                  {
120                      srcRegion = srcRegion.Reduce(1);
121                      dstRegion = dstRegion.Reduce(1);
122                  }
123              }
124  
125              Attach(FramebufferTarget.ReadFramebuffer, src.Format, 0);
126              Attach(FramebufferTarget.DrawFramebuffer, dst.Format, 0);
127  
128              GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
129              GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
130  
131              ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable();
132              ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard();
133  
134              if (srcConverted != src)
135              {
136                  srcConverted.Dispose();
137              }
138          }
139  
140          public void CopyUnscaled(
141              ITextureInfo src,
142              ITextureInfo dst,
143              int srcLayer,
144              int dstLayer,
145              int srcLevel,
146              int dstLevel)
147          {
148              TextureCreateInfo srcInfo = src.Info;
149              TextureCreateInfo dstInfo = dst.Info;
150  
151              int srcDepth = srcInfo.GetDepthOrLayers();
152              int srcLevels = srcInfo.Levels;
153  
154              int dstDepth = dstInfo.GetDepthOrLayers();
155              int dstLevels = dstInfo.Levels;
156  
157              if (dstInfo.Target == Target.Texture3D)
158              {
159                  dstDepth = Math.Max(1, dstDepth >> dstLevel);
160              }
161  
162              int depth = Math.Min(srcDepth, dstDepth);
163              int levels = Math.Min(srcLevels, dstLevels);
164  
165              CopyUnscaled(src, dst, srcLayer, dstLayer, srcLevel, dstLevel, depth, levels);
166          }
167  
168          public void CopyUnscaled(
169              ITextureInfo src,
170              ITextureInfo dst,
171              int srcLayer,
172              int dstLayer,
173              int srcLevel,
174              int dstLevel,
175              int depth,
176              int levels)
177          {
178              TextureCreateInfo srcInfo = src.Info;
179              TextureCreateInfo dstInfo = dst.Info;
180  
181              int srcHandle = src.Handle;
182              int dstHandle = dst.Handle;
183  
184              int srcWidth = srcInfo.Width;
185              int srcHeight = srcInfo.Height;
186  
187              int dstWidth = dstInfo.Width;
188              int dstHeight = dstInfo.Height;
189  
190              srcWidth = Math.Max(1, srcWidth >> srcLevel);
191              srcHeight = Math.Max(1, srcHeight >> srcLevel);
192  
193              dstWidth = Math.Max(1, dstWidth >> dstLevel);
194              dstHeight = Math.Max(1, dstHeight >> dstLevel);
195  
196              int blockWidth = 1;
197              int blockHeight = 1;
198              bool sizeInBlocks = false;
199  
200              // When copying from a compressed to a non-compressed format,
201              // the non-compressed texture will have the size of the texture
202              // in blocks (not in texels), so we must adjust that size to
203              // match the size in texels of the compressed texture.
204              if (!srcInfo.IsCompressed && dstInfo.IsCompressed)
205              {
206                  srcWidth *= dstInfo.BlockWidth;
207                  srcHeight *= dstInfo.BlockHeight;
208                  blockWidth = dstInfo.BlockWidth;
209                  blockHeight = dstInfo.BlockHeight;
210  
211                  sizeInBlocks = true;
212              }
213              else if (srcInfo.IsCompressed && !dstInfo.IsCompressed)
214              {
215                  dstWidth *= srcInfo.BlockWidth;
216                  dstHeight *= srcInfo.BlockHeight;
217                  blockWidth = srcInfo.BlockWidth;
218                  blockHeight = srcInfo.BlockHeight;
219              }
220  
221              int width = Math.Min(srcWidth, dstWidth);
222              int height = Math.Min(srcHeight, dstHeight);
223  
224              for (int level = 0; level < levels; level++)
225              {
226                  // Stop copy if we are already out of the levels range.
227                  if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels)
228                  {
229                      break;
230                  }
231  
232                  if ((width % blockWidth != 0 || height % blockHeight != 0) && src is TextureView srcView && dst is TextureView dstView)
233                  {
234                      PboCopy(srcView, dstView, srcLayer, dstLayer, srcLevel + level, dstLevel + level, width, height);
235                  }
236                  else
237                  {
238                      int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
239                      int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
240  
241                      if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows)
242                      {
243                          GL.CopyImageSubData(
244                              src.Storage.Handle,
245                              src.Storage.Info.Target.ConvertToImageTarget(),
246                              src.FirstLevel + srcLevel + level,
247                              0,
248                              0,
249                              src.FirstLayer + srcLayer,
250                              dst.Storage.Handle,
251                              dst.Storage.Info.Target.ConvertToImageTarget(),
252                              dst.FirstLevel + dstLevel + level,
253                              0,
254                              0,
255                              dst.FirstLayer + dstLayer,
256                              copyWidth,
257                              copyHeight,
258                              depth);
259                      }
260                      else
261                      {
262                          GL.CopyImageSubData(
263                              srcHandle,
264                              srcInfo.Target.ConvertToImageTarget(),
265                              srcLevel + level,
266                              0,
267                              0,
268                              srcLayer,
269                              dstHandle,
270                              dstInfo.Target.ConvertToImageTarget(),
271                              dstLevel + level,
272                              0,
273                              0,
274                              dstLayer,
275                              copyWidth,
276                              copyHeight,
277                              depth);
278                      }
279                  }
280  
281                  width = Math.Max(1, width >> 1);
282                  height = Math.Max(1, height >> 1);
283  
284                  if (srcInfo.Target == Target.Texture3D)
285                  {
286                      depth = Math.Max(1, depth >> 1);
287                  }
288              }
289          }
290  
291          private static FramebufferAttachment AttachmentForFormat(Format format)
292          {
293              if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint)
294              {
295                  return FramebufferAttachment.DepthStencilAttachment;
296              }
297              else if (FormatTable.IsDepthOnly(format))
298              {
299                  return FramebufferAttachment.DepthAttachment;
300              }
301              else if (format == Format.S8Uint)
302              {
303                  return FramebufferAttachment.StencilAttachment;
304              }
305              else
306              {
307                  return FramebufferAttachment.ColorAttachment0;
308              }
309          }
310  
311          private static void Attach(FramebufferTarget target, Format format, int handle, int level = 0)
312          {
313              FramebufferAttachment attachment = AttachmentForFormat(format);
314  
315              GL.FramebufferTexture(target, attachment, handle, level);
316          }
317  
318          private static void Attach(FramebufferTarget target, Format format, int handle, int level, int layer)
319          {
320              FramebufferAttachment attachment = AttachmentForFormat(format);
321  
322              GL.FramebufferTextureLayer(target, attachment, handle, level, layer);
323          }
324  
325          private static ClearBufferMask GetMask(Format format)
326          {
327              if (FormatTable.IsPackedDepthStencil(format))
328              {
329                  return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
330              }
331              else if (FormatTable.IsDepthOnly(format))
332              {
333                  return ClearBufferMask.DepthBufferBit;
334              }
335              else if (format == Format.S8Uint)
336              {
337                  return ClearBufferMask.StencilBufferBit;
338              }
339              else
340              {
341                  return ClearBufferMask.ColorBufferBit;
342              }
343          }
344  
345          public TextureView BgraSwap(TextureView from)
346          {
347              TextureView to = (TextureView)_renderer.CreateTexture(from.Info);
348  
349              EnsurePbo(from);
350  
351              GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle);
352  
353              from.WriteToPbo(0, forceBgra: true);
354  
355              GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
356              GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPboHandle);
357  
358              to.ReadFromPbo(0, _copyPboSize);
359  
360              GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
361  
362              return to;
363          }
364  
365          public void PboCopy(TextureView from, TextureView to, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int width, int height)
366          {
367              int dstWidth = width;
368              int dstHeight = height;
369  
370              // The size of the source texture.
371              int unpackWidth = from.Width;
372              int unpackHeight = from.Height;
373  
374              if (from.Info.IsCompressed != to.Info.IsCompressed)
375              {
376                  if (from.Info.IsCompressed)
377                  {
378                      // Dest size is in pixels, but should be in blocks
379                      dstWidth = BitUtils.DivRoundUp(width, from.Info.BlockWidth);
380                      dstHeight = BitUtils.DivRoundUp(height, from.Info.BlockHeight);
381  
382                      // When copying from a compressed texture, the source size must be taken in blocks for unpacking to the uncompressed block texture.
383                      unpackWidth = BitUtils.DivRoundUp(from.Info.Width, from.Info.BlockWidth);
384                      unpackHeight = BitUtils.DivRoundUp(from.Info.Height, from.Info.BlockHeight);
385                  }
386                  else
387                  {
388                      // When copying to a compressed texture, the source size must be scaled by the block width for unpacking on the compressed target.
389                      unpackWidth = from.Info.Width * to.Info.BlockWidth;
390                      unpackHeight = from.Info.Height * to.Info.BlockHeight;
391                  }
392              }
393  
394              EnsurePbo(from);
395  
396              GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle);
397  
398              // The source texture is written out in full, then the destination is taken as a slice from the data using unpack params.
399              // The offset points to the base at which the requested layer is at.
400  
401              int offset = from.WriteToPbo2D(0, srcLayer, srcLevel);
402  
403              // If the destination size is not an exact match for the source unpack parameters, we need to set them to slice the data correctly.
404  
405              bool slice = (unpackWidth != dstWidth || unpackHeight != dstHeight);
406  
407              if (slice)
408              {
409                  // Set unpack parameters to take a slice of width/height:
410                  GL.PixelStore(PixelStoreParameter.UnpackRowLength, unpackWidth);
411                  GL.PixelStore(PixelStoreParameter.UnpackImageHeight, unpackHeight);
412  
413                  if (to.Info.IsCompressed)
414                  {
415                      GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockWidth, to.Info.BlockWidth);
416                      GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockHeight, to.Info.BlockHeight);
417                      GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockDepth, 1);
418                      GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockSize, to.Info.BytesPerPixel);
419                  }
420              }
421  
422              GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
423              GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPboHandle);
424  
425              to.ReadFromPbo2D(offset, dstLayer, dstLevel, dstWidth, dstHeight);
426  
427              if (slice)
428              {
429                  // Reset unpack parameters
430                  GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0);
431                  GL.PixelStore(PixelStoreParameter.UnpackImageHeight, 0);
432  
433                  if (to.Info.IsCompressed)
434                  {
435                      GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockWidth, 0);
436                      GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockHeight, 0);
437                      GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockDepth, 0);
438                      GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockSize, 0);
439                  }
440              }
441  
442              GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
443          }
444  
445          private void EnsurePbo(TextureView view)
446          {
447              int requiredSize = 0;
448  
449              for (int level = 0; level < view.Info.Levels; level++)
450              {
451                  requiredSize += view.Info.GetMipSize(level);
452              }
453  
454              if (_copyPboSize < requiredSize && _copyPboHandle != 0)
455              {
456                  GL.DeleteBuffer(_copyPboHandle);
457  
458                  _copyPboHandle = 0;
459              }
460  
461              if (_copyPboHandle == 0)
462              {
463                  _copyPboHandle = GL.GenBuffer();
464                  _copyPboSize = requiredSize;
465  
466                  GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle);
467                  GL.BufferData(BufferTarget.PixelPackBuffer, requiredSize, IntPtr.Zero, BufferUsageHint.DynamicCopy);
468              }
469          }
470  
471          private int GetSrcFramebufferLazy()
472          {
473              if (_srcFramebuffer == 0)
474              {
475                  _srcFramebuffer = GL.GenFramebuffer();
476              }
477  
478              return _srcFramebuffer;
479          }
480  
481          private int GetDstFramebufferLazy()
482          {
483              if (_dstFramebuffer == 0)
484              {
485                  _dstFramebuffer = GL.GenFramebuffer();
486              }
487  
488              return _dstFramebuffer;
489          }
490  
491          public void Dispose()
492          {
493              if (_srcFramebuffer != 0)
494              {
495                  GL.DeleteFramebuffer(_srcFramebuffer);
496  
497                  _srcFramebuffer = 0;
498              }
499  
500              if (_dstFramebuffer != 0)
501              {
502                  GL.DeleteFramebuffer(_dstFramebuffer);
503  
504                  _dstFramebuffer = 0;
505              }
506  
507              if (_copyPboHandle != 0)
508              {
509                  GL.DeleteBuffer(_copyPboHandle);
510  
511                  _copyPboHandle = 0;
512              }
513  
514              IntermediatePool.Dispose();
515          }
516      }
517  }