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 }