/ src / Ryujinx.Graphics.Vulkan / TextureCopy.cs
TextureCopy.cs
  1  using Ryujinx.Common;
  2  using Ryujinx.Graphics.GAL;
  3  using Silk.NET.Vulkan;
  4  using System;
  5  using System.Numerics;
  6  
  7  namespace Ryujinx.Graphics.Vulkan
  8  {
  9      static class TextureCopy
 10      {
 11          public static void Blit(
 12              Vk api,
 13              CommandBuffer commandBuffer,
 14              Image srcImage,
 15              Image dstImage,
 16              TextureCreateInfo srcInfo,
 17              TextureCreateInfo dstInfo,
 18              Extents2D srcRegion,
 19              Extents2D dstRegion,
 20              int srcLayer,
 21              int dstLayer,
 22              int srcLevel,
 23              int dstLevel,
 24              int layers,
 25              int levels,
 26              bool linearFilter,
 27              ImageAspectFlags srcAspectFlags = 0,
 28              ImageAspectFlags dstAspectFlags = 0)
 29          {
 30              static (Offset3D, Offset3D) ExtentsToOffset3D(Extents2D extents, int width, int height, int level)
 31              {
 32                  static int Clamp(int value, int max)
 33                  {
 34                      return Math.Clamp(value, 0, max);
 35                  }
 36  
 37                  var xy1 = new Offset3D(Clamp(extents.X1, width) >> level, Clamp(extents.Y1, height) >> level, 0);
 38                  var xy2 = new Offset3D(Clamp(extents.X2, width) >> level, Clamp(extents.Y2, height) >> level, 1);
 39  
 40                  return (xy1, xy2);
 41              }
 42  
 43              if (srcAspectFlags == 0)
 44              {
 45                  srcAspectFlags = srcInfo.Format.ConvertAspectFlags();
 46              }
 47  
 48              if (dstAspectFlags == 0)
 49              {
 50                  dstAspectFlags = dstInfo.Format.ConvertAspectFlags();
 51              }
 52  
 53              var srcOffsets = new ImageBlit.SrcOffsetsBuffer();
 54              var dstOffsets = new ImageBlit.DstOffsetsBuffer();
 55  
 56              var filter = linearFilter && !dstInfo.Format.IsDepthOrStencil() ? Filter.Linear : Filter.Nearest;
 57  
 58              TextureView.InsertImageBarrier(
 59                  api,
 60                  commandBuffer,
 61                  srcImage,
 62                  TextureStorage.DefaultAccessMask,
 63                  AccessFlags.TransferReadBit,
 64                  PipelineStageFlags.AllCommandsBit,
 65                  PipelineStageFlags.TransferBit,
 66                  srcAspectFlags,
 67                  srcLayer,
 68                  srcLevel,
 69                  layers,
 70                  levels);
 71  
 72              uint copySrcLevel = (uint)srcLevel;
 73              uint copyDstLevel = (uint)dstLevel;
 74  
 75              for (int level = 0; level < levels; level++)
 76              {
 77                  var srcSl = new ImageSubresourceLayers(srcAspectFlags, copySrcLevel, (uint)srcLayer, (uint)layers);
 78                  var dstSl = new ImageSubresourceLayers(dstAspectFlags, copyDstLevel, (uint)dstLayer, (uint)layers);
 79  
 80                  (srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level);
 81                  (dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level);
 82  
 83                  var region = new ImageBlit
 84                  {
 85                      SrcSubresource = srcSl,
 86                      SrcOffsets = srcOffsets,
 87                      DstSubresource = dstSl,
 88                      DstOffsets = dstOffsets,
 89                  };
 90  
 91                  api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region, filter);
 92  
 93                  copySrcLevel++;
 94                  copyDstLevel++;
 95  
 96                  if (srcInfo.Target == Target.Texture3D || dstInfo.Target == Target.Texture3D)
 97                  {
 98                      layers = Math.Max(1, layers >> 1);
 99                  }
100              }
101  
102              TextureView.InsertImageBarrier(
103                  api,
104                  commandBuffer,
105                  dstImage,
106                  AccessFlags.TransferWriteBit,
107                  TextureStorage.DefaultAccessMask,
108                  PipelineStageFlags.TransferBit,
109                  PipelineStageFlags.AllCommandsBit,
110                  dstAspectFlags,
111                  dstLayer,
112                  dstLevel,
113                  layers,
114                  levels);
115          }
116  
117          public static void Copy(
118              Vk api,
119              CommandBuffer commandBuffer,
120              Image srcImage,
121              Image dstImage,
122              TextureCreateInfo srcInfo,
123              TextureCreateInfo dstInfo,
124              int srcViewLayer,
125              int dstViewLayer,
126              int srcViewLevel,
127              int dstViewLevel,
128              int srcLayer,
129              int dstLayer,
130              int srcLevel,
131              int dstLevel)
132          {
133              int srcDepth = srcInfo.GetDepthOrLayers();
134              int srcLevels = srcInfo.Levels;
135  
136              int dstDepth = dstInfo.GetDepthOrLayers();
137              int dstLevels = dstInfo.Levels;
138  
139              if (dstInfo.Target == Target.Texture3D)
140              {
141                  dstDepth = Math.Max(1, dstDepth >> dstLevel);
142              }
143  
144              int depth = Math.Min(srcDepth, dstDepth);
145              int levels = Math.Min(srcLevels, dstLevels);
146  
147              Copy(
148                  api,
149                  commandBuffer,
150                  srcImage,
151                  dstImage,
152                  srcInfo,
153                  dstInfo,
154                  srcViewLayer,
155                  dstViewLayer,
156                  srcViewLevel,
157                  dstViewLevel,
158                  srcLayer,
159                  dstLayer,
160                  srcLevel,
161                  dstLevel,
162                  depth,
163                  levels);
164          }
165  
166          private static int ClampLevels(TextureCreateInfo info, int levels)
167          {
168              int width = info.Width;
169              int height = info.Height;
170              int depth = info.Target == Target.Texture3D ? info.Depth : 1;
171  
172              int maxLevels = 1 + BitOperations.Log2((uint)Math.Max(Math.Max(width, height), depth));
173  
174              if (levels > maxLevels)
175              {
176                  levels = maxLevels;
177              }
178  
179              return levels;
180          }
181  
182          public static void Copy(
183              Vk api,
184              CommandBuffer commandBuffer,
185              Image srcImage,
186              Image dstImage,
187              TextureCreateInfo srcInfo,
188              TextureCreateInfo dstInfo,
189              int srcViewLayer,
190              int dstViewLayer,
191              int srcViewLevel,
192              int dstViewLevel,
193              int srcDepthOrLayer,
194              int dstDepthOrLayer,
195              int srcLevel,
196              int dstLevel,
197              int depthOrLayers,
198              int levels)
199          {
200              int srcZ;
201              int srcLayer;
202              int srcDepth;
203              int srcLayers;
204  
205              if (srcInfo.Target == Target.Texture3D)
206              {
207                  srcZ = srcDepthOrLayer;
208                  srcLayer = 0;
209                  srcDepth = depthOrLayers;
210                  srcLayers = 1;
211              }
212              else
213              {
214                  srcZ = 0;
215                  srcLayer = srcDepthOrLayer;
216                  srcDepth = 1;
217                  srcLayers = depthOrLayers;
218              }
219  
220              int dstZ;
221              int dstLayer;
222              int dstLayers;
223  
224              if (dstInfo.Target == Target.Texture3D)
225              {
226                  dstZ = dstDepthOrLayer;
227                  dstLayer = 0;
228                  dstLayers = 1;
229              }
230              else
231              {
232                  dstZ = 0;
233                  dstLayer = dstDepthOrLayer;
234                  dstLayers = depthOrLayers;
235              }
236  
237              int srcWidth = srcInfo.Width;
238              int srcHeight = srcInfo.Height;
239  
240              int dstWidth = dstInfo.Width;
241              int dstHeight = dstInfo.Height;
242  
243              srcWidth = Math.Max(1, srcWidth >> srcLevel);
244              srcHeight = Math.Max(1, srcHeight >> srcLevel);
245  
246              dstWidth = Math.Max(1, dstWidth >> dstLevel);
247              dstHeight = Math.Max(1, dstHeight >> dstLevel);
248  
249              int blockWidth = 1;
250              int blockHeight = 1;
251              bool sizeInBlocks = false;
252  
253              // When copying from a compressed to a non-compressed format,
254              // the non-compressed texture will have the size of the texture
255              // in blocks (not in texels), so we must adjust that size to
256              // match the size in texels of the compressed texture.
257              if (!srcInfo.IsCompressed && dstInfo.IsCompressed)
258              {
259                  srcWidth *= dstInfo.BlockWidth;
260                  srcHeight *= dstInfo.BlockHeight;
261                  blockWidth = dstInfo.BlockWidth;
262                  blockHeight = dstInfo.BlockHeight;
263  
264                  sizeInBlocks = true;
265              }
266              else if (srcInfo.IsCompressed && !dstInfo.IsCompressed)
267              {
268                  dstWidth *= srcInfo.BlockWidth;
269                  dstHeight *= srcInfo.BlockHeight;
270                  blockWidth = srcInfo.BlockWidth;
271                  blockHeight = srcInfo.BlockHeight;
272              }
273  
274              int width = Math.Min(srcWidth, dstWidth);
275              int height = Math.Min(srcHeight, dstHeight);
276  
277              ImageAspectFlags srcAspect = srcInfo.Format.ConvertAspectFlags();
278              ImageAspectFlags dstAspect = dstInfo.Format.ConvertAspectFlags();
279  
280              TextureView.InsertImageBarrier(
281                  api,
282                  commandBuffer,
283                  srcImage,
284                  TextureStorage.DefaultAccessMask,
285                  AccessFlags.TransferReadBit,
286                  PipelineStageFlags.AllCommandsBit,
287                  PipelineStageFlags.TransferBit,
288                  srcAspect,
289                  srcViewLayer + srcLayer,
290                  srcViewLevel + srcLevel,
291                  srcLayers,
292                  levels);
293  
294              for (int level = 0; level < levels; level++)
295              {
296                  // Stop copy if we are already out of the levels range.
297                  if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels)
298                  {
299                      break;
300                  }
301  
302                  var srcSl = new ImageSubresourceLayers(
303                      srcAspect,
304                      (uint)(srcViewLevel + srcLevel + level),
305                      (uint)(srcViewLayer + srcLayer),
306                      (uint)srcLayers);
307  
308                  var dstSl = new ImageSubresourceLayers(
309                      dstAspect,
310                      (uint)(dstViewLevel + dstLevel + level),
311                      (uint)(dstViewLayer + dstLayer),
312                      (uint)dstLayers);
313  
314                  int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
315                  int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
316  
317                  var extent = new Extent3D((uint)copyWidth, (uint)copyHeight, (uint)srcDepth);
318  
319                  if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)
320                  {
321                      var region = new ImageResolve(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
322  
323                      api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
324                  }
325                  else
326                  {
327                      var region = new ImageCopy(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
328  
329                      api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
330                  }
331  
332                  width = Math.Max(1, width >> 1);
333                  height = Math.Max(1, height >> 1);
334  
335                  if (srcInfo.Target == Target.Texture3D)
336                  {
337                      srcDepth = Math.Max(1, srcDepth >> 1);
338                  }
339              }
340  
341              TextureView.InsertImageBarrier(
342                  api,
343                  commandBuffer,
344                  dstImage,
345                  AccessFlags.TransferWriteBit,
346                  TextureStorage.DefaultAccessMask,
347                  PipelineStageFlags.TransferBit,
348                  PipelineStageFlags.AllCommandsBit,
349                  dstAspect,
350                  dstViewLayer + dstLayer,
351                  dstViewLevel + dstLevel,
352                  dstLayers,
353                  levels);
354          }
355  
356          public unsafe static void ResolveDepthStencil(
357              VulkanRenderer gd,
358              Device device,
359              CommandBufferScoped cbs,
360              TextureView src,
361              TextureView dst)
362          {
363              var dsAttachmentReference = new AttachmentReference2(StructureType.AttachmentReference2, null, 0, ImageLayout.General);
364              var dsResolveAttachmentReference = new AttachmentReference2(StructureType.AttachmentReference2, null, 1, ImageLayout.General);
365  
366              var subpassDsResolve = new SubpassDescriptionDepthStencilResolve
367              {
368                  SType = StructureType.SubpassDescriptionDepthStencilResolve,
369                  PDepthStencilResolveAttachment = &dsResolveAttachmentReference,
370                  DepthResolveMode = ResolveModeFlags.SampleZeroBit,
371                  StencilResolveMode = ResolveModeFlags.SampleZeroBit,
372              };
373  
374              var subpass = new SubpassDescription2
375              {
376                  SType = StructureType.SubpassDescription2,
377                  PipelineBindPoint = PipelineBindPoint.Graphics,
378                  PDepthStencilAttachment = &dsAttachmentReference,
379                  PNext = &subpassDsResolve,
380              };
381  
382              AttachmentDescription2[] attachmentDescs = new AttachmentDescription2[2];
383  
384              attachmentDescs[0] = new AttachmentDescription2(
385                  StructureType.AttachmentDescription2,
386                  null,
387                  0,
388                  src.VkFormat,
389                  TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)src.Info.Samples),
390                  AttachmentLoadOp.Load,
391                  AttachmentStoreOp.Store,
392                  AttachmentLoadOp.Load,
393                  AttachmentStoreOp.Store,
394                  ImageLayout.General,
395                  ImageLayout.General);
396  
397              attachmentDescs[1] = new AttachmentDescription2(
398                  StructureType.AttachmentDescription2,
399                  null,
400                  0,
401                  dst.VkFormat,
402                  TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)dst.Info.Samples),
403                  AttachmentLoadOp.Load,
404                  AttachmentStoreOp.Store,
405                  AttachmentLoadOp.Load,
406                  AttachmentStoreOp.Store,
407                  ImageLayout.General,
408                  ImageLayout.General);
409  
410              var subpassDependency = PipelineConverter.CreateSubpassDependency2(gd);
411  
412              fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs)
413              {
414                  var renderPassCreateInfo = new RenderPassCreateInfo2
415                  {
416                      SType = StructureType.RenderPassCreateInfo2,
417                      PAttachments = pAttachmentDescs,
418                      AttachmentCount = (uint)attachmentDescs.Length,
419                      PSubpasses = &subpass,
420                      SubpassCount = 1,
421                      PDependencies = &subpassDependency,
422                      DependencyCount = 1,
423                  };
424  
425                  gd.Api.CreateRenderPass2(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
426  
427                  using var rp = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
428  
429                  ImageView* attachments = stackalloc ImageView[2];
430  
431                  var srcView = src.GetImageViewForAttachment();
432                  var dstView = dst.GetImageViewForAttachment();
433  
434                  attachments[0] = srcView.Get(cbs).Value;
435                  attachments[1] = dstView.Get(cbs).Value;
436  
437                  var framebufferCreateInfo = new FramebufferCreateInfo
438                  {
439                      SType = StructureType.FramebufferCreateInfo,
440                      RenderPass = rp.Get(cbs).Value,
441                      AttachmentCount = 2,
442                      PAttachments = attachments,
443                      Width = (uint)src.Width,
444                      Height = (uint)src.Height,
445                      Layers = (uint)src.Layers,
446                  };
447  
448                  gd.Api.CreateFramebuffer(device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
449                  using var fb = new Auto<DisposableFramebuffer>(new DisposableFramebuffer(gd.Api, device, framebuffer), null, srcView, dstView);
450  
451                  var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height));
452                  var clearValue = new ClearValue();
453  
454                  var renderPassBeginInfo = new RenderPassBeginInfo
455                  {
456                      SType = StructureType.RenderPassBeginInfo,
457                      RenderPass = rp.Get(cbs).Value,
458                      Framebuffer = fb.Get(cbs).Value,
459                      RenderArea = renderArea,
460                      PClearValues = &clearValue,
461                      ClearValueCount = 1,
462                  };
463  
464                  // The resolve operation happens at the end of the subpass, so let's just do a begin/end
465                  // to resolve the depth-stencil texture.
466                  // TODO: Do speculative resolve and part of the same render pass as the draw to avoid
467                  // ending the current render pass?
468                  gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
469                  gd.Api.CmdEndRenderPass(cbs.CommandBuffer);
470              }
471          }
472      }
473  }