diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 110 |
1 files changed, 98 insertions, 12 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 234e7e8c..14ab5d1e 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -58,6 +58,12 @@ namespace Ryujinx.Graphics.Gpu.Image public bool HasCopyDependencies { get; set; } /// <summary> + /// Indicates if the texture group has a pre-emptive flush buffer. + /// When one is present, the group must always be notified on unbind. + /// </summary> + public bool HasFlushBuffer => _flushBuffer != BufferHandle.Null; + + /// <summary> /// Indicates if this texture has any incompatible overlaps alive. /// </summary> public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0; @@ -89,6 +95,10 @@ namespace Ryujinx.Graphics.Gpu.Image private bool _incompatibleOverlapsDirty = true; private bool _flushIncompatibleOverlaps; + private BufferHandle _flushBuffer; + private bool _flushBufferImported; + private bool _flushBufferInvalid; + /// <summary> /// Create a new texture group. /// </summary> @@ -464,8 +474,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// </remarks> /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param> /// <param name="sliceIndex">The index of the slice to flush</param> + /// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param> /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param> - private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, ITexture texture = null) + private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, bool inBuffer, ITexture texture = null) { (int layer, int level) = GetLayerLevelForView(sliceIndex); @@ -475,7 +486,16 @@ namespace Ryujinx.Graphics.Gpu.Image using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked); - Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture); + if (inBuffer) + { + using PinnedSpan<byte> data = _context.Renderer.GetBufferData(_flushBuffer, offset, size); + + Storage.ConvertFromHostCompatibleFormat(region.Memory.Span, data.Get(), level, true); + } + else + { + Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture); + } } /// <summary> @@ -484,12 +504,13 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param> /// <param name="sliceStart">The first slice to flush</param> /// <param name="sliceEnd">The slice to finish flushing on (exclusive)</param> + /// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param> /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param> - private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, ITexture texture = null) + private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, bool inBuffer, ITexture texture = null) { for (int i = sliceStart; i < sliceEnd; i++) { - FlushTextureDataSliceToGuest(tracked, i, texture); + FlushTextureDataSliceToGuest(tracked, i, inBuffer, texture); } } @@ -520,7 +541,7 @@ namespace Ryujinx.Graphics.Gpu.Image { if (endSlice > startSlice) { - FlushSliceRange(tracked, startSlice, endSlice); + FlushSliceRange(tracked, startSlice, endSlice, false); flushed = true; } @@ -553,7 +574,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - FlushSliceRange(tracked, startSlice, endSlice); + FlushSliceRange(tracked, startSlice, endSlice, false); } flushed = true; @@ -566,6 +587,58 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> + /// Flush the texture data into a persistently mapped buffer. + /// If the buffer does not exist, this method will create it. + /// </summary> + /// <param name="handle">Handle of the texture group to flush slices of</param> + public void FlushIntoBuffer(TextureGroupHandle handle) + { + // Ensure that the buffer exists. + + if (_flushBufferInvalid && _flushBuffer != BufferHandle.Null) + { + _flushBufferInvalid = false; + _context.Renderer.DeleteBuffer(_flushBuffer); + _flushBuffer = BufferHandle.Null; + } + + if (_flushBuffer == BufferHandle.Null) + { + if (!TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities)) + { + return; + } + + bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel; + + var hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0; + + if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size)) + { + _flushBuffer = _context.Renderer.CreateBuffer(hostPointer, (int)Storage.Size); + _flushBufferImported = true; + } + else + { + _flushBuffer = _context.Renderer.CreateBuffer((int)Storage.Size, BufferAccess.FlushPersistent); + _flushBufferImported = false; + } + + Storage.BlacklistScale(); + } + + int sliceStart = handle.BaseSlice; + int sliceEnd = sliceStart + handle.SliceCount; + + for (int i = sliceStart; i < sliceEnd; i++) + { + (int layer, int level) = GetLayerLevelForView(i); + + Storage.GetFlushTexture().CopyTo(new BufferRange(_flushBuffer, _allOffsets[i], _sliceSizes[level]), layer, level, _flushBufferImported ? Storage.Info.Stride : 0); + } + } + + /// <summary> /// Clears competing modified flags for all incompatible ranges, if they have possibly been modified. /// </summary> /// <param name="texture">The texture that has been modified</param> @@ -1570,10 +1643,7 @@ namespace Ryujinx.Graphics.Gpu.Image _context.Renderer.BackgroundContextAction(() => { - if (!isGpuThread) - { - handle.Sync(_context); - } + bool inBuffer = !isGpuThread && handle.Sync(_context); Storage.SignalModifiedDirty(); @@ -1585,14 +1655,25 @@ namespace Ryujinx.Graphics.Gpu.Image } } - if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities)) + if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities) && !(inBuffer && _flushBufferImported)) { - FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, Storage.GetFlushTexture()); + FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, inBuffer, Storage.GetFlushTexture()); } }); } /// <summary> + /// Called if any part of the storage texture is unmapped. + /// </summary> + public void Unmapped() + { + if (_flushBufferImported) + { + _flushBufferInvalid = true; + } + } + + /// <summary> /// Dispose this texture group, disposing all related memory tracking handles. /// </summary> public void Dispose() @@ -1606,6 +1687,11 @@ namespace Ryujinx.Graphics.Gpu.Image { incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == this); } + + if (_flushBuffer != BufferHandle.Null) + { + _context.Renderer.DeleteBuffer(_flushBuffer); + } } } } |