diff options
author | riperiperi <rhy3756547@hotmail.com> | 2020-11-20 16:30:59 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-20 13:30:59 -0300 |
commit | cf7044e37bc628f25525941d25b830594b833428 (patch) | |
tree | 3da120676707d1ee7f4c2fcc2273ce036d6fd821 /Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | |
parent | 9852cb9c9ea3793eaef405ebb671052bb4b6643a (diff) |
Perform Compressed<->Uncompressed copies using Pixel Buffer Objects (#1732)
* PBO single layer copy, part 1
Still needs ability to take and set width/height slices. (using pack paramaters)
* PBO Copies pt 2
* Some fixes and cleanup.
* Misc Cleanup
* Move handle into the TextureInfo interface.
This interface is shared between texture storages and views.
* Move unscaled copy to the TextureCopy class.
* Address feedback.
Diffstat (limited to 'Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs')
-rw-r--r-- | Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | 195 |
1 files changed, 194 insertions, 1 deletions
diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index 191e9b63..74832dd8 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -1,5 +1,6 @@ -using Ryujinx.Graphics.GAL; using OpenTK.Graphics.OpenGL; +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; using System; namespace Ryujinx.Graphics.OpenGL.Image @@ -80,6 +81,116 @@ namespace Ryujinx.Graphics.OpenGL.Image } } + public void CopyUnscaled( + ITextureInfo src, + ITextureInfo dst, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel) + { + TextureCreateInfo srcInfo = src.Info; + TextureCreateInfo dstInfo = dst.Info; + + int srcHandle = src.Handle; + int dstHandle = dst.Handle; + + int srcWidth = srcInfo.Width; + int srcHeight = srcInfo.Height; + int srcDepth = srcInfo.GetDepthOrLayers(); + int srcLevels = srcInfo.Levels; + + int dstWidth = dstInfo.Width; + int dstHeight = dstInfo.Height; + int dstDepth = dstInfo.GetDepthOrLayers(); + int dstLevels = dstInfo.Levels; + + srcWidth = Math.Max(1, srcWidth >> srcLevel); + srcHeight = Math.Max(1, srcHeight >> srcLevel); + + dstWidth = Math.Max(1, dstWidth >> dstLevel); + dstHeight = Math.Max(1, dstHeight >> dstLevel); + + if (dstInfo.Target == Target.Texture3D) + { + dstDepth = Math.Max(1, dstDepth >> dstLevel); + } + + int blockWidth = 1; + int blockHeight = 1; + bool sizeInBlocks = false; + + // When copying from a compressed to a non-compressed format, + // the non-compressed texture will have the size of the texture + // in blocks (not in texels), so we must adjust that size to + // match the size in texels of the compressed texture. + if (!srcInfo.IsCompressed && dstInfo.IsCompressed) + { + srcWidth *= dstInfo.BlockWidth; + srcHeight *= dstInfo.BlockHeight; + blockWidth = dstInfo.BlockWidth; + blockHeight = dstInfo.BlockHeight; + + sizeInBlocks = true; + } + else if (srcInfo.IsCompressed && !dstInfo.IsCompressed) + { + dstWidth *= srcInfo.BlockWidth; + dstHeight *= srcInfo.BlockHeight; + blockWidth = srcInfo.BlockWidth; + blockHeight = srcInfo.BlockHeight; + } + + int width = Math.Min(srcWidth, dstWidth); + int height = Math.Min(srcHeight, dstHeight); + int depth = Math.Min(srcDepth, dstDepth); + int levels = Math.Min(srcLevels, dstLevels); + + for (int level = 0; level < levels; level++) + { + // Stop copy if we are already out of the levels range. + if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels) + { + break; + } + + if ((width % blockWidth != 0 || height % blockHeight != 0) && src is TextureView srcView && dst is TextureView dstView) + { + PboCopy(srcView, dstView, srcLayer, dstLayer, srcLevel + level, dstLevel + level, width, height); + } + else + { + int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width; + int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height; + + GL.CopyImageSubData( + srcHandle, + srcInfo.Target.ConvertToImageTarget(), + srcLevel + level, + 0, + 0, + srcLayer, + dstHandle, + dstInfo.Target.ConvertToImageTarget(), + dstLevel + level, + 0, + 0, + dstLayer, + copyWidth, + copyHeight, + depth); + } + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (srcInfo.Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } + } + } + private static void Attach(FramebufferTarget target, Format format, int handle) { if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) @@ -147,6 +258,88 @@ namespace Ryujinx.Graphics.OpenGL.Image return to; } + private TextureView PboCopy(TextureView from, TextureView to, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int width, int height) + { + int dstWidth = width; + int dstHeight = height; + + // The size of the source texture. + int unpackWidth = from.Width; + int unpackHeight = from.Height; + + if (from.Info.IsCompressed != to.Info.IsCompressed) + { + if (from.Info.IsCompressed) + { + // Dest size is in pixels, but should be in blocks + dstWidth = BitUtils.DivRoundUp(width, from.Info.BlockWidth); + dstHeight = BitUtils.DivRoundUp(height, from.Info.BlockHeight); + + // When copying from a compressed texture, the source size must be taken in blocks for unpacking to the uncompressed block texture. + unpackWidth = BitUtils.DivRoundUp(from.Info.Width, from.Info.BlockWidth); + unpackHeight = BitUtils.DivRoundUp(from.Info.Height, from.Info.BlockHeight); + } + else + { + // When copying to a compressed texture, the source size must be scaled by the block width for unpacking on the compressed target. + unpackWidth = from.Info.Width * to.Info.BlockWidth; + unpackHeight = from.Info.Height * to.Info.BlockHeight; + } + } + + EnsurePbo(from); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle); + + // The source texture is written out in full, then the destination is taken as a slice from the data using unpack params. + // The offset points to the base at which the requested layer is at. + + int offset = from.WriteToPbo2D(0, srcLayer, srcLevel); + + // If the destination size is not an exact match for the source unpack parameters, we need to set them to slice the data correctly. + + bool slice = (unpackWidth != dstWidth || unpackHeight != dstHeight); + + if (slice) + { + // Set unpack parameters to take a slice of width/height: + GL.PixelStore(PixelStoreParameter.UnpackRowLength, unpackWidth); + GL.PixelStore(PixelStoreParameter.UnpackImageHeight, unpackHeight); + + if (to.Info.IsCompressed) + { + GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockWidth, to.Info.BlockWidth); + GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockHeight, to.Info.BlockHeight); + GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockDepth, 1); + GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockSize, to.Info.BytesPerPixel); + } + } + + GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPboHandle); + + to.ReadFromPbo2D(offset, dstLayer, dstLevel, dstWidth, dstHeight); + + if (slice) + { + // Reset unpack parameters + GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0); + GL.PixelStore(PixelStoreParameter.UnpackImageHeight, 0); + + if (to.Info.IsCompressed) + { + GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockWidth, 0); + GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockHeight, 0); + GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockDepth, 0); + GL.PixelStore(PixelStoreParameter.UnpackCompressedBlockSize, 0); + } + } + + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); + + return to; + } + private void EnsurePbo(TextureView view) { int requiredSize = 0; |