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 }