mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-20 18:06:12 +08:00
[shader-decoder] Fix address calculation for SW linear textures (#45)
This commit is contained in:
@@ -142,6 +142,7 @@ public static class AgcExports
|
|||||||
private const ulong ShaderSpecialGeUserVgprEnOffset = 0x28;
|
private const ulong ShaderSpecialGeUserVgprEnOffset = 0x28;
|
||||||
private const uint CbSetShRegisterRangeMarker = 0x6875000D;
|
private const uint CbSetShRegisterRangeMarker = 0x6875000D;
|
||||||
private static readonly object _submitTraceGate = new();
|
private static readonly object _submitTraceGate = new();
|
||||||
|
private static readonly object _textureHashTraceGate = new();
|
||||||
private static readonly HashSet<uint> _tracedDcbSizes = new();
|
private static readonly HashSet<uint> _tracedDcbSizes = new();
|
||||||
private static readonly HashSet<(ulong Es, ulong Ps, GuestDrawKind Kind)> _tracedShaderTranslations = new();
|
private static readonly HashSet<(ulong Es, ulong Ps, GuestDrawKind Kind)> _tracedShaderTranslations = new();
|
||||||
private static readonly HashSet<(ulong Es, ulong Ps)> _tracedShaderDecodePairs = new();
|
private static readonly HashSet<(ulong Es, ulong Ps)> _tracedShaderDecodePairs = new();
|
||||||
@@ -149,6 +150,7 @@ public static class AgcExports
|
|||||||
private static readonly HashSet<(ulong Ps, string Error)> _tracedShaderFailures = new();
|
private static readonly HashSet<(ulong Ps, string Error)> _tracedShaderFailures = new();
|
||||||
private static readonly HashSet<(int Handle, int Index, ulong Address, string Path)> _tracedDisplayBuffers = new();
|
private static readonly HashSet<(int Handle, int Index, ulong Address, string Path)> _tracedDisplayBuffers = new();
|
||||||
private static readonly HashSet<ulong> _tracedComputeShaders = new();
|
private static readonly HashSet<ulong> _tracedComputeShaders = new();
|
||||||
|
private static readonly Dictionary<(ulong Address, uint Width, uint Height), ulong> _tracedTextureHashes = [];
|
||||||
private static readonly HashSet<uint> _tracedSubmittedDrawOpcodes = new();
|
private static readonly HashSet<uint> _tracedSubmittedDrawOpcodes = new();
|
||||||
private static readonly Dictionary<(ulong Ps, ulong State, Gen5PixelOutputKind Output), byte[]> _pixelSpirvCache = new();
|
private static readonly Dictionary<(ulong Ps, ulong State, Gen5PixelOutputKind Output), byte[]> _pixelSpirvCache = new();
|
||||||
private static readonly Dictionary<
|
private static readonly Dictionary<
|
||||||
@@ -168,6 +170,10 @@ public static class AgcExports
|
|||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC_SHADER"),
|
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC_SHADER"),
|
||||||
"1",
|
"1",
|
||||||
StringComparison.Ordinal);
|
StringComparison.Ordinal);
|
||||||
|
private static readonly bool _traceTextureHashes = string.Equals(
|
||||||
|
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_TEXTURE_HASHES"),
|
||||||
|
"1",
|
||||||
|
StringComparison.Ordinal);
|
||||||
private static long _dcbWriteDataTraceCount;
|
private static long _dcbWriteDataTraceCount;
|
||||||
private static long _dcbWaitRegMemTraceCount;
|
private static long _dcbWaitRegMemTraceCount;
|
||||||
private static long _createShaderTraceCount;
|
private static long _createShaderTraceCount;
|
||||||
@@ -3747,7 +3753,6 @@ public static class AgcExports
|
|||||||
var sourceWidth = descriptor.TileMode == 0
|
var sourceWidth = descriptor.TileMode == 0
|
||||||
? GetLinearTexturePitch(
|
? GetLinearTexturePitch(
|
||||||
Math.Max(descriptor.Width, descriptor.Pitch),
|
Math.Max(descriptor.Width, descriptor.Pitch),
|
||||||
descriptor.Height,
|
|
||||||
descriptor.Format)
|
descriptor.Format)
|
||||||
: descriptor.Width;
|
: descriptor.Width;
|
||||||
var sourceByteCount = GetTextureByteCount(
|
var sourceByteCount = GetTextureByteCount(
|
||||||
@@ -3764,7 +3769,7 @@ public static class AgcExports
|
|||||||
|
|
||||||
if (!isStorage &&
|
if (!isStorage &&
|
||||||
descriptor.Address != 0 &&
|
descriptor.Address != 0 &&
|
||||||
VulkanVideoPresenter.IsGuestImageAvailable(
|
VulkanVideoPresenter.IsGpuGuestImageAvailable(
|
||||||
descriptor.Address,
|
descriptor.Address,
|
||||||
descriptor.Format,
|
descriptor.Format,
|
||||||
descriptor.NumberType))
|
descriptor.NumberType))
|
||||||
@@ -3825,6 +3830,8 @@ public static class AgcExports
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TraceTextureHash(descriptor, source);
|
||||||
|
|
||||||
var nonZero = 0;
|
var nonZero = 0;
|
||||||
for (var i = 0; i < source.Length; i++)
|
for (var i = 0; i < source.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -3884,6 +3891,34 @@ public static class AgcExports
|
|||||||
MipLevel: 0);
|
MipLevel: 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void TraceTextureHash(TextureDescriptor descriptor, ReadOnlySpan<byte> source)
|
||||||
|
{
|
||||||
|
if (!_traceTextureHashes ||
|
||||||
|
descriptor.Address == 0 ||
|
||||||
|
descriptor.Width > 256 ||
|
||||||
|
descriptor.Height > 256)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = ComputeFingerprint(source);
|
||||||
|
var key = (descriptor.Address, descriptor.Width, descriptor.Height);
|
||||||
|
lock (_textureHashTraceGate)
|
||||||
|
{
|
||||||
|
if (_tracedTextureHashes.TryGetValue(key, out var previousHash) &&
|
||||||
|
previousHash == hash)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tracedTextureHashes[key] = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[LOADER][TRACE] agc.texture_hash addr=0x{descriptor.Address:X16} " +
|
||||||
|
$"size={descriptor.Width}x{descriptor.Height} bytes={source.Length} hash=0x{hash:X16}");
|
||||||
|
}
|
||||||
|
|
||||||
private static VulkanGuestSampler ToVulkanSampler(IReadOnlyList<uint> descriptor) =>
|
private static VulkanGuestSampler ToVulkanSampler(IReadOnlyList<uint> descriptor) =>
|
||||||
descriptor.Count >= 4
|
descriptor.Count >= 4
|
||||||
? new VulkanGuestSampler(
|
? new VulkanGuestSampler(
|
||||||
@@ -4527,23 +4562,17 @@ public static class AgcExports
|
|||||||
: checked(((ulong)width + 3) / 4 * (((ulong)height + 3) / 4) * blockBytes);
|
: checked(((ulong)width + 3) / 4 * (((ulong)height + 3) / 4) * blockBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint GetLinearTexturePitch(uint pitch, uint height, uint format)
|
private static uint GetLinearTexturePitch(uint pitch, uint format)
|
||||||
{
|
{
|
||||||
var bytesPerTexel = GetTextureBytesPerTexel(format);
|
var bytesPerTexel = GetTextureBytesPerTexel(format);
|
||||||
if (bytesPerTexel == 0 || height == 0)
|
if (bytesPerTexel == 0)
|
||||||
{
|
{
|
||||||
return pitch;
|
return pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pitchAlignment = Math.Max(8UL, 64UL / bytesPerTexel);
|
// GFX10 ADDR_SW_LINEAR aligns each row to 256 bytes.
|
||||||
var alignedPitch = AlignUp(pitch, pitchAlignment);
|
var pitchAlignment = Math.Max(1UL, 256UL / bytesPerTexel);
|
||||||
var sliceAlignment = Math.Max(64UL, 256UL / bytesPerTexel);
|
return checked((uint)AlignUp(pitch, pitchAlignment));
|
||||||
while ((alignedPitch * height) % sliceAlignment != 0)
|
|
||||||
{
|
|
||||||
alignedPitch += pitchAlignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
return checked((uint)alignedPitch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong AlignUp(ulong value, ulong alignment) =>
|
private static ulong AlignUp(ulong value, ulong alignment) =>
|
||||||
@@ -4833,9 +4862,13 @@ public static class AgcExports
|
|||||||
var type = (fields[3] >> 28) & 0xFu;
|
var type = (fields[3] >> 28) & 0xFu;
|
||||||
var baseLevel = (fields[3] >> 12) & 0xFu;
|
var baseLevel = (fields[3] >> 12) & 0xFu;
|
||||||
var lastLevel = (fields[3] >> 16) & 0xFu;
|
var lastLevel = (fields[3] >> 16) & 0xFu;
|
||||||
var pitch = fields.Count >= 5
|
// The 128-bit RDNA2 2D resource derives pitch[12:0] from width;
|
||||||
? ((fields[4] >> 13) & 0x3FFFu) + 1
|
// the optional extension word only supplies pitch[13].
|
||||||
: width;
|
var pitch = width;
|
||||||
|
if (fields.Count >= 5)
|
||||||
|
{
|
||||||
|
pitch = ((((width - 1) & 0x1FFFu) | (((fields[4] >> 13) & 1u) << 13)) + 1);
|
||||||
|
}
|
||||||
var dstSelect = fields[3] & 0xFFFu;
|
var dstSelect = fields[3] & 0xFFFu;
|
||||||
if (address == 0 || width == 0 || height == 0)
|
if (address == 0 || width == 0 || height == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private static readonly object _gate = new();
|
private static readonly object _gate = new();
|
||||||
private static readonly Queue<object> _pendingGuestWork = new();
|
private static readonly Queue<object> _pendingGuestWork = new();
|
||||||
private static readonly Dictionary<ulong, uint> _availableGuestImages = new();
|
private static readonly Dictionary<ulong, uint> _availableGuestImages = new();
|
||||||
|
private static readonly Dictionary<ulong, uint> _gpuGuestImages = new();
|
||||||
private static readonly HashSet<(ulong Address, uint Width, uint Height)>
|
private static readonly HashSet<(ulong Address, uint Width, uint Height)>
|
||||||
_tracedGuestImageSubmissions = [];
|
_tracedGuestImageSubmissions = [];
|
||||||
private static Thread? _thread;
|
private static Thread? _thread;
|
||||||
@@ -600,7 +601,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsGuestImageAvailable(
|
internal static bool IsGpuGuestImageAvailable(
|
||||||
ulong address,
|
ulong address,
|
||||||
uint format,
|
uint format,
|
||||||
uint numberType)
|
uint numberType)
|
||||||
@@ -613,7 +614,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
|
|
||||||
lock (_gate)
|
lock (_gate)
|
||||||
{
|
{
|
||||||
return _availableGuestImages.TryGetValue(address, out var availableFormat) &&
|
return _gpuGuestImages.TryGetValue(address, out var availableFormat) &&
|
||||||
availableFormat == guestFormat;
|
availableFormat == guestFormat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -971,6 +972,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
public VulkanGuestSampler SamplerState;
|
public VulkanGuestSampler SamplerState;
|
||||||
public Sampler Sampler;
|
public Sampler Sampler;
|
||||||
public GuestImageResource? GuestImage;
|
public GuestImageResource? GuestImage;
|
||||||
|
public ulong CpuContentFingerprint;
|
||||||
|
public bool UpdatesCpuContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class GlobalBufferResource
|
private sealed class GlobalBufferResource
|
||||||
@@ -1009,6 +1012,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
public Framebuffer Framebuffer;
|
public Framebuffer Framebuffer;
|
||||||
public bool Initialized;
|
public bool Initialized;
|
||||||
public bool InitialUploadPending;
|
public bool InitialUploadPending;
|
||||||
|
public bool IsCpuBacked;
|
||||||
|
public ulong CpuContentFingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed record PendingGuestSubmission(
|
private sealed record PendingGuestSubmission(
|
||||||
@@ -2838,6 +2843,11 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
$"tile={texture.TileMode} format={vkFormat}");
|
$"tile={texture.TileMode} format={vkFormat}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TryCreateCpuTextureRefreshResource(texture, guestImage, view, out var refresh))
|
||||||
|
{
|
||||||
|
return refresh;
|
||||||
|
}
|
||||||
|
|
||||||
return new TextureResource
|
return new TextureResource
|
||||||
{
|
{
|
||||||
Address = texture.Address,
|
Address = texture.Address,
|
||||||
@@ -2855,6 +2865,71 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return CreateTextureResource(texture);
|
return CreateTextureResource(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryCreateCpuTextureRefreshResource(
|
||||||
|
VulkanGuestDrawTexture texture,
|
||||||
|
GuestImageResource guestImage,
|
||||||
|
ImageView view,
|
||||||
|
out TextureResource resource)
|
||||||
|
{
|
||||||
|
resource = default!;
|
||||||
|
if (!guestImage.IsCpuBacked ||
|
||||||
|
guestImage.Width != texture.Width ||
|
||||||
|
guestImage.Height != texture.Height ||
|
||||||
|
guestImage.MipLevels != 1 ||
|
||||||
|
texture.RgbaPixels.Length == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rowLength = texture.TileMode == 0
|
||||||
|
? Math.Max(texture.Pitch, texture.Width)
|
||||||
|
: texture.Width;
|
||||||
|
var expectedSize = GetTextureByteCount(texture.Format, rowLength, texture.Height);
|
||||||
|
if (expectedSize == 0 || expectedSize > int.MaxValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pixels = texture.RgbaPixels.Length == (int)expectedSize
|
||||||
|
? texture.RgbaPixels
|
||||||
|
: CreateFallbackTexturePixels(texture.Format, rowLength, texture.Height, expectedSize);
|
||||||
|
var fingerprint = ComputeTextureContentFingerprint(pixels);
|
||||||
|
if ((guestImage.Initialized || guestImage.InitialUploadPending) &&
|
||||||
|
guestImage.CpuContentFingerprint == fingerprint)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uploadPixels = texture.Format == 13
|
||||||
|
? ExpandRgb32Pixels(pixels)
|
||||||
|
: pixels;
|
||||||
|
var debugName = TextureDebugName(texture, guestImage.Format);
|
||||||
|
var (stagingBuffer, stagingMemory) = CreateTextureStagingBuffer(
|
||||||
|
uploadPixels,
|
||||||
|
$"{debugName} refresh staging");
|
||||||
|
TraceVulkanShader(
|
||||||
|
$"vk.texture_refresh addr=0x{texture.Address:X16} " +
|
||||||
|
$"size={texture.Width}x{texture.Height} bytes={uploadPixels.Length}");
|
||||||
|
resource = new TextureResource
|
||||||
|
{
|
||||||
|
Address = texture.Address,
|
||||||
|
StagingBuffer = stagingBuffer,
|
||||||
|
StagingMemory = stagingMemory,
|
||||||
|
Image = guestImage.Image,
|
||||||
|
View = view,
|
||||||
|
Width = guestImage.Width,
|
||||||
|
Height = guestImage.Height,
|
||||||
|
RowLength = rowLength,
|
||||||
|
DstSelect = texture.DstSelect,
|
||||||
|
NeedsUpload = true,
|
||||||
|
SamplerState = texture.Sampler,
|
||||||
|
GuestImage = guestImage,
|
||||||
|
CpuContentFingerprint = fingerprint,
|
||||||
|
UpdatesCpuContent = true,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsCompatibleGuestImageAlias(
|
private static bool IsCompatibleGuestImageAlias(
|
||||||
VulkanGuestDrawTexture texture,
|
VulkanGuestDrawTexture texture,
|
||||||
GuestImageResource guestImage)
|
GuestImageResource guestImage)
|
||||||
@@ -3096,24 +3171,11 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
var uploadPixels = texture.Format == 13
|
var uploadPixels = texture.Format == 13
|
||||||
? ExpandRgb32Pixels(pixels)
|
? ExpandRgb32Pixels(pixels)
|
||||||
: pixels;
|
: pixels;
|
||||||
var uploadSize = (ulong)uploadPixels.Length;
|
var contentFingerprint = ComputeTextureContentFingerprint(pixels);
|
||||||
|
|
||||||
var stagingBuffer = CreateBuffer(
|
var (stagingBuffer, stagingMemory) = CreateTextureStagingBuffer(
|
||||||
uploadSize,
|
uploadPixels,
|
||||||
BufferUsageFlags.TransferSrcBit,
|
$"{TextureDebugName(texture, vkFormat)} staging");
|
||||||
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
|
||||||
out var stagingMemory);
|
|
||||||
void* mapped;
|
|
||||||
Check(_vk.MapMemory(_device, stagingMemory, 0, uploadSize, 0, &mapped), "vkMapMemory(texture)");
|
|
||||||
fixed (byte* source = uploadPixels)
|
|
||||||
{
|
|
||||||
System.Buffer.MemoryCopy(
|
|
||||||
source,
|
|
||||||
mapped,
|
|
||||||
uploadPixels.Length,
|
|
||||||
uploadPixels.Length);
|
|
||||||
}
|
|
||||||
_vk.UnmapMemory(_device, stagingMemory);
|
|
||||||
|
|
||||||
var supportsAttachmentUsage = !IsBlockCompressedFormat(vkFormat);
|
var supportsAttachmentUsage = !IsBlockCompressedFormat(vkFormat);
|
||||||
var imageInfo = new ImageCreateInfo
|
var imageInfo = new ImageCreateInfo
|
||||||
@@ -3163,7 +3225,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
};
|
};
|
||||||
Check(_vk.CreateImageView(_device, &viewInfo, null, out var view), "vkCreateImageView(texture)");
|
Check(_vk.CreateImageView(_device, &viewInfo, null, out var view), "vkCreateImageView(texture)");
|
||||||
var debugName = TextureDebugName(texture, vkFormat);
|
var debugName = TextureDebugName(texture, vkFormat);
|
||||||
SetDebugName(ObjectType.Buffer, stagingBuffer.Handle, $"{debugName} staging");
|
|
||||||
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
||||||
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
||||||
var resource = new TextureResource
|
var resource = new TextureResource
|
||||||
@@ -3181,6 +3242,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
NeedsUpload = true,
|
NeedsUpload = true,
|
||||||
OwnsStorage = true,
|
OwnsStorage = true,
|
||||||
SamplerState = texture.Sampler,
|
SamplerState = texture.Sampler,
|
||||||
|
CpuContentFingerprint = contentFingerprint,
|
||||||
|
UpdatesCpuContent = texture.Address != 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (texture.Address != 0 &&
|
if (texture.Address != 0 &&
|
||||||
@@ -3197,6 +3260,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
Memory = imageMemory,
|
Memory = imageMemory,
|
||||||
View = view,
|
View = view,
|
||||||
InitialUploadPending = true,
|
InitialUploadPending = true,
|
||||||
|
IsCpuBacked = true,
|
||||||
|
CpuContentFingerprint = contentFingerprint,
|
||||||
};
|
};
|
||||||
_guestImages.Add(texture.Address, guestImage);
|
_guestImages.Add(texture.Address, guestImage);
|
||||||
resource.OwnsStorage = false;
|
resource.OwnsStorage = false;
|
||||||
@@ -3209,6 +3274,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
if (guestFormat != 0)
|
if (guestFormat != 0)
|
||||||
{
|
{
|
||||||
_availableGuestImages[texture.Address] = guestFormat;
|
_availableGuestImages[texture.Address] = guestFormat;
|
||||||
|
_gpuGuestImages.Remove(texture.Address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3216,6 +3282,41 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (VkBuffer Buffer, DeviceMemory Memory) CreateTextureStagingBuffer(
|
||||||
|
byte[] pixels,
|
||||||
|
string debugName)
|
||||||
|
{
|
||||||
|
var size = (ulong)pixels.Length;
|
||||||
|
var buffer = CreateBuffer(
|
||||||
|
size,
|
||||||
|
BufferUsageFlags.TransferSrcBit,
|
||||||
|
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
||||||
|
out var memory);
|
||||||
|
void* mapped;
|
||||||
|
Check(_vk.MapMemory(_device, memory, 0, size, 0, &mapped), "vkMapMemory(texture)");
|
||||||
|
fixed (byte* source = pixels)
|
||||||
|
{
|
||||||
|
System.Buffer.MemoryCopy(source, mapped, pixels.Length, pixels.Length);
|
||||||
|
}
|
||||||
|
_vk.UnmapMemory(_device, memory);
|
||||||
|
SetDebugName(ObjectType.Buffer, buffer.Handle, debugName);
|
||||||
|
return (buffer, memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong ComputeTextureContentFingerprint(ReadOnlySpan<byte> pixels)
|
||||||
|
{
|
||||||
|
const ulong offsetBasis = 14695981039346656037;
|
||||||
|
const ulong prime = 1099511628211;
|
||||||
|
var hash = offsetBasis;
|
||||||
|
foreach (var value in pixels)
|
||||||
|
{
|
||||||
|
hash ^= value;
|
||||||
|
hash *= prime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
private void DumpTextureUpload(
|
private void DumpTextureUpload(
|
||||||
VulkanGuestDrawTexture texture,
|
VulkanGuestDrawTexture texture,
|
||||||
byte[] pixels,
|
byte[] pixels,
|
||||||
@@ -4267,12 +4368,13 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
RecordTextureUploads(resources, PipelineStageFlags.FragmentShaderBit);
|
RecordTextureUploads(resources, PipelineStageFlags.FragmentShaderBit);
|
||||||
RecordStorageImagesForWrite(resources, PipelineStageFlags.FragmentShaderBit);
|
RecordStorageImagesForWrite(resources, PipelineStageFlags.FragmentShaderBit);
|
||||||
|
|
||||||
|
var targetHasPriorContents = target.Initialized || target.InitialUploadPending;
|
||||||
var toColorAttachment = new ImageMemoryBarrier
|
var toColorAttachment = new ImageMemoryBarrier
|
||||||
{
|
{
|
||||||
SType = StructureType.ImageMemoryBarrier,
|
SType = StructureType.ImageMemoryBarrier,
|
||||||
SrcAccessMask = target.Initialized ? AccessFlags.ShaderReadBit : 0,
|
SrcAccessMask = targetHasPriorContents ? AccessFlags.ShaderReadBit : 0,
|
||||||
DstAccessMask = AccessFlags.ColorAttachmentWriteBit,
|
DstAccessMask = AccessFlags.ColorAttachmentWriteBit,
|
||||||
OldLayout = target.Initialized
|
OldLayout = targetHasPriorContents
|
||||||
? ImageLayout.ShaderReadOnlyOptimal
|
? ImageLayout.ShaderReadOnlyOptimal
|
||||||
: ImageLayout.Undefined,
|
: ImageLayout.Undefined,
|
||||||
NewLayout = ImageLayout.ColorAttachmentOptimal,
|
NewLayout = ImageLayout.ColorAttachmentOptimal,
|
||||||
@@ -4283,8 +4385,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
};
|
};
|
||||||
_vk.CmdPipelineBarrier(
|
_vk.CmdPipelineBarrier(
|
||||||
_commandBuffer,
|
_commandBuffer,
|
||||||
target.Initialized
|
targetHasPriorContents
|
||||||
? PipelineStageFlags.FragmentShaderBit
|
? PipelineStageFlags.AllCommandsBit
|
||||||
: PipelineStageFlags.TopOfPipeBit,
|
: PipelineStageFlags.TopOfPipeBit,
|
||||||
PipelineStageFlags.ColorAttachmentOutputBit,
|
PipelineStageFlags.ColorAttachmentOutputBit,
|
||||||
0,
|
0,
|
||||||
@@ -4337,12 +4439,15 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
MarkSampledImagesInitialized(resources);
|
MarkSampledImagesInitialized(resources);
|
||||||
MarkStorageImagesInitialized(resources, traceContents: false);
|
MarkStorageImagesInitialized(resources, traceContents: false);
|
||||||
|
|
||||||
var guestTextureFormat = GetGuestTextureFormat(target.Format);
|
var guestTextureFormat = VulkanVideoPresenter.GetGuestTextureFormat(
|
||||||
|
work.Target.Format,
|
||||||
|
work.Target.NumberType);
|
||||||
if (work.PublishTarget && guestTextureFormat != 0)
|
if (work.PublishTarget && guestTextureFormat != 0)
|
||||||
{
|
{
|
||||||
lock (_gate)
|
lock (_gate)
|
||||||
{
|
{
|
||||||
_availableGuestImages[target.Address] = guestTextureFormat;
|
_availableGuestImages[target.Address] = guestTextureFormat;
|
||||||
|
_gpuGuestImages[target.Address] = guestTextureFormat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ShouldTraceGuestImageWriteForDiagnostics(target.Address))
|
if (ShouldTraceGuestImageWriteForDiagnostics(target.Address))
|
||||||
@@ -4384,6 +4489,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
lock (_gate)
|
lock (_gate)
|
||||||
{
|
{
|
||||||
_availableGuestImages.Remove(work.Target.Address);
|
_availableGuestImages.Remove(work.Target.Address);
|
||||||
|
_gpuGuestImages.Remove(work.Target.Address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4423,6 +4529,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
existing.MipLevels == mipLevels &&
|
existing.MipLevels == mipLevels &&
|
||||||
existing.Format == format)
|
existing.Format == format)
|
||||||
{
|
{
|
||||||
|
existing.IsCpuBacked = false;
|
||||||
|
existing.CpuContentFingerprint = 0;
|
||||||
if (existing.RenderPass.Handle == 0)
|
if (existing.RenderPass.Handle == 0)
|
||||||
{
|
{
|
||||||
var attachmentView = existing.MipViews.Length > 0
|
var attachmentView = existing.MipViews.Length > 0
|
||||||
@@ -4448,6 +4556,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
lock (_gate)
|
lock (_gate)
|
||||||
{
|
{
|
||||||
_availableGuestImages.Remove(target.Address);
|
_availableGuestImages.Remove(target.Address);
|
||||||
|
_gpuGuestImages.Remove(target.Address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5386,11 +5495,18 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasPriorContents = texture.GuestImage is { } guestImage &&
|
||||||
|
(guestImage.Initialized || guestImage.InitialUploadPending);
|
||||||
var toTransfer = new ImageMemoryBarrier
|
var toTransfer = new ImageMemoryBarrier
|
||||||
{
|
{
|
||||||
SType = StructureType.ImageMemoryBarrier,
|
SType = StructureType.ImageMemoryBarrier,
|
||||||
|
SrcAccessMask = hasPriorContents
|
||||||
|
? AccessFlags.ShaderReadBit
|
||||||
|
: 0,
|
||||||
DstAccessMask = AccessFlags.TransferWriteBit,
|
DstAccessMask = AccessFlags.TransferWriteBit,
|
||||||
OldLayout = ImageLayout.Undefined,
|
OldLayout = hasPriorContents
|
||||||
|
? ImageLayout.ShaderReadOnlyOptimal
|
||||||
|
: ImageLayout.Undefined,
|
||||||
NewLayout = ImageLayout.TransferDstOptimal,
|
NewLayout = ImageLayout.TransferDstOptimal,
|
||||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||||
@@ -5399,7 +5515,9 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
};
|
};
|
||||||
_vk.CmdPipelineBarrier(
|
_vk.CmdPipelineBarrier(
|
||||||
_commandBuffer,
|
_commandBuffer,
|
||||||
PipelineStageFlags.TopOfPipeBit,
|
hasPriorContents
|
||||||
|
? PipelineStageFlags.AllCommandsBit
|
||||||
|
: PipelineStageFlags.TopOfPipeBit,
|
||||||
PipelineStageFlags.TransferBit,
|
PipelineStageFlags.TransferBit,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -5569,6 +5687,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
if (format != 0)
|
if (format != 0)
|
||||||
{
|
{
|
||||||
_availableGuestImages[texture.Address] = format;
|
_availableGuestImages[texture.Address] = format;
|
||||||
|
_gpuGuestImages[texture.Address] = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (traceContents &&
|
if (traceContents &&
|
||||||
@@ -5608,6 +5727,10 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
|
|
||||||
guestImage.Initialized = true;
|
guestImage.Initialized = true;
|
||||||
guestImage.InitialUploadPending = false;
|
guestImage.InitialUploadPending = false;
|
||||||
|
if (texture.UpdatesCpuContent)
|
||||||
|
{
|
||||||
|
guestImage.CpuContentFingerprint = texture.CpuContentFingerprint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6379,6 +6502,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
lock (_gate)
|
lock (_gate)
|
||||||
{
|
{
|
||||||
_availableGuestImages.Clear();
|
_availableGuestImages.Clear();
|
||||||
|
_gpuGuestImages.Clear();
|
||||||
}
|
}
|
||||||
DestroySwapchainResources();
|
DestroySwapchainResources();
|
||||||
if (_device.Handle != 0)
|
if (_device.Handle != 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user