[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:
Berk
2026-07-07 21:06:04 +03:00
committed by GitHub
parent 44c43c8ebd
commit db20387502
3 changed files with 409 additions and 123 deletions
+15 -8
View File
@@ -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
+94 -38
View File
@@ -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)