[GPU] Refresh the stencil plane through the depth image (#989)
This commit is contained in:
parent
b480391a7b
commit
a424a67c1c
|
|
@ -586,7 +586,7 @@ public sealed partial class GuestImageCache
|
|||
return replacementImageIdentifier;
|
||||
}
|
||||
|
||||
private void AssociateStencilRange(ResourceSlotIdentifier depthImageIdentifier, GuestSpan stencil)
|
||||
private ResourceSlotIdentifier AssociateStencilRange(ResourceSlotIdentifier depthImageIdentifier, GuestSpan stencil)
|
||||
{
|
||||
if (!ImageDescription.IsValidRange(stencil))
|
||||
{
|
||||
|
|
@ -620,5 +620,6 @@ public sealed partial class GuestImageCache
|
|||
var record = _slots[association];
|
||||
TouchImage(record);
|
||||
record.AssociateDepth(depthImageIdentifier);
|
||||
return association;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ public sealed partial class GuestImageCache
|
|||
foreach (var imageIdentifier in FindImagesInRange(address, size, pageOverlap: true))
|
||||
{
|
||||
var owner = _slots.TryGet(imageIdentifier);
|
||||
if (owner == null || owner.DepthOwner.IsValid)
|
||||
if (owner == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -467,7 +467,7 @@ public sealed partial class GuestImageCache
|
|||
foreach (var imageIdentifier in FindImagesInRange(address, size, pageOverlap: true))
|
||||
{
|
||||
var image = _slots[imageIdentifier];
|
||||
if (image.DepthOwner.IsValid || !image.Overlaps(address, size))
|
||||
if (!image.Overlaps(address, size))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -496,14 +496,9 @@ public sealed partial class GuestImageCache
|
|||
foreach (var imageIdentifier in FindImagesInRange(address, size, pageOverlap: true))
|
||||
{
|
||||
var image = _slots[imageIdentifier];
|
||||
if (image.DepthOwner.IsValid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
imagePages = true;
|
||||
imageBytes |= image.Overlaps(address, size);
|
||||
gpuImageBytes |= image.GpuOverlaps(address, size);
|
||||
gpuImageBytes |= !image.DepthOwner.IsValid && image.GpuOverlaps(address, size);
|
||||
}
|
||||
|
||||
return new ImageRegionInfo(imagePages, imageBytes, gpuImageBytes);
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ public sealed unsafe partial class GuestImageCache
|
|||
return tiles;
|
||||
}
|
||||
|
||||
private static BufferImageCopy[] DepthCopies(in ImageDescription info, ulong sliceSize)
|
||||
private static BufferImageCopy[] DepthCopies(in ImageDescription info, ulong sliceSize, ImageAspectFlags aspect = ImageAspectFlags.DepthBit)
|
||||
{
|
||||
var copies = new BufferImageCopy[info.Resources.Layers];
|
||||
for (uint layer = 0; layer < info.Resources.Layers; layer++)
|
||||
|
|
@ -200,7 +200,7 @@ public sealed unsafe partial class GuestImageCache
|
|||
BufferOffset = sliceSize * layer,
|
||||
BufferRowLength = info.Pitch,
|
||||
BufferImageHeight = info.Extent.Height,
|
||||
ImageSubresource = new ImageSubresourceLayers(ImageAspectFlags.DepthBit, 0, layer, 1),
|
||||
ImageSubresource = new ImageSubresourceLayers(aspect, 0, layer, 1),
|
||||
ImageExtent = new Extent3D(info.Extent.Width, info.Extent.Height, 1),
|
||||
};
|
||||
}
|
||||
|
|
@ -230,8 +230,44 @@ public sealed unsafe partial class GuestImageCache
|
|||
image.UploadFromBuffer(System.Runtime.InteropServices.CollectionsMarshal.AsSpan(copies), linear.Buffer, linear.Offset, linear.Size);
|
||||
}
|
||||
|
||||
private void UploadStencilPlane(CachedImage association, GpuBuffer source, ulong sourceOffset)
|
||||
{
|
||||
var destination = _slots[association.DepthOwner];
|
||||
var info = destination.Description;
|
||||
info.Data = association.Description.Data;
|
||||
info.GuestFormat = GuestPixelFormat.Bits8UInt;
|
||||
info.BytesPerBlock = 1;
|
||||
if (info.IsTiled)
|
||||
{
|
||||
info.Pitch = TileGeometry.DepthPitch(info.Extent.Width, 1);
|
||||
}
|
||||
|
||||
if (info.Samples != 1 || destination.Backing.Samples != 1 || info.Resources.Layers == 0 || info.Data.Size % info.Resources.Layers != 0)
|
||||
{
|
||||
throw SubmissionScheduler.Fatal(
|
||||
$"The stencil upload is invalid: address=0x{info.Data.Address:X16} size=0x{info.Data.Size:X} layers={info.Resources.Layers} samples={info.Samples} backingSamples={destination.Backing.Samples}.");
|
||||
}
|
||||
|
||||
var fullSliceSize = info.Data.Size / info.Resources.Layers;
|
||||
var copies = DepthCopies(info, fullSliceSize, ImageAspectFlags.StencilBit);
|
||||
var linear = new TilerBufferSpan(source.Handle, sourceOffset, source.Size - sourceOffset);
|
||||
if (info.IsTiled)
|
||||
{
|
||||
var tiles = DepthTileTransfers(info, DepthBlock(info), fullSliceSize);
|
||||
linear = _tiler.Detile(source.Handle, sourceOffset, info.Data.Size, info.Data.Size, System.Runtime.InteropServices.CollectionsMarshal.AsSpan(tiles));
|
||||
}
|
||||
|
||||
UploadRegions(destination, copies.ToList(), linear);
|
||||
}
|
||||
|
||||
private void UploadFromBuffer(CachedImage image, in ImageRequest request, GpuBuffer source, ulong sourceOffset)
|
||||
{
|
||||
if (image.DepthOwner.IsValid)
|
||||
{
|
||||
UploadStencilPlane(image, source, sourceOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
ref readonly var info = ref image.Description;
|
||||
if (request.Role != ImageRole.DepthTarget)
|
||||
{
|
||||
|
|
@ -830,6 +866,11 @@ public sealed unsafe partial class GuestImageCache
|
|||
}
|
||||
|
||||
TakeGpuOwnership(image);
|
||||
if (aspect == ImageAspectFlags.StencilBit)
|
||||
{
|
||||
TakeStencilOwnership(selected, image);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -378,15 +378,27 @@ public sealed unsafe partial class GuestImageCache : IGuestImageCache, IGuestIma
|
|||
}
|
||||
}
|
||||
|
||||
TakeGpuOwnership(image);
|
||||
if (request.Description.HasStencil)
|
||||
{
|
||||
AssociateStencilRange(imageIdentifier, request.Description.Stencil);
|
||||
image.Description.Stencil = request.Description.Stencil;
|
||||
RefreshStencilPlane(imageIdentifier, image, request.Description.Metadata.StencilCompressed);
|
||||
}
|
||||
|
||||
TakeGpuOwnership(image);
|
||||
return image.GetOrCreateView(request.View);
|
||||
}
|
||||
|
||||
private void RefreshStencilPlane(ResourceSlotIdentifier depthIdentifier, CachedImage depth, bool stencilCompressed)
|
||||
{
|
||||
var association = AssociateStencilRange(depthIdentifier, depth.Description.Stencil);
|
||||
if (stencilCompressed || depth.Description.Samples != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshFromGuest(association, RefreshRequest(_slots[association]));
|
||||
}
|
||||
|
||||
public void MarkGpuWritten(ResourceSlotIdentifier imageIdentifier)
|
||||
{
|
||||
using var held = _lock.Hold();
|
||||
|
|
@ -398,13 +410,24 @@ public sealed unsafe partial class GuestImageCache : IGuestImageCache, IGuestIma
|
|||
|
||||
WatchImage(imageIdentifier);
|
||||
TakeGpuOwnership(image);
|
||||
if (image.Description.HasStencil)
|
||||
{
|
||||
TakeStencilOwnership(imageIdentifier, image);
|
||||
}
|
||||
}
|
||||
|
||||
private void TakeStencilOwnership(ResourceSlotIdentifier depthIdentifier, CachedImage depth)
|
||||
{
|
||||
var association = AssociateStencilRange(depthIdentifier, depth.Description.Stencil);
|
||||
WatchImage(association);
|
||||
TakeGpuOwnership(_slots[association]);
|
||||
}
|
||||
|
||||
private static void TakeGpuOwnership(CachedImage image)
|
||||
{
|
||||
if (image.DepthOwner.IsValid || !image.Backing.Exists)
|
||||
if (!image.DepthOwner.IsValid && !image.Backing.Exists)
|
||||
{
|
||||
throw SubmissionScheduler.Fatal($"A stencil association cannot own image contents: address=0x{image.Description.Data.Address:X16}.");
|
||||
throw SubmissionScheduler.Fatal($"GPU ownership needs a native image or a stencil association: address=0x{image.Description.Data.Address:X16}.");
|
||||
}
|
||||
|
||||
image.ClearBufferModified();
|
||||
|
|
|
|||
|
|
@ -543,4 +543,69 @@ public sealed unsafe partial class GuestImageCacheTests
|
|||
Assert.Equal(Bytes((ushort)0x0000, (ushort)0x2468, (ushort)0xabcd, (ushort)0xffff), harness.ReadImageBytes(harness.Image(storageId)));
|
||||
harness.Shutdown();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StencilPlaneWrites_ReachTheCombinedAttachment()
|
||||
{
|
||||
if (!GatePrerequisites.Ready(_vulkan)) return;
|
||||
var stencilFormat = SupportedStencilFormat(_vulkan, SampleCountFlags.Count1Bit);
|
||||
if (stencilFormat == Format.Undefined) return;
|
||||
using var harness = new CacheHarness(_vulkan);
|
||||
var address = harness.MapBacked(0x10000, ReadWrite);
|
||||
var depthAddress = address + 0xc000;
|
||||
var stencilAddress = address + 0xd000;
|
||||
harness.Write(depthAddress, Bytes((ushort)0x1111, (ushort)0x2222, (ushort)0x3333, (ushort)0x4444));
|
||||
harness.Write(stencilAddress, [0x56, 0x56, 0x00, 0x56]);
|
||||
var depth = LinearRequest(depthAddress, 8, stencilFormat, GuestPixelFormat.Bits16UNorm, GuestImageType.Color2D, new Extent3D(4, 1, 1), 1, 2, 1);
|
||||
depth = AsDepthTarget(depth, stencilFormat);
|
||||
depth.Description.Stencil = new GuestSpan(stencilAddress, 4);
|
||||
depth.View = depth.View with { Aspect = ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit };
|
||||
byte[] Stencil(ResourceSlotIdentifier id) => harness.ReadImageBytes(harness.Image(id), ImageAspectFlags.StencilBit)[..4];
|
||||
|
||||
var depthId = harness.Acquire(ref depth);
|
||||
Assert.Equal(new byte[] { 0x56, 0x56, 0x00, 0x56 }, Stencil(depthId));
|
||||
|
||||
harness.Write(stencilAddress, [0x01, 0x02, 0x03, 0x04]);
|
||||
Assert.True(harness.WriteFault(stencilAddress));
|
||||
Assert.Equal(depthId, harness.Acquire(ref depth));
|
||||
Assert.Equal(new byte[] { 0x01, 0x02, 0x03, 0x04 }, Stencil(depthId));
|
||||
|
||||
harness.Worker.Run(() => harness.Cache.FillBuffer(stencilAddress, 4, 0, false));
|
||||
Assert.Equal(depthId, harness.Acquire(ref depth));
|
||||
Assert.Equal(new byte[4], Stencil(depthId));
|
||||
|
||||
harness.Write(stencilAddress, [0x09, 0x09, 0x09, 0x09]);
|
||||
Assert.True(harness.Worker.Run(() => harness.Images.TryClearImageFromBuffer(stencilAddress, 4, 0x07070707)));
|
||||
Assert.Equal(depthId, harness.Acquire(ref depth));
|
||||
Assert.Equal(new byte[] { 0x07, 0x07, 0x07, 0x07 }, Stencil(depthId));
|
||||
harness.Shutdown();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TiledStencilPlaneWrites_ReachTheCombinedAttachment()
|
||||
{
|
||||
if (!GatePrerequisites.Ready(_vulkan)) return;
|
||||
const ImageUsageFlags usage = ImageUsageFlags.TransferSrcBit | ImageUsageFlags.TransferDstBit | ImageUsageFlags.DepthStencilAttachmentBit;
|
||||
if (!_vulkan.DeviceInfo.TryGetImageFormatProperties(Format.D32SfloatS8Uint, ImageType.Type2D, ImageTiling.Optimal, usage, 0, out _)) return;
|
||||
using var harness = new CacheHarness(_vulkan);
|
||||
const ulong planeSize = 0x10000;
|
||||
const int texels = 128 * 128;
|
||||
var address = harness.MapBacked(planeSize * 3, ReadWrite);
|
||||
var stencilAddress = address + planeSize;
|
||||
harness.Write(stencilAddress, Enumerable.Repeat((byte)0x5a, (int)planeSize).ToArray());
|
||||
var depth = LinearRequest(address, planeSize, Format.D32SfloatS8Uint, GuestPixelFormat.Bits32Float, GuestImageType.Color2D, new Extent3D(128, 128, 1), 1, 4, 1);
|
||||
depth.Description.TileMode = GuestTileMode.Depth;
|
||||
depth = AsDepthTarget(depth, Format.D32SfloatS8Uint);
|
||||
depth.Description.Stencil = new GuestSpan(stencilAddress, planeSize);
|
||||
depth.View = depth.View with { Aspect = ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit };
|
||||
byte[] Stencil(ResourceSlotIdentifier id) => harness.ReadImageBytes(harness.Image(id), ImageAspectFlags.StencilBit)[..texels];
|
||||
|
||||
var depthId = harness.Acquire(ref depth);
|
||||
Assert.All(Stencil(depthId), value => Assert.Equal(0x5a, value));
|
||||
|
||||
harness.Worker.Run(() => harness.Cache.FillBuffer(stencilAddress, planeSize, 0, false));
|
||||
Assert.Equal(depthId, harness.Acquire(ref depth));
|
||||
Assert.All(Stencil(depthId), value => Assert.Equal(0, value));
|
||||
harness.Shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue