[GPU] Track per-slice guest DCC clear metadata
This commit is contained in:
parent
597192c904
commit
8df819acc2
|
|
@ -84,6 +84,70 @@ public sealed partial class GuestImageCache
|
|||
return false;
|
||||
}
|
||||
|
||||
public static bool IsDccClearCode(byte code) => code is 0x00 or 0x20 or 0x40 or 0x80 or 0xc0;
|
||||
|
||||
public bool TryReadGuestDccClear(ulong metadataAddress, ulong sliceSize, uint slice, out ulong sliceAddress, out byte code)
|
||||
{
|
||||
sliceAddress = 0;
|
||||
code = 0;
|
||||
if (metadataAddress == 0 || sliceSize == 0 || sliceSize > int.MaxValue ||
|
||||
(ulong)slice > (ulong.MaxValue - metadataAddress) / sliceSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var address = metadataAddress + (ulong)slice * sliceSize;
|
||||
if (!IsValidRange(address, sliceSize) || _bufferCache.HasGpuDirtyBytes(address, sliceSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Span<byte> first = stackalloc byte[1];
|
||||
if (!_backing.TryReadBacking(address, first) || !IsDccClearCode(first[0]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent((int)Math.Min(sliceSize, 64 * 1024));
|
||||
try
|
||||
{
|
||||
for (ulong position = 0; position < sliceSize;)
|
||||
{
|
||||
var span = buffer.AsSpan(0, (int)Math.Min(sliceSize - position, (ulong)buffer.Length));
|
||||
if (!_backing.TryReadBacking(address + position, span) || span.IndexOfAnyExcept(first[0]) >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
position += (ulong)span.Length;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
sliceAddress = address;
|
||||
code = first[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SynchronizeGuestDccMetadata(ulong metadataAddress, ulong sliceSize, uint baseLayer, uint layerCount)
|
||||
{
|
||||
if (metadataAddress == 0 || sliceSize == 0 || layerCount == 0 ||
|
||||
(ulong)baseLayer + layerCount > (ulong.MaxValue - metadataAddress) / sliceSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var address = metadataAddress + (ulong)baseLayer * sliceSize;
|
||||
var size = (ulong)layerCount * sliceSize;
|
||||
if (IsValidRange(address, size) && _bufferCache.HasGpuDirtyBytes(address, size))
|
||||
{
|
||||
_ = _bufferCache.TrySynchronizeCpuRead(address, size);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetMetadataSlice(ulong address, uint slice, bool isClear)
|
||||
{
|
||||
using var held = _lock.Hold();
|
||||
|
|
|
|||
|
|
@ -245,6 +245,24 @@ public struct ImageDescription
|
|||
|
||||
public readonly bool IsTiledRenderTarget => TileMode == GuestTileMode.RenderTarget || IsSupportedStandard64RenderTarget;
|
||||
|
||||
public readonly ulong DccSliceSize
|
||||
{
|
||||
get
|
||||
{
|
||||
const ulong DccBlockBytes = 4096;
|
||||
if (Metadata.Kind != MetadataKind.Dcc || TileMode != GuestTileMode.RenderTarget || Resources.Levels != 1 || Samples != 1 ||
|
||||
Extent.Width == 0 || Extent.Height == 0 || BytesPerBlock is 0 or > 16 || !System.Numerics.BitOperations.IsPow2(BytesPerBlock))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var coverageBits = 20 - System.Numerics.BitOperations.TrailingZeroCount(BytesPerBlock);
|
||||
var blockWidth = 1UL << ((coverageBits + 1) / 2);
|
||||
var blockHeight = 1UL << (coverageBits / 2);
|
||||
return (Extent.Width + blockWidth - 1) / blockWidth * ((Extent.Height + blockHeight - 1) / blockHeight) * DccBlockBytes;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsSupportedDisplayRenderTargetTileMode(GuestTileMode tileMode) => tileMode == GuestTileMode.RenderTarget;
|
||||
|
||||
public static bool CanUseDisplayNativeWithoutUpload(DisplayCompression compression, bool renderTarget, bool gpuModified, bool guestModified) =>
|
||||
|
|
|
|||
|
|
@ -156,6 +156,36 @@ public sealed partial class GuestImageCacheTests
|
|||
harness.Shutdown();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GuestDccClear_ReadsOnlyUniformClearSlices()
|
||||
{
|
||||
if (!GatePrerequisites.Ready(_vulkan)) return;
|
||||
using var harness = new CacheHarness(_vulkan);
|
||||
const ulong SliceSize = 0x2000;
|
||||
var metadata = harness.MapBacked(0x10000, ReadWrite);
|
||||
harness.Write(metadata, Enumerable.Repeat((byte)0x80, (int)SliceSize).ToArray());
|
||||
harness.Write(metadata + SliceSize, Enumerable.Repeat((byte)0x00, (int)SliceSize).ToArray());
|
||||
var mixed = Enumerable.Repeat((byte)0x40, (int)SliceSize).ToArray();
|
||||
mixed[^1] = 0xff;
|
||||
harness.Write(metadata + 2 * SliceSize, mixed);
|
||||
harness.Write(metadata + 3 * SliceSize, Enumerable.Repeat((byte)0xff, (int)SliceSize).ToArray());
|
||||
|
||||
Assert.True(harness.Images.TryReadGuestDccClear(metadata, SliceSize, 0, out var slice, out var code));
|
||||
Assert.Equal(metadata, slice);
|
||||
Assert.Equal(0x80, code);
|
||||
Assert.True(harness.Images.TryReadGuestDccClear(metadata, SliceSize, 1, out slice, out code));
|
||||
Assert.Equal(metadata + SliceSize, slice);
|
||||
Assert.Equal(0x00, code);
|
||||
Assert.False(harness.Images.TryReadGuestDccClear(metadata, SliceSize, 2, out _, out _));
|
||||
Assert.False(harness.Images.TryReadGuestDccClear(metadata, SliceSize, 3, out _, out _));
|
||||
Assert.False(harness.Images.TryReadGuestDccClear(metadata, 0, 0, out _, out _));
|
||||
|
||||
harness.Worker.Run(() => harness.Cache.FillBuffer(metadata, SliceSize, uint.MaxValue, isGds: false));
|
||||
Assert.False(harness.Images.TryReadGuestDccClear(metadata, SliceSize, 0, out _, out _));
|
||||
Assert.True(harness.Images.TryReadGuestDccClear(metadata, SliceSize, 1, out _, out _));
|
||||
harness.Shutdown();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Unregister_DropsMetadataInTheRange()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -259,6 +259,28 @@ public sealed class ImageDescriptionTests
|
|||
Reject(association, "stencil-association image", fatal);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(2560u, 1440u, 8u, 1u, 1u, 0x1E000ul)]
|
||||
[InlineData(1920u, 1080u, 4u, 1u, 1u, 0xC000ul)]
|
||||
[InlineData(3840u, 2160u, 4u, 1u, 1u, 0x28000ul)]
|
||||
[InlineData(640u, 360u, 16u, 1u, 1u, 0x6000ul)]
|
||||
[InlineData(1920u, 1080u, 12u, 1u, 1u, 0ul)]
|
||||
[InlineData(1920u, 1080u, 4u, 2u, 1u, 0ul)]
|
||||
[InlineData(1920u, 1080u, 4u, 1u, 2u, 0ul)]
|
||||
public void DccSliceSize_CoversOneMetadataBytePerColorBlock(uint width, uint height, uint bytesPerElement, uint levels, uint samples, ulong expected)
|
||||
{
|
||||
var description = Color(Base, 0x100, width, height, levels);
|
||||
description.BytesPerBlock = bytesPerElement;
|
||||
description.Samples = samples;
|
||||
description.TileMode = GuestTileMode.RenderTarget;
|
||||
description.Metadata.Kind = MetadataKind.Dcc;
|
||||
description.Metadata.Range = new GuestSpan(0x200000, 0);
|
||||
Assert.Equal(expected, description.DccSliceSize);
|
||||
|
||||
description.TileMode = GuestTileMode.Linear;
|
||||
Assert.Equal(0ul, description.DccSliceSize);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Predicates_ReadTheDescription()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue