[GPU] Materialize guest DCC clears in color and sampled images
This commit is contained in:
parent
8df819acc2
commit
dccdc2d6fd
|
|
@ -46,18 +46,20 @@ public static partial class ImageRequestBuilders
|
|||
{
|
||||
ClearColorValue value = default;
|
||||
var supported = hasDcc && PackedClearValue.TryDecodeColor(format, packedClear, out value);
|
||||
var fixedSupported = hasDcc && format switch
|
||||
{
|
||||
Format.R8Unorm or Format.R8G8Unorm or Format.R8G8B8A8Unorm or Format.R8G8B8A8Srgb or Format.B8G8R8A8Unorm or Format.B8G8R8A8Srgb or
|
||||
Format.A2B10G10R10UnormPack32 or Format.A2R10G10B10UnormPack32 or Format.R5G6B5UnormPack16 or Format.A1R5G5B5UnormPack16 or
|
||||
Format.R4G4B4A4UnormPack16 or Format.R16Unorm or Format.R16G16Unorm or Format.R16G16B16A16Unorm or Format.R16Sfloat or
|
||||
Format.R16G16Sfloat or Format.R16G16B16A16Sfloat or Format.R32Sfloat or Format.R32G32Sfloat or Format.R32G32B32A32Sfloat or
|
||||
Format.B10G11R11UfloatPack32 => true,
|
||||
_ => false,
|
||||
};
|
||||
var fixedSupported = hasDcc && SupportsDccFixedClear(format);
|
||||
return (supported, fixedSupported, supported ? value : default);
|
||||
}
|
||||
|
||||
public static bool SupportsDccFixedClear(Format format) => format switch
|
||||
{
|
||||
Format.R8Unorm or Format.R8G8Unorm or Format.R8G8B8A8Unorm or Format.R8G8B8A8Srgb or Format.B8G8R8A8Unorm or Format.B8G8R8A8Srgb or
|
||||
Format.A2B10G10R10UnormPack32 or Format.A2R10G10B10UnormPack32 or Format.R5G6B5UnormPack16 or Format.A1R5G5B5UnormPack16 or
|
||||
Format.R4G4B4A4UnormPack16 or Format.R16Unorm or Format.R16G16Unorm or Format.R16G16B16A16Unorm or Format.R16Sfloat or
|
||||
Format.R16G16Sfloat or Format.R16G16B16A16Sfloat or Format.R32Sfloat or Format.R32G32Sfloat or Format.R32G32B32A32Sfloat or
|
||||
Format.B10G11R11UfloatPack32 => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// Builds the request for a bound color target. Null when the slot carries no target.
|
||||
public static ColorTargetResolution? ColorTarget(in ColorTargetWords words, uint targetMask, uint drawLayerOffset, bool ignoreTargetMask)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Collections.Concurrent;
|
|||
using SharpEmu.Libs.Gpu;
|
||||
using SharpEmu.Libs.Gpu.Buffers;
|
||||
using SharpEmu.Libs.Gpu.Images;
|
||||
using SharpEmu.Libs.Gpu.Rendering;
|
||||
using SharpEmu.Libs.Gpu.Scheduling;
|
||||
using SharpEmu.Libs.Gpu.Vulkan;
|
||||
using Silk.NET.Vulkan;
|
||||
|
|
@ -287,53 +288,14 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
var metadataAddress = target.Request.Description.Metadata.Range.Address;
|
||||
if (!_imageCache.IsMetadataCleared(metadataAddress, view.BaseLayer, out var metadataValue))
|
||||
{
|
||||
return false;
|
||||
return ResolveGuestDccAttachmentClear(target, out clearValue);
|
||||
}
|
||||
|
||||
var resolution = target.Resolution;
|
||||
switch ((byte)metadataValue)
|
||||
if (!TryDecodeDccClear((byte)metadataValue, resolution.MetadataClearSupported, resolution.MetadataFixedClearSupported,
|
||||
resolution.ColorClearValue, out clearValue))
|
||||
{
|
||||
case 0x00:
|
||||
break;
|
||||
case 0x20:
|
||||
if (!resolution.MetadataClearSupported)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
clearValue = resolution.ColorClearValue;
|
||||
break;
|
||||
case 0x40:
|
||||
if (!resolution.MetadataFixedClearSupported)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
clearValue.Float32_3 = 1f;
|
||||
break;
|
||||
case 0x80:
|
||||
if (!resolution.MetadataFixedClearSupported)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
clearValue.Float32_0 = 1f;
|
||||
clearValue.Float32_1 = 1f;
|
||||
clearValue.Float32_2 = 1f;
|
||||
break;
|
||||
case 0xc0:
|
||||
if (!resolution.MetadataFixedClearSupported)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
clearValue.Float32_0 = 1f;
|
||||
clearValue.Float32_1 = 1f;
|
||||
clearValue.Float32_2 = 1f;
|
||||
clearValue.Float32_3 = 1f;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint layer = 1; layer < view.LayerCount; layer++)
|
||||
|
|
@ -353,9 +315,104 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
}
|
||||
}
|
||||
|
||||
ConsumeGuestDccClears(target.Request.Description, view.BaseLayer, view.LayerCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ResolveGuestDccAttachmentClear(ColorAttachment target, out ClearColorValue clearValue)
|
||||
{
|
||||
clearValue = default;
|
||||
var description = target.Request.Description;
|
||||
var sliceSize = description.DccSliceSize;
|
||||
var view = target.Request.View;
|
||||
if (sliceSize == 0 || view.LayerCount == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var metadataAddress = description.Metadata.Range.Address;
|
||||
var slices = new ulong[view.LayerCount];
|
||||
byte clearCode = 0;
|
||||
for (uint layer = 0; layer < view.LayerCount; layer++)
|
||||
{
|
||||
if (!_imageCache.TryReadGuestDccClear(metadataAddress, sliceSize, view.BaseLayer + layer, out slices[layer], out var code) ||
|
||||
(layer != 0 && code != clearCode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
clearCode = code;
|
||||
}
|
||||
|
||||
var resolution = target.Resolution;
|
||||
if (!TryDecodeDccClear(clearCode, resolution.MetadataClearSupported, resolution.MetadataFixedClearSupported,
|
||||
resolution.ColorClearValue, out clearValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var slice in slices)
|
||||
{
|
||||
_bufferCache.FillBuffer(slice, sliceSize, uint.MaxValue, false);
|
||||
}
|
||||
|
||||
if (RenderTrace.Enabled && RenderTrace.MetadataClear())
|
||||
{
|
||||
RenderTrace.Write(
|
||||
$"Materialized a guest DCC clear on a color target: metadata=0x{metadataAddress:X16} code=0x{clearCode:X2} " +
|
||||
$"layers={view.BaseLayer}+{view.LayerCount} slice=0x{sliceSize:X} format={description.PixelFormat}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ConsumeGuestDccClears(in ImageDescription description, uint baseLayer, uint layerCount)
|
||||
{
|
||||
var sliceSize = description.DccSliceSize;
|
||||
if (sliceSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint layer = 0; layer < layerCount; layer++)
|
||||
{
|
||||
if (_imageCache.TryReadGuestDccClear(description.Metadata.Range.Address, sliceSize, baseLayer + layer, out var slice, out _))
|
||||
{
|
||||
_bufferCache.FillBuffer(slice, sliceSize, uint.MaxValue, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryDecodeDccClear(byte code, bool registerClearSupported, bool fixedClearSupported, ClearColorValue registerClear,
|
||||
out ClearColorValue clearValue)
|
||||
{
|
||||
clearValue = default;
|
||||
switch (code)
|
||||
{
|
||||
case 0x00:
|
||||
return true;
|
||||
case 0x20:
|
||||
clearValue = registerClear;
|
||||
return registerClearSupported;
|
||||
case 0x40:
|
||||
clearValue.Float32_3 = 1f;
|
||||
return fixedClearSupported;
|
||||
case 0x80:
|
||||
clearValue.Float32_0 = 1f;
|
||||
clearValue.Float32_1 = 1f;
|
||||
clearValue.Float32_2 = 1f;
|
||||
return fixedClearSupported;
|
||||
case 0xc0:
|
||||
clearValue.Float32_0 = 1f;
|
||||
clearValue.Float32_1 = 1f;
|
||||
clearValue.Float32_2 = 1f;
|
||||
clearValue.Float32_3 = 1f;
|
||||
return fixedClearSupported;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private DepthAttachment? DiscoverDepthTarget(GuestDepthTarget target)
|
||||
{
|
||||
if (target.Registers is not { } words || ImageRequestBuilders.DepthTarget(words, _deviceInfo) is not { } resolution)
|
||||
|
|
|
|||
|
|
@ -236,6 +236,12 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
public ResourceSlotIdentifier FindImage(ref ImageRequest request, bool exactFormat)
|
||||
{
|
||||
_ = BeginBatchedGuestCommands();
|
||||
if (request.Role == ImageRole.ColorTarget && request.Description.DccSliceSize is var sliceSize and not 0)
|
||||
{
|
||||
_imageCache.SynchronizeGuestDccMetadata(request.Description.Metadata.Range.Address, sliceSize,
|
||||
request.View.BaseLayer, request.View.LayerCount);
|
||||
}
|
||||
|
||||
return _imageCache.FindImage(ref request, exactFormat);
|
||||
}
|
||||
|
||||
|
|
@ -534,8 +540,8 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
// A DCC fast clear stays pending until the surface binds as a color target. A shader that
|
||||
// samples or writes the surface first must see the cleared contents, and the later bind must
|
||||
// not clear over its writes: Astro Bot's save-slot cards were drawn by compute into a
|
||||
// fast-cleared target, then wiped black by the deferred clear. Only the zero clear code is
|
||||
// materialized here; the register clear color is known only when the surface is a target.
|
||||
// fast-cleared target, then wiped black by the deferred clear. The register clear color is
|
||||
// known only when the surface is a target.
|
||||
private void RecordSampledColorMetadataClears(TextureResource[] bindings)
|
||||
{
|
||||
const byte DccClearToZero = 0x00;
|
||||
|
|
@ -547,10 +553,17 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
}
|
||||
|
||||
var metadataAddress = image.Description.Metadata.Range.Address;
|
||||
var sliceSize = image.Description.DccSliceSize;
|
||||
var fixedClearSupported = ImageRequestBuilders.SupportsDccFixedClear(image.Description.PixelFormat);
|
||||
var view = binding.Request.View;
|
||||
for (var layer = view.BaseLayer; layer < view.BaseLayer + view.LayerCount; layer++)
|
||||
{
|
||||
if (!_imageCache.IsMetadataCleared(metadataAddress, layer, out var metadataValue) || (byte)metadataValue != DccClearToZero)
|
||||
var tracked = _imageCache.IsMetadataCleared(metadataAddress, layer, out var metadataValue) && (byte)metadataValue == DccClearToZero;
|
||||
var clearValue = default(ClearColorValue);
|
||||
ulong guestSlice = 0;
|
||||
if (!tracked &&
|
||||
(sliceSize == 0 || !_imageCache.TryReadGuestDccClear(metadataAddress, sliceSize, layer, out guestSlice, out var code) ||
|
||||
!TryDecodeDccClear(code, false, fixedClearSupported, default, out clearValue)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -560,12 +573,26 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
var range = new SubresourceRange(0, 1, layer, 1);
|
||||
image.Transition(ImageLayout.TransferDstOptimal, AccessFlags.TransferWriteBit, range, command);
|
||||
var vkRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, 1, layer, 1);
|
||||
var clearValue = default(ClearColorValue);
|
||||
_vk.CmdClearColorImage(command, image.Backing.Handle, ImageLayout.TransferDstOptimal, &clearValue, 1, &vkRange);
|
||||
if (!tracked)
|
||||
{
|
||||
_bufferCache.FillBuffer(guestSlice, sliceSize, uint.MaxValue, false);
|
||||
if (RenderTrace.Enabled && RenderTrace.MetadataClear())
|
||||
{
|
||||
RenderTrace.Write(
|
||||
$"Materialized a guest DCC clear on a sampled image: metadata=0x{metadataAddress:X16} layer={layer} " +
|
||||
$"slice=0x{sliceSize:X} format={image.Description.PixelFormat}");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_imageCache.SetMetadataSlice(metadataAddress, layer, false))
|
||||
{
|
||||
throw SubmissionScheduler.Fatal($"The DCC clear state could not be consumed: metadata=0x{metadataAddress:X16} layer={layer}.");
|
||||
}
|
||||
|
||||
ConsumeGuestDccClears(image.Description, layer, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue