mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-19 09:26:16 +08:00
[shader-decoder-part1-hotfix] New format support, maintenance8 support, robustness improvements, and bug fixes. This commit includes updates to the AGC exports, Gen5 SPIR-V translator, and Vulkan video presenter to enhance compatibility and performance. (#31)
This commit is contained in:
@@ -3609,7 +3609,7 @@ public static class AgcExports
|
||||
',',
|
||||
draw.GlobalMemoryBindings.Select((binding, index) =>
|
||||
$"{index}:0x{binding.BaseAddress:X16}:{binding.Data.Length}:" +
|
||||
Convert.ToHexString(binding.Data.AsSpan(0, Math.Min(binding.Data.Length, 32)))));
|
||||
Convert.ToHexString(binding.Data.AsSpan(0, Math.Min(binding.Data.Length, 256)))));
|
||||
var indices = draw.IndexBuffer is { } indexBuffer
|
||||
? $"{(indexBuffer.Is32Bit ? 32 : 16)}:" +
|
||||
Convert.ToHexString(indexBuffer.Data.AsSpan(0, Math.Min(indexBuffer.Data.Length, 32)))
|
||||
@@ -3744,7 +3744,7 @@ public static class AgcExports
|
||||
descriptor.Width > 8192 ||
|
||||
descriptor.Height > 8192)
|
||||
{
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage);
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3762,7 +3762,7 @@ public static class AgcExports
|
||||
sourceByteCount > MaxPresentedTextureBytes ||
|
||||
sourceByteCount > int.MaxValue)
|
||||
{
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage);
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3825,7 +3825,7 @@ public static class AgcExports
|
||||
var source = new byte[(int)sourceByteCount];
|
||||
if (!ctx.Memory.TryRead(descriptor.Address, source))
|
||||
{
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage);
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3868,18 +3868,25 @@ public static class AgcExports
|
||||
return true;
|
||||
}
|
||||
|
||||
private static VulkanGuestDrawTexture CreateFallbackGuestDrawTexture(bool isStorage = false) =>
|
||||
new(
|
||||
private static VulkanGuestDrawTexture CreateFallbackGuestDrawTexture(
|
||||
bool isStorage,
|
||||
uint format,
|
||||
uint numberType)
|
||||
{
|
||||
var fallbackFormat = format == 0 ? 10u : format;
|
||||
var fallbackNumberType = numberType;
|
||||
return new(
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
56,
|
||||
NumberType: 0,
|
||||
fallbackFormat,
|
||||
fallbackNumberType,
|
||||
[0, 0, 0, 255],
|
||||
IsFallback: true,
|
||||
IsStorage: isStorage,
|
||||
MipLevels: 1,
|
||||
MipLevel: 0);
|
||||
}
|
||||
|
||||
private static VulkanGuestSampler ToVulkanSampler(IReadOnlyList<uint> descriptor) =>
|
||||
descriptor.Count >= 4
|
||||
|
||||
@@ -467,9 +467,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
_imageBindingByPc.TryAdd(binding.Pc, index);
|
||||
var isStorage =
|
||||
Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode);
|
||||
var isDepth =
|
||||
binding.Opcode.Contains("SampleC", StringComparison.Ordinal) ||
|
||||
binding.Opcode.Contains("Gather4C", StringComparison.Ordinal);
|
||||
var (format, componentKind) =
|
||||
DecodeImageFormat(binding.ResourceDescriptor);
|
||||
var componentType = componentKind switch
|
||||
@@ -494,7 +491,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
var imageType = _module.TypeImage(
|
||||
componentType,
|
||||
SpirvImageDim.Dim2D,
|
||||
depth: isDepth,
|
||||
depth: false,
|
||||
arrayed: false,
|
||||
multisampled: false,
|
||||
sampled: isStorage ? 2u : 1u,
|
||||
@@ -1661,18 +1658,14 @@ internal static partial class Gen5SpirvTranslator
|
||||
var offset = hasOffset ? BuildImageOffset(image, 0) : 0u;
|
||||
var imageOperands =
|
||||
(explicitLod ? 2u : 0u) | (hasOffset ? 0x10u : 0u);
|
||||
var reference = hasCompare
|
||||
? Bitcast(_floatType, LoadV(image.GetAddressRegister(hasOffset ? 1 : 0)))
|
||||
: 0u;
|
||||
var operands = new List<uint>
|
||||
{
|
||||
imageObject,
|
||||
coordinates,
|
||||
};
|
||||
if (hasCompare)
|
||||
{
|
||||
operands.Add(
|
||||
Bitcast(
|
||||
_floatType,
|
||||
LoadV(image.GetAddressRegister(hasOffset ? 1 : 0))));
|
||||
}
|
||||
|
||||
if (imageOperands != 0)
|
||||
{
|
||||
@@ -1689,29 +1682,14 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
sampled = _module.AddInstruction(
|
||||
hasCompare
|
||||
? explicitLod
|
||||
? SpirvOp.ImageSampleDrefExplicitLod
|
||||
: SpirvOp.ImageSampleDrefImplicitLod
|
||||
: explicitLod
|
||||
? SpirvOp.ImageSampleExplicitLod
|
||||
: SpirvOp.ImageSampleImplicitLod,
|
||||
hasCompare ? resource.ComponentType : resource.VectorType,
|
||||
explicitLod
|
||||
? SpirvOp.ImageSampleExplicitLod
|
||||
: SpirvOp.ImageSampleImplicitLod,
|
||||
resource.VectorType,
|
||||
[.. operands]);
|
||||
if (hasCompare)
|
||||
{
|
||||
var scalar = sampled;
|
||||
sampled = _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
resource.VectorType,
|
||||
scalar,
|
||||
scalar,
|
||||
scalar,
|
||||
resource.ComponentKind == ImageComponentKind.Float
|
||||
? Float(1)
|
||||
: resource.ComponentKind == ImageComponentKind.Uint
|
||||
? UInt(1)
|
||||
: _module.Constant(_intType, 1));
|
||||
sampled = EmitManualDepthCompare(resource, sampled, reference);
|
||||
}
|
||||
}
|
||||
else if (instruction.Opcode.StartsWith(
|
||||
@@ -1725,6 +1703,9 @@ internal static partial class Gen5SpirvTranslator
|
||||
var start = (hasOffset ? 1 : 0) + (hasCompare ? 1 : 0);
|
||||
var coordinates = BuildFloatCoordinates(image, start);
|
||||
var offset = hasOffset ? BuildImageOffset(image, 0) : 0u;
|
||||
var reference = hasCompare
|
||||
? Bitcast(_floatType, LoadV(image.GetAddressRegister(hasOffset ? 1 : 0)))
|
||||
: 0u;
|
||||
var operands = new List<uint>
|
||||
{
|
||||
imageObject,
|
||||
@@ -1732,10 +1713,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
};
|
||||
if (hasCompare)
|
||||
{
|
||||
operands.Add(
|
||||
Bitcast(
|
||||
_floatType,
|
||||
LoadV(image.GetAddressRegister(hasOffset ? 1 : 0))));
|
||||
operands.Add(UInt(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1756,11 +1734,28 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
sampled = _module.AddInstruction(
|
||||
hasCompare
|
||||
? SpirvOp.ImageDrefGather
|
||||
: SpirvOp.ImageGather,
|
||||
SpirvOp.ImageGather,
|
||||
resource.VectorType,
|
||||
[.. operands]);
|
||||
if (hasCompare)
|
||||
{
|
||||
var compared = new uint[4];
|
||||
for (var component = 0u; component < 4; component++)
|
||||
{
|
||||
var texel = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
resource.ComponentType,
|
||||
sampled,
|
||||
component);
|
||||
compared[component] = EmitDepthCompareScalar(resource, texel, reference);
|
||||
}
|
||||
|
||||
sampled = _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
resource.VectorType,
|
||||
compared);
|
||||
}
|
||||
|
||||
writeAllComponents = true;
|
||||
}
|
||||
else
|
||||
@@ -1794,6 +1789,67 @@ internal static partial class Gen5SpirvTranslator
|
||||
return true;
|
||||
}
|
||||
|
||||
private uint EmitDepthCompareScalar(
|
||||
SpirvImageResource resource,
|
||||
uint texel,
|
||||
uint reference)
|
||||
{
|
||||
var texelAsFloat = resource.ComponentKind switch
|
||||
{
|
||||
ImageComponentKind.Uint => _module.AddInstruction(
|
||||
SpirvOp.ConvertUToF, _floatType, texel),
|
||||
ImageComponentKind.Sint => _module.AddInstruction(
|
||||
SpirvOp.ConvertSToF, _floatType, texel),
|
||||
_ => texel,
|
||||
};
|
||||
var passes = _module.AddInstruction(
|
||||
SpirvOp.FOrdLessThanEqual,
|
||||
_boolType,
|
||||
reference,
|
||||
texelAsFloat);
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
resource.ComponentType,
|
||||
passes,
|
||||
resource.ComponentKind switch
|
||||
{
|
||||
ImageComponentKind.Uint => UInt(1),
|
||||
ImageComponentKind.Sint => _module.Constant(_intType, 1),
|
||||
_ => Float(1),
|
||||
},
|
||||
resource.ComponentKind switch
|
||||
{
|
||||
ImageComponentKind.Uint => UInt(0),
|
||||
ImageComponentKind.Sint => _module.Constant(_intType, 0),
|
||||
_ => Float(0),
|
||||
});
|
||||
}
|
||||
|
||||
private uint EmitManualDepthCompare(
|
||||
SpirvImageResource resource,
|
||||
uint sampledVector,
|
||||
uint reference)
|
||||
{
|
||||
var texel = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
resource.ComponentType,
|
||||
sampledVector,
|
||||
0u);
|
||||
var scalar = EmitDepthCompareScalar(resource, texel, reference);
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
resource.VectorType,
|
||||
scalar,
|
||||
scalar,
|
||||
scalar,
|
||||
resource.ComponentKind switch
|
||||
{
|
||||
ImageComponentKind.Uint => UInt(1),
|
||||
ImageComponentKind.Sint => _module.Constant(_intType, 1),
|
||||
_ => Float(1),
|
||||
});
|
||||
}
|
||||
|
||||
private uint BuildFloatCoordinates(Gen5ImageControl image, int start)
|
||||
{
|
||||
var x = Bitcast(
|
||||
|
||||
@@ -1459,8 +1459,20 @@ internal static unsafe class VulkanVideoPresenter
|
||||
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
|
||||
FragmentStoresAndAtomics = supportedFeatures.FragmentStoresAndAtomics,
|
||||
ShaderInt64 = supportedFeatures.ShaderInt64,
|
||||
ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended,
|
||||
ShaderStorageImageExtendedFormats = supportedFeatures.ShaderStorageImageExtendedFormats,
|
||||
ShaderStorageImageReadWithoutFormat = supportedFeatures.ShaderStorageImageReadWithoutFormat,
|
||||
ShaderStorageImageWriteWithoutFormat = supportedFeatures.ShaderStorageImageWriteWithoutFormat,
|
||||
RobustBufferAccess = supportedFeatures.RobustBufferAccess,
|
||||
};
|
||||
|
||||
if (!supportedFeatures.RobustBufferAccess)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
"[LOADER][WARN] GPU does not support robustBufferAccess " +
|
||||
"translated shaders performing out-of-bounds buffer access may cause device loss.");
|
||||
}
|
||||
|
||||
if (!supportedFeatures.ShaderInt64)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
@@ -1474,18 +1486,96 @@ internal static unsafe class VulkanVideoPresenter
|
||||
"[LOADER][WARN] GPU does not support vertexPipelineStoresAndAtomics/fragmentStoresAndAtomics " +
|
||||
"translated shaders using storage buffers in vertex/fragment stages may fail.");
|
||||
}
|
||||
|
||||
|
||||
if (!supportedFeatures.ShaderImageGatherExtended)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
"[LOADER][WARN] GPU does not support shaderImageGatherExtended " +
|
||||
"translated shaders using image gather with offsets/LOD/bias will fail.");
|
||||
}
|
||||
|
||||
if (!supportedFeatures.ShaderStorageImageReadWithoutFormat ||
|
||||
!supportedFeatures.ShaderStorageImageWriteWithoutFormat)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
"[LOADER][WARN] GPU does not support shaderStorageImage(Read|Write)WithoutFormat " +
|
||||
"translated shaders using unformatted storage image load/store will fail.");
|
||||
}
|
||||
|
||||
var maintenance8Features = new PhysicalDeviceMaintenance8FeaturesKHR
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceMaintenance8FeaturesKhr,
|
||||
};
|
||||
var robustness2Features = new PhysicalDeviceRobustness2FeaturesEXT
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
|
||||
PNext = &maintenance8Features,
|
||||
};
|
||||
var featuresQuery = new PhysicalDeviceFeatures2
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceFeatures2,
|
||||
PNext = &robustness2Features,
|
||||
};
|
||||
_vk.GetPhysicalDeviceFeatures2(_physicalDevice, &featuresQuery);
|
||||
var supportsMaintenance8 = maintenance8Features.Maintenance8;
|
||||
var supportsRobustImageAccess2 = robustness2Features.RobustImageAccess2;
|
||||
var supportsNullDescriptor = robustness2Features.NullDescriptor;
|
||||
var supportsRobustness2 = supportsRobustImageAccess2 || supportsNullDescriptor;
|
||||
if (!supportsMaintenance8)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
"[LOADER][WARN] GPU does not support VK_KHR_maintenance8 " +
|
||||
"translated shaders using a dynamic texel offset on non-gather image samples will fail.");
|
||||
}
|
||||
|
||||
if (!supportsRobustImageAccess2)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
"[LOADER][WARN] GPU does not support VK_EXT_robustness2 robustImageAccess2 " +
|
||||
"translated shaders performing out-of-bounds image access may cause device loss.");
|
||||
}
|
||||
|
||||
var swapchainExtension = (byte*)SilkMarshal.StringToPtr("VK_KHR_swapchain");
|
||||
var maintenance8Extension = (byte*)SilkMarshal.StringToPtr("VK_KHR_maintenance8");
|
||||
var robustness2Extension = (byte*)SilkMarshal.StringToPtr("VK_EXT_robustness2");
|
||||
try
|
||||
{
|
||||
var extensions = stackalloc byte*[3];
|
||||
var extensionCount = 0u;
|
||||
extensions[extensionCount++] = swapchainExtension;
|
||||
if (supportsMaintenance8)
|
||||
{
|
||||
extensions[extensionCount++] = maintenance8Extension;
|
||||
}
|
||||
|
||||
if (supportsRobustness2)
|
||||
{
|
||||
extensions[extensionCount++] = robustness2Extension;
|
||||
}
|
||||
|
||||
maintenance8Features.Maintenance8 = supportsMaintenance8;
|
||||
maintenance8Features.PNext = null;
|
||||
robustness2Features.RobustBufferAccess2 =
|
||||
supportsRobustImageAccess2 && supportedFeatures.RobustBufferAccess;
|
||||
robustness2Features.RobustImageAccess2 = supportsRobustImageAccess2;
|
||||
robustness2Features.NullDescriptor = supportsNullDescriptor;
|
||||
robustness2Features.PNext = supportsMaintenance8 ? &maintenance8Features : null;
|
||||
var features2 = new PhysicalDeviceFeatures2
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceFeatures2,
|
||||
PNext = supportsRobustness2
|
||||
? &robustness2Features
|
||||
: (supportsMaintenance8 ? &maintenance8Features : null),
|
||||
Features = enabledFeatures,
|
||||
};
|
||||
var createInfo = new DeviceCreateInfo
|
||||
{
|
||||
SType = StructureType.DeviceCreateInfo,
|
||||
PNext = &features2,
|
||||
QueueCreateInfoCount = 1,
|
||||
PQueueCreateInfos = &queueInfo,
|
||||
EnabledExtensionCount = 1,
|
||||
PpEnabledExtensionNames = &swapchainExtension,
|
||||
PEnabledFeatures = &enabledFeatures,
|
||||
EnabledExtensionCount = extensionCount,
|
||||
PpEnabledExtensionNames = extensions,
|
||||
};
|
||||
|
||||
Check(_vk.CreateDevice(_physicalDevice, &createInfo, null, out _device), "vkCreateDevice");
|
||||
@@ -1493,6 +1583,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
finally
|
||||
{
|
||||
SilkMarshal.Free((nint)swapchainExtension);
|
||||
SilkMarshal.Free((nint)maintenance8Extension);
|
||||
SilkMarshal.Free((nint)robustness2Extension);
|
||||
}
|
||||
|
||||
_vk.GetDeviceQueue(_device, _queueFamilyIndex, 0, out _queue);
|
||||
@@ -1657,6 +1749,39 @@ internal static unsafe class VulkanVideoPresenter
|
||||
resources.DebugName));
|
||||
}
|
||||
|
||||
private void SubmitGuestCommandBufferAndWait(CommandBuffer commandBuffer)
|
||||
{
|
||||
var fenceInfo = new FenceCreateInfo
|
||||
{
|
||||
SType = StructureType.FenceCreateInfo,
|
||||
};
|
||||
Fence fence;
|
||||
Check(
|
||||
_vk.CreateFence(_device, &fenceInfo, null, out fence),
|
||||
"vkCreateFence(guest chunk)");
|
||||
try
|
||||
{
|
||||
var submitInfo = new SubmitInfo
|
||||
{
|
||||
SType = StructureType.SubmitInfo,
|
||||
CommandBufferCount = 1,
|
||||
PCommandBuffers = &commandBuffer,
|
||||
};
|
||||
Check(
|
||||
_vk.QueueSubmit(_queue, 1, &submitInfo, fence),
|
||||
"vkQueueSubmit(guest chunk)");
|
||||
Check(
|
||||
_vk.WaitForFences(_device, 1, &fence, true, ulong.MaxValue),
|
||||
"vkWaitForFences(guest chunk)");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_vk.DestroyFence(_device, fence, null);
|
||||
}
|
||||
|
||||
_vk.FreeCommandBuffers(_device, _commandPool, 1, &commandBuffer);
|
||||
}
|
||||
|
||||
private void EnsureGuestSubmissionCapacity()
|
||||
{
|
||||
CollectCompletedGuestSubmissions(waitForOldest: false);
|
||||
@@ -2641,6 +2766,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
var pipelineInfo = new ComputePipelineCreateInfo
|
||||
{
|
||||
SType = StructureType.ComputePipelineCreateInfo,
|
||||
Flags = PipelineCreateFlags.CreateDispatchBaseBit,
|
||||
Stage = stage,
|
||||
Layout = resources.PipelineLayout,
|
||||
};
|
||||
@@ -2986,9 +3112,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
_vk.UnmapMemory(_device, stagingMemory);
|
||||
|
||||
var supportsAttachmentUsage = !IsBlockCompressedFormat(vkFormat);
|
||||
var imageInfo = new ImageCreateInfo
|
||||
{
|
||||
SType = StructureType.ImageCreateInfo,
|
||||
Flags = supportsAttachmentUsage
|
||||
? ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit
|
||||
: 0,
|
||||
ImageType = ImageType.Type2D,
|
||||
Format = vkFormat,
|
||||
Extent = new Extent3D(width, height, 1),
|
||||
@@ -2996,7 +3126,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
ArrayLayers = 1,
|
||||
Samples = SampleCountFlags.Count1Bit,
|
||||
Tiling = ImageTiling.Optimal,
|
||||
Usage = ImageUsageFlags.TransferDstBit | ImageUsageFlags.SampledBit,
|
||||
Usage = supportsAttachmentUsage
|
||||
? ImageUsageFlags.TransferDstBit |
|
||||
ImageUsageFlags.SampledBit |
|
||||
ImageUsageFlags.ColorAttachmentBit |
|
||||
ImageUsageFlags.StorageBit |
|
||||
ImageUsageFlags.TransferSrcBit
|
||||
: ImageUsageFlags.TransferDstBit | ImageUsageFlags.SampledBit,
|
||||
SharingMode = SharingMode.Exclusive,
|
||||
InitialLayout = ImageLayout.Undefined,
|
||||
};
|
||||
@@ -3831,6 +3967,12 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_ => Format.Undefined,
|
||||
};
|
||||
|
||||
private static bool IsBlockCompressedFormat(Format format) =>
|
||||
format is Format.BC1RgbaUnormBlock or
|
||||
Format.BC1RgbaSrgbBlock or
|
||||
Format.BC7UnormBlock or
|
||||
Format.BC7SrgbBlock;
|
||||
|
||||
private VkBuffer CreateBuffer(
|
||||
ulong size,
|
||||
BufferUsageFlags usage,
|
||||
@@ -3884,8 +4026,19 @@ internal static unsafe class VulkanVideoPresenter
|
||||
throw new InvalidOperationException("No compatible Vulkan host-visible memory type was found.");
|
||||
}
|
||||
|
||||
private const uint MaxComputeZSlicesPerSubmission = 8;
|
||||
|
||||
private void ExecuteComputeDispatch(VulkanComputeGuestDispatch work)
|
||||
{
|
||||
if (AddressListContains("SHARPEMU_SKIP_COMPUTE_CS", work.ShaderAddress))
|
||||
{
|
||||
TraceVulkanShader(
|
||||
$"vk.compute_skip cs=0x{work.ShaderAddress:X16} " +
|
||||
$"groups={work.GroupCountX}x{work.GroupCountY}x{work.GroupCountZ} " +
|
||||
$"textures={work.Textures.Count}");
|
||||
return;
|
||||
}
|
||||
|
||||
TranslatedDrawResources? resources = null;
|
||||
CommandBuffer commandBuffer = default;
|
||||
var submitted = false;
|
||||
@@ -3893,59 +4046,95 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
EnsureGuestSubmissionCapacity();
|
||||
resources = CreateComputeDispatchResources(work);
|
||||
commandBuffer = AllocateGuestCommandBuffer();
|
||||
_commandBuffer = commandBuffer;
|
||||
var beginInfo = new CommandBufferBeginInfo
|
||||
{
|
||||
SType = StructureType.CommandBufferBeginInfo,
|
||||
Flags = CommandBufferUsageFlags.OneTimeSubmitBit,
|
||||
};
|
||||
Check(
|
||||
_vk.BeginCommandBuffer(_commandBuffer, &beginInfo),
|
||||
"vkBeginCommandBuffer(compute)");
|
||||
|
||||
BeginDebugLabel(_commandBuffer, resources.DebugName);
|
||||
RecordTextureUploads(resources, PipelineStageFlags.ComputeShaderBit);
|
||||
RecordStorageImagesForWrite(resources, PipelineStageFlags.ComputeShaderBit);
|
||||
_vk.CmdBindPipeline(
|
||||
_commandBuffer,
|
||||
PipelineBindPoint.Compute,
|
||||
resources.Pipeline);
|
||||
if (resources.DescriptorSet.Handle != 0)
|
||||
var batchCount = Math.Max(
|
||||
1u,
|
||||
(uint)Math.Ceiling(work.GroupCountZ / (double)MaxComputeZSlicesPerSubmission));
|
||||
|
||||
for (var batchIndex = 0u; batchIndex < batchCount; batchIndex++)
|
||||
{
|
||||
var descriptorSet = resources.DescriptorSet;
|
||||
_vk.CmdBindDescriptorSets(
|
||||
var zStart = batchIndex * MaxComputeZSlicesPerSubmission;
|
||||
var zCount = Math.Min(MaxComputeZSlicesPerSubmission, work.GroupCountZ - zStart);
|
||||
var isFirstBatch = batchIndex == 0;
|
||||
var isLastBatch = batchIndex == batchCount - 1;
|
||||
|
||||
commandBuffer = AllocateGuestCommandBuffer();
|
||||
_commandBuffer = commandBuffer;
|
||||
var beginInfo = new CommandBufferBeginInfo
|
||||
{
|
||||
SType = StructureType.CommandBufferBeginInfo,
|
||||
Flags = CommandBufferUsageFlags.OneTimeSubmitBit,
|
||||
};
|
||||
Check(
|
||||
_vk.BeginCommandBuffer(_commandBuffer, &beginInfo),
|
||||
"vkBeginCommandBuffer(compute)");
|
||||
|
||||
BeginDebugLabel(_commandBuffer, resources.DebugName);
|
||||
if (isFirstBatch)
|
||||
{
|
||||
RecordTextureUploads(resources, PipelineStageFlags.ComputeShaderBit);
|
||||
RecordStorageImagesForWrite(resources, PipelineStageFlags.ComputeShaderBit);
|
||||
}
|
||||
|
||||
_vk.CmdBindPipeline(
|
||||
_commandBuffer,
|
||||
PipelineBindPoint.Compute,
|
||||
resources.PipelineLayout,
|
||||
0,
|
||||
1,
|
||||
&descriptorSet,
|
||||
0,
|
||||
null);
|
||||
resources.Pipeline);
|
||||
if (resources.DescriptorSet.Handle != 0)
|
||||
{
|
||||
var descriptorSet = resources.DescriptorSet;
|
||||
_vk.CmdBindDescriptorSets(
|
||||
_commandBuffer,
|
||||
PipelineBindPoint.Compute,
|
||||
resources.PipelineLayout,
|
||||
0,
|
||||
1,
|
||||
&descriptorSet,
|
||||
0,
|
||||
null);
|
||||
}
|
||||
|
||||
RecordChunkedComputeDispatch(_commandBuffer, work, zStart, zCount);
|
||||
|
||||
if (isLastBatch)
|
||||
{
|
||||
RecordStorageImagesForRead(resources, PipelineStageFlags.ComputeShaderBit);
|
||||
}
|
||||
|
||||
EndDebugLabel(_commandBuffer);
|
||||
Check(_vk.EndCommandBuffer(_commandBuffer), "vkEndCommandBuffer(compute)");
|
||||
|
||||
TraceVulkanShader(
|
||||
$"vk.compute_submit cs=0x{work.ShaderAddress:X16} " +
|
||||
$"batch={batchIndex}/{batchCount} z={zStart}..{zStart + zCount}");
|
||||
if (isLastBatch)
|
||||
{
|
||||
SubmitGuestCommandBuffer(
|
||||
commandBuffer,
|
||||
resources,
|
||||
GetTraceImages(resources));
|
||||
submitted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SubmitGuestCommandBufferAndWait(commandBuffer);
|
||||
commandBuffer = default;
|
||||
}
|
||||
}
|
||||
|
||||
RecordChunkedComputeDispatch(_commandBuffer, work);
|
||||
RecordStorageImagesForRead(resources, PipelineStageFlags.ComputeShaderBit);
|
||||
EndDebugLabel(_commandBuffer);
|
||||
|
||||
Check(_vk.EndCommandBuffer(_commandBuffer), "vkEndCommandBuffer(compute)");
|
||||
SubmitGuestCommandBuffer(
|
||||
commandBuffer,
|
||||
resources,
|
||||
GetTraceImages(resources));
|
||||
submitted = true;
|
||||
MarkSampledImagesInitialized(resources);
|
||||
MarkStorageImagesInitialized(resources, traceContents: false);
|
||||
TraceVulkanShader(
|
||||
$"vk.compute_dispatch groups={work.GroupCountX}x" +
|
||||
$"{work.GroupCountY}x{work.GroupCountZ} " +
|
||||
$"textures={work.Textures.Count} cs=0x{work.ShaderAddress:X16}");
|
||||
$"textures={work.Textures.Count} cs=0x{work.ShaderAddress:X16} " +
|
||||
$"batches={batchCount}");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][ERROR] Vulkan compute dispatch failed: {exception.Message}");
|
||||
$"[LOADER][ERROR] Vulkan compute dispatch failed " +
|
||||
$"cs=0x{work.ShaderAddress:X16}: {exception.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -3968,7 +4157,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
|
||||
private void RecordChunkedComputeDispatch(
|
||||
CommandBuffer commandBuffer,
|
||||
VulkanComputeGuestDispatch work)
|
||||
VulkanComputeGuestDispatch work,
|
||||
uint zStart,
|
||||
uint zCount)
|
||||
{
|
||||
const uint maxWorkgroupsPerCommand = 4096;
|
||||
var yChunk = Math.Max(
|
||||
@@ -3978,7 +4169,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
maxWorkgroupsPerCommand / Math.Max(work.GroupCountX, 1u)));
|
||||
var commandCount = 0u;
|
||||
|
||||
for (var z = 0u; z < work.GroupCountZ; z++)
|
||||
for (var z = zStart; z < zStart + zCount; z++)
|
||||
{
|
||||
for (var y = 0u; y < work.GroupCountY; y += yChunk)
|
||||
{
|
||||
@@ -4000,7 +4191,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
TraceVulkanShader(
|
||||
$"vk.compute_chunked cs=0x{work.ShaderAddress:X16} " +
|
||||
$"groups={work.GroupCountX}x{work.GroupCountY}x{work.GroupCountZ} " +
|
||||
$"commands={commandCount} y_chunk={yChunk}");
|
||||
$"z_range={zStart}..{zStart + zCount} commands={commandCount} y_chunk={yChunk}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4208,6 +4399,23 @@ internal static unsafe class VulkanVideoPresenter
|
||||
existing.MipLevels == mipLevels &&
|
||||
existing.Format == format)
|
||||
{
|
||||
if (existing.RenderPass.Handle == 0)
|
||||
{
|
||||
var attachmentView = existing.MipViews.Length > 0
|
||||
? existing.MipViews[0]
|
||||
: existing.View;
|
||||
var (promotedRenderPass, promotedFramebuffer) = CreateRenderPassAndFramebuffer(
|
||||
existing.Format,
|
||||
attachmentView,
|
||||
existing.Width,
|
||||
existing.Height);
|
||||
existing.RenderPass = promotedRenderPass;
|
||||
existing.Framebuffer = promotedFramebuffer;
|
||||
var promotedName = GuestImageDebugName(target, format);
|
||||
SetDebugName(ObjectType.RenderPass, promotedRenderPass.Handle, $"{promotedName} renderpass");
|
||||
SetDebugName(ObjectType.Framebuffer, promotedFramebuffer.Handle, $"{promotedName} framebuffer");
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
@@ -4288,6 +4496,48 @@ internal static unsafe class VulkanVideoPresenter
|
||||
mipViews[mipLevel] = mipView;
|
||||
}
|
||||
|
||||
var (renderPass, framebuffer) = CreateRenderPassAndFramebuffer(
|
||||
format,
|
||||
mipViews[0],
|
||||
target.Width,
|
||||
target.Height);
|
||||
|
||||
var resource = new GuestImageResource
|
||||
{
|
||||
Address = target.Address,
|
||||
Width = target.Width,
|
||||
Height = target.Height,
|
||||
MipLevels = mipLevels,
|
||||
Format = format,
|
||||
Image = image,
|
||||
Memory = memory,
|
||||
View = view,
|
||||
MipViews = mipViews,
|
||||
RenderPass = renderPass,
|
||||
Framebuffer = framebuffer,
|
||||
};
|
||||
var debugName = GuestImageDebugName(target, format);
|
||||
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
||||
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
||||
for (var mipLevel = 0; mipLevel < mipViews.Length; mipLevel++)
|
||||
{
|
||||
SetDebugName(
|
||||
ObjectType.ImageView,
|
||||
mipViews[mipLevel].Handle,
|
||||
$"{debugName} mip{mipLevel}");
|
||||
}
|
||||
SetDebugName(ObjectType.RenderPass, renderPass.Handle, $"{debugName} renderpass");
|
||||
SetDebugName(ObjectType.Framebuffer, framebuffer.Handle, $"{debugName} framebuffer");
|
||||
_guestImages.Add(target.Address, resource);
|
||||
return resource;
|
||||
}
|
||||
|
||||
private (RenderPass RenderPass, Framebuffer Framebuffer) CreateRenderPassAndFramebuffer(
|
||||
Format format,
|
||||
ImageView attachmentView,
|
||||
uint width,
|
||||
uint height)
|
||||
{
|
||||
var colorAttachment = new AttachmentDescription
|
||||
{
|
||||
Format = format,
|
||||
@@ -4322,49 +4572,22 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_vk.CreateRenderPass(_device, &renderPassInfo, null, out var renderPass),
|
||||
"vkCreateRenderPass(offscreen)");
|
||||
|
||||
var attachment = mipViews[0];
|
||||
var attachment = attachmentView;
|
||||
var framebufferInfo = new FramebufferCreateInfo
|
||||
{
|
||||
SType = StructureType.FramebufferCreateInfo,
|
||||
RenderPass = renderPass,
|
||||
AttachmentCount = 1,
|
||||
PAttachments = &attachment,
|
||||
Width = target.Width,
|
||||
Height = target.Height,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Layers = 1,
|
||||
};
|
||||
Check(
|
||||
_vk.CreateFramebuffer(_device, &framebufferInfo, null, out var framebuffer),
|
||||
"vkCreateFramebuffer(offscreen)");
|
||||
|
||||
var resource = new GuestImageResource
|
||||
{
|
||||
Address = target.Address,
|
||||
Width = target.Width,
|
||||
Height = target.Height,
|
||||
MipLevels = mipLevels,
|
||||
Format = format,
|
||||
Image = image,
|
||||
Memory = memory,
|
||||
View = view,
|
||||
MipViews = mipViews,
|
||||
RenderPass = renderPass,
|
||||
Framebuffer = framebuffer,
|
||||
};
|
||||
var debugName = GuestImageDebugName(target, format);
|
||||
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
||||
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
||||
for (var mipLevel = 0; mipLevel < mipViews.Length; mipLevel++)
|
||||
{
|
||||
SetDebugName(
|
||||
ObjectType.ImageView,
|
||||
mipViews[mipLevel].Handle,
|
||||
$"{debugName} mip{mipLevel}");
|
||||
}
|
||||
SetDebugName(ObjectType.RenderPass, renderPass.Handle, $"{debugName} renderpass");
|
||||
SetDebugName(ObjectType.Framebuffer, framebuffer.Handle, $"{debugName} framebuffer");
|
||||
_guestImages.Add(target.Address, resource);
|
||||
return resource;
|
||||
return (renderPass, framebuffer);
|
||||
}
|
||||
|
||||
private static uint ClampMipLevels(uint width, uint height, uint requestedMipLevels)
|
||||
|
||||
Reference in New Issue
Block a user