[Gpu] Backend-neutral shader compiler and guest-GPU renderer seam (#200)

* [ShaderCompiler] Extract the backend-neutral shader compiler project

Move the Gen5 (gfx10) microcode decoder, the scalar evaluator, the
shader IR, and the metadata reader out of SharpEmu.Libs/Agc into a new
SharpEmu.ShaderCompiler project — the half of shader compilation every
codegen backend (SPIR-V today; MSL and DXIL later) consumes. Types go
public: they are the contract now. Nothing in the project may depend on
a host graphics API; the SPIR-V-specific artifact types
(Gen5SpirvShader, Gen5SpirvStage) stay beside the emitter in Libs.

Three couplings surfaced by the move, each resolved at the right depth:
GuestDrawKind was defined inside VulkanVideoPresenter despite being a
guest-domain, decoder-produced concept — it moves to the shared project;
the evaluator's one HLE dependency (the tracked-libc-heap read
fallback) becomes an injectable hook that a Libs module initializer
installs before any caller can reach the evaluator; and the inline-
constant table is promoted to a shared Gen5InlineConstants so backends
cannot drift on constant semantics (the SPIR-V translator now delegates
to it).

The ShaderDump tool drops its reflection over the moved types in favor
of direct typed calls; only the SPIR-V emitter, still internal to Libs
until it moves to its own backend project, is reached via reflection.
Verified by a clean solution build, the existing test suite, and a full
ShaderDump conformance run.

* [ShaderCompiler] Move the SPIR-V emitter into SharpEmu.ShaderCompiler.Vulkan

Gen5SpirvTranslator (with its ALU partial), SpirvModuleBuilder,
SpirvFixedShaders, and the Gen5SpirvShader/Gen5SpirvStage artifact types
move whole from SharpEmu.Libs/Agc into the first per-backend codegen
project. Notably it needs no Vulkan bindings reference: emitters
produce bytes from the shared IR; renderers own graphics APIs. Types go
public as the backend's contract; AgcExports and the presenter consume
them exactly as before.

The ShaderDump tool drops its last reflection: with both halves of the
pipeline public it drives decode and all three emit entry points with
direct typed calls, retiring the PadWithDefaults invoke shim — and it
no longer references SharpEmu.Libs at all, making the conformance tool
emulator-independent by design. Verified by a clean solution build, the
test suite, a full ShaderDump conformance run, and a locked-mode
restore under the pinned SDK.

* [Gpu] Extract the guest-GPU backend seam (IGuestGpuBackend)

The AGC/VideoOut/SystemService export layers now reach the renderer
through IGuestGpuBackend via GuestGpu.Current (mirroring HostPlatform),
instead of calling VulkanVideoPresenter statics. The Vulkan backend is
a thin adapter over the existing presenter, so the extraction stays
mechanical; only the adapter and the presenter itself reference the
presenter now.

The types crossing the seam move to Gpu/GuestGpuTypes.cs and drop their
Vulkan prefixes, which an audit showed were misnomers: every field is a
neutral primitive or a raw guest value (guest addresses, format and
number-type codes, CB_BLEND register bitfields, verbatim sampler
descriptor dwords). The one genuine Vulkan value in the old surface —
the Silk.NET Format inside VulkanRenderTargetFormat, which callers
never read — stops crossing: TryDecodeRenderTargetFormat is replaced at
the seam by TryGetRenderTargetOutputKind, which surfaces only the
Gen5PixelOutputKind callers actually consume, keeping native formats a
backend-internal concern. ToVulkanSampler in AgcExports is renamed
ToGuestSampler to match what it always produced.

Seam rules are documented on the interface: no host-API value crosses,
and submission stays coarse-grained with synchronization internal to
backends. Interim exception, resolved next: shader parameters are still
SPIR-V blobs.

* [Gpu] Move shader compilation behind the guest-GPU backend

The seam's interim exception is gone: AgcExports no longer calls
Gen5SpirvTranslator or handles SPIR-V bytes. IGuestGpuBackend gains the
three TryCompile entry points, which take the backend-neutral
(Gen5ShaderState, Gen5ShaderEvaluation) contract plus the flat
per-role resource-slot bases a multi-stage draw needs, and return
opaque IGuestCompiledShader handles that only the producing backend can
submit — the Vulkan backend wraps its SPIR-V in
VulkanCompiledGuestShader and rejects foreign handles loudly. Draw and
dispatch submissions take handles instead of byte arrays; the shader
caches in AgcExports store handles.

IGuestCompiledShader.Payload exposes the backend-defined compiled bytes
for exactly two callers: the diagnostics dump and the size trace —
documented as never-interpret. The unused _pixelSpirvCache is deleted.
With this, a Metal or DX12 backend plugs in by implementing
IGuestGpuBackend with its own codegen; nothing in the export layers
knows which shader format exists.

Verified by a clean solution build, the test suite, and a full
ShaderDump conformance run under the pinned SDK.

* [Gpu] Fix rename collateral from the seam extraction

Address review findings: a doc comment picked up the mechanical
VulkanVideoPresenter -> GuestGpu.Current rewrite and ended up naming
members that do not exist on the interface, and CreateVulkanIndexBuffer
kept its Vulkan prefix while every sibling factory was de-Vulkanized —
it produces the neutral GuestIndexBuffer, so it is CreateGuestIndexBuffer.

* [Gpu] Label diagnostics dumps with the backend's payload extension

Address the review's altitude finding on DumpSpirv: the dump helper's
IR-disassembly half is backend-neutral and stays put, but writing the
opaque payload to a hardcoded .spv interpreted bytes the seam says
never to interpret. IGuestCompiledShader now declares its payload's
file extension, and the renamed DumpCompiledShader takes the handle and
writes honestly-labeled dumps whichever backend produced them.

* [Gpu] Make the shader-cache hit path allocation-free and lock-free

Every translated draw built its cache key with a LINQ Select feeding
string.Join plus one interpolated string per render target — steady
per-draw allocation whether or not the shaders were already cached. The
output layout is now packed exactly into a ulong (guest slot in 6 bits
+ output kind in 2 bits per target, host locations being the byte
positions, target count in the key beside it), and the
Gen5PixelOutputBinding array is only materialized on a cache miss,
where compilation dwarfs it.

The graphics/compute shader caches switch from Dictionary guarded by
_submitTraceGate to ConcurrentDictionary, making the per-draw and
per-dispatch hit paths lock-free and decoupling them from the tracing
gate they coincidentally shared. And the seam-shaped render-target list
is built once when a translated draw is created instead of a
Select/ToArray per submission of a cached draw.

* [Gpu] Replace LINQ with explicit loops in code this branch introduced

Project rule going forward: no LINQ — it allocates enumerators,
closures, and delegates, and this codebase is GC-pause-sensitive. The
pixel-output and guest-render-target array builds and the ShaderDump
store-PC collection become plain loops; pre-existing LINQ elsewhere is
left for changes that already touch those lines.

* [ShaderCompiler] Suppress CA2255 on the evaluator hook installer

The analyzer coverage that arrived with the rebase flags
ModuleInitializer in library code; this is the rule's intended advanced
scenario — the hook must be installed before any code path can reach
the evaluator, and every such path enters through this assembly — so
suppress with that justification rather than weaken the guarantee to a
static constructor's lazier timing.

* [Gpu] Resolve rebase artifacts onto main

Dedupe the System.Collections.Concurrent using in AgcExports that the
rebase merge duplicated (main and this branch each added it), and
regenerate the lock files for the new shader-compiler projects and
SharpEmu.Libs against main's current package graph so --locked-mode
restore matches at the branch tip.

* [CI] Comment per-platform build artifact links on PRs

Adds a workflow_run workflow that, after "Build and Release" finishes a
pull-request build, posts (and keeps updated in place) a single PR
comment linking the Windows, Linux, and macOS artifacts from that run.

It runs via workflow_run rather than in the build workflow because PRs
from forks build with a read-only token that cannot comment; the
follow-on run executes in the base-repo context with write access and
without checking out fork code. GitHub only triggers workflow_run from
the default branch, so this takes effect once merged to main.
This commit is contained in:
Gutemberg Ribeiro
2026-07-15 18:11:24 +01:00
committed by GitHub
parent c69ac6ddab
commit 30fdd8d6ed
35 changed files with 1308 additions and 646 deletions
@@ -7,6 +7,9 @@ using Silk.NET.Maths;
using SharpEmu.HLE;
using SharpEmu.Libs.Agc;
using Silk.NET.Input;
using SharpEmu.Libs.Gpu;
using SharpEmu.ShaderCompiler;
using SharpEmu.ShaderCompiler.Vulkan;
using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.KHR;
using Silk.NET.Vulkan.Extensions.EXT;
@@ -22,111 +25,6 @@ using VkSemaphore = Silk.NET.Vulkan.Semaphore;
namespace SharpEmu.Libs.VideoOut;
internal enum GuestDrawKind
{
None,
FullscreenBarycentric,
}
internal sealed record VulkanGuestDrawTexture(
ulong Address,
uint Width,
uint Height,
uint Format,
uint NumberType,
byte[] RgbaPixels,
bool IsFallback,
bool IsStorage,
uint MipLevels = 1,
uint MipLevel = 0,
uint Pitch = 0,
uint TileMode = 0,
uint DstSelect = 0xFAC,
VulkanGuestSampler Sampler = default);
internal readonly record struct VulkanGuestSampler(
uint Word0,
uint Word1,
uint Word2,
uint Word3);
internal sealed record VulkanGuestMemoryBuffer(
ulong BaseAddress,
byte[] Data);
internal sealed record VulkanGuestVertexBuffer(
uint Location,
uint ComponentCount,
uint DataFormat,
uint NumberFormat,
ulong BaseAddress,
uint Stride,
uint OffsetBytes,
byte[] Data);
internal sealed record VulkanGuestIndexBuffer(
byte[] Data,
bool Is32Bit);
internal readonly record struct VulkanGuestRect(
int X,
int Y,
uint Width,
uint Height);
internal readonly record struct VulkanGuestViewport(
float X,
float Y,
float Width,
float Height,
float MinDepth,
float MaxDepth);
internal readonly record struct VulkanGuestBlendState(
bool Enable,
uint ColorSrcFactor,
uint ColorDstFactor,
uint ColorFunc,
uint AlphaSrcFactor,
uint AlphaDstFactor,
uint AlphaFunc,
bool SeparateAlphaBlend,
uint WriteMask)
{
public static VulkanGuestBlendState Default { get; } = new(
Enable: false,
ColorSrcFactor: 1,
ColorDstFactor: 0,
ColorFunc: 0,
AlphaSrcFactor: 1,
AlphaDstFactor: 0,
AlphaFunc: 0,
SeparateAlphaBlend: false,
WriteMask: 0xFu);
}
internal sealed record VulkanGuestRenderState(
IReadOnlyList<VulkanGuestBlendState> Blends,
VulkanGuestRect? Scissor,
VulkanGuestViewport? Viewport)
{
public static VulkanGuestRenderState Default { get; } = new(
[VulkanGuestBlendState.Default],
Scissor: null,
Viewport: null);
public VulkanGuestBlendState Blend =>
Blends.Count == 0 ? VulkanGuestBlendState.Default : Blends[0];
}
internal sealed record VulkanGuestRenderTarget(
ulong Address,
uint Width,
uint Height,
uint Format,
uint NumberType,
uint MipLevels = 1);
internal readonly record struct VulkanRenderTargetFormat(
Format Format,
Gen5PixelOutputKind OutputKind)
@@ -137,26 +35,26 @@ internal readonly record struct VulkanRenderTargetFormat(
internal sealed record VulkanTranslatedGuestDraw(
byte[] VertexSpirv,
byte[] PixelSpirv,
IReadOnlyList<VulkanGuestDrawTexture> Textures,
IReadOnlyList<VulkanGuestMemoryBuffer> GlobalMemoryBuffers,
IReadOnlyList<VulkanGuestVertexBuffer> VertexBuffers,
IReadOnlyList<GuestDrawTexture> Textures,
IReadOnlyList<GuestMemoryBuffer> GlobalMemoryBuffers,
IReadOnlyList<GuestVertexBuffer> VertexBuffers,
uint AttributeCount,
uint VertexCount,
uint InstanceCount,
uint PrimitiveType,
VulkanGuestIndexBuffer? IndexBuffer,
VulkanGuestRenderState RenderState);
GuestIndexBuffer? IndexBuffer,
GuestRenderState RenderState);
internal sealed record VulkanOffscreenGuestDraw(
VulkanTranslatedGuestDraw Draw,
IReadOnlyList<VulkanGuestRenderTarget> Targets,
IReadOnlyList<GuestRenderTarget> Targets,
bool PublishTarget);
internal sealed record VulkanComputeGuestDispatch(
ulong ShaderAddress,
byte[] ComputeSpirv,
IReadOnlyList<VulkanGuestDrawTexture> Textures,
IReadOnlyList<VulkanGuestMemoryBuffer> GlobalMemoryBuffers,
IReadOnlyList<GuestDrawTexture> Textures,
IReadOnlyList<GuestMemoryBuffer> GlobalMemoryBuffers,
uint GroupCountX,
uint GroupCountY,
uint GroupCountZ);
@@ -407,8 +305,8 @@ internal static unsafe class VulkanVideoPresenter
public static void SubmitTranslatedDraw(
byte[] pixelSpirv,
IReadOnlyList<VulkanGuestDrawTexture> textures,
IReadOnlyList<VulkanGuestMemoryBuffer> globalMemoryBuffers,
IReadOnlyList<GuestDrawTexture> textures,
IReadOnlyList<GuestMemoryBuffer> globalMemoryBuffers,
uint width,
uint height,
uint attributeCount,
@@ -416,9 +314,9 @@ internal static unsafe class VulkanVideoPresenter
uint vertexCount = 3,
uint instanceCount = 1,
uint primitiveType = 4,
VulkanGuestIndexBuffer? indexBuffer = null,
IReadOnlyList<VulkanGuestVertexBuffer>? vertexBuffers = null,
VulkanGuestRenderState? renderState = null)
GuestIndexBuffer? indexBuffer = null,
IReadOnlyList<GuestVertexBuffer>? vertexBuffers = null,
GuestRenderState? renderState = null)
{
if (pixelSpirv.Length == 0 || width == 0 || height == 0)
{
@@ -456,7 +354,7 @@ internal static unsafe class VulkanVideoPresenter
instanceCount,
primitiveType,
indexBuffer,
renderState ?? VulkanGuestRenderState.Default),
renderState ?? GuestRenderState.Default),
RequiredGuestWorkSequence: _enqueuedGuestWorkSequence,
IsSplash: false);
System.Threading.Monitor.PulseAll(_gate);
@@ -473,17 +371,17 @@ internal static unsafe class VulkanVideoPresenter
public static void SubmitOffscreenTranslatedDraw(
byte[] pixelSpirv,
IReadOnlyList<VulkanGuestDrawTexture> textures,
IReadOnlyList<VulkanGuestMemoryBuffer> globalMemoryBuffers,
IReadOnlyList<GuestDrawTexture> textures,
IReadOnlyList<GuestMemoryBuffer> globalMemoryBuffers,
uint attributeCount,
VulkanGuestRenderTarget target,
GuestRenderTarget target,
byte[]? vertexSpirv = null,
uint vertexCount = 3,
uint instanceCount = 1,
uint primitiveType = 4,
VulkanGuestIndexBuffer? indexBuffer = null,
IReadOnlyList<VulkanGuestVertexBuffer>? vertexBuffers = null,
VulkanGuestRenderState? renderState = null)
GuestIndexBuffer? indexBuffer = null,
IReadOnlyList<GuestVertexBuffer>? vertexBuffers = null,
GuestRenderState? renderState = null)
{
SubmitOffscreenTranslatedDraw(
pixelSpirv,
@@ -502,17 +400,17 @@ internal static unsafe class VulkanVideoPresenter
public static void SubmitOffscreenTranslatedDraw(
byte[] pixelSpirv,
IReadOnlyList<VulkanGuestDrawTexture> textures,
IReadOnlyList<VulkanGuestMemoryBuffer> globalMemoryBuffers,
IReadOnlyList<GuestDrawTexture> textures,
IReadOnlyList<GuestMemoryBuffer> globalMemoryBuffers,
uint attributeCount,
IReadOnlyList<VulkanGuestRenderTarget> targets,
IReadOnlyList<GuestRenderTarget> targets,
byte[]? vertexSpirv = null,
uint vertexCount = 3,
uint instanceCount = 1,
uint primitiveType = 4,
VulkanGuestIndexBuffer? indexBuffer = null,
IReadOnlyList<VulkanGuestVertexBuffer>? vertexBuffers = null,
VulkanGuestRenderState? renderState = null)
GuestIndexBuffer? indexBuffer = null,
IReadOnlyList<GuestVertexBuffer>? vertexBuffers = null,
GuestRenderState? renderState = null)
{
if (pixelSpirv.Length == 0 ||
targets.Count == 0 ||
@@ -541,7 +439,7 @@ internal static unsafe class VulkanVideoPresenter
$"{firstTarget.Width}x{firstTarget.Height} textures={textures.Count}");
}
var effectiveRenderState = renderState ?? VulkanGuestRenderState.Default;
var effectiveRenderState = renderState ?? GuestRenderState.Default;
if (effectiveRenderState.Blends.Count == 1 && targets.Count > 1)
{
effectiveRenderState = effectiveRenderState with
@@ -589,8 +487,8 @@ internal static unsafe class VulkanVideoPresenter
public static void SubmitStorageTranslatedDraw(
byte[] pixelSpirv,
IReadOnlyList<VulkanGuestDrawTexture> textures,
IReadOnlyList<VulkanGuestMemoryBuffer> globalMemoryBuffers,
IReadOnlyList<GuestDrawTexture> textures,
IReadOnlyList<GuestMemoryBuffer> globalMemoryBuffers,
uint attributeCount,
uint width,
uint height)
@@ -623,8 +521,8 @@ internal static unsafe class VulkanVideoPresenter
1,
4,
null,
VulkanGuestRenderState.Default),
[new VulkanGuestRenderTarget(
GuestRenderState.Default),
[new GuestRenderTarget(
Address: 0,
width,
height,
@@ -637,8 +535,8 @@ internal static unsafe class VulkanVideoPresenter
public static void SubmitComputeDispatch(
ulong shaderAddress,
byte[] computeSpirv,
IReadOnlyList<VulkanGuestDrawTexture> textures,
IReadOnlyList<VulkanGuestMemoryBuffer> globalMemoryBuffers,
IReadOnlyList<GuestDrawTexture> textures,
IReadOnlyList<GuestMemoryBuffer> globalMemoryBuffers,
uint groupCountX,
uint groupCountY,
uint groupCountZ)
@@ -818,7 +716,7 @@ internal static unsafe class VulkanVideoPresenter
SubmitOffscreenTranslatedDraw(
fragmentSpirv,
[
new VulkanGuestDrawTexture(
new GuestDrawTexture(
sourceAddress,
sourceWidth,
sourceHeight,
@@ -830,7 +728,7 @@ internal static unsafe class VulkanVideoPresenter
],
[],
attributeCount: 1,
new VulkanGuestRenderTarget(
new GuestRenderTarget(
destinationAddress,
destinationWidth,
destinationHeight,
@@ -1363,7 +1261,7 @@ internal static unsafe class VulkanVideoPresenter
private readonly Dictionary<byte[], Pipeline> _computePipelines =
new(ReferenceEqualityComparer.Instance);
private readonly Dictionary<GraphicsPipelineKey, Pipeline> _graphicsPipelines = new();
private readonly Dictionary<VulkanGuestSampler, Sampler> _samplers = new();
private readonly Dictionary<GuestSampler, Sampler> _samplers = new();
private readonly Dictionary<byte[], string> _shaderDigests =
new(ReferenceEqualityComparer.Instance);
private readonly Dictionary<DescriptorLayoutKey, DescriptorLayoutBundle>
@@ -1418,9 +1316,9 @@ internal static unsafe class VulkanVideoPresenter
public uint VertexCount = 3;
public uint InstanceCount = 1;
public PrimitiveTopology Topology = PrimitiveTopology.TriangleList;
public VulkanGuestBlendState[] Blends = [VulkanGuestBlendState.Default];
public VulkanGuestRect? Scissor;
public VulkanGuestViewport? Viewport;
public GuestBlendState[] Blends = [GuestBlendState.Default];
public GuestRect? Scissor;
public GuestViewport? Viewport;
public RenderPass TransientRenderPass;
public Framebuffer TransientFramebuffer;
}
@@ -1440,7 +1338,7 @@ internal static unsafe class VulkanVideoPresenter
public bool NeedsUpload;
public bool OwnsStorage;
public bool IsStorage;
public VulkanGuestSampler SamplerState;
public GuestSampler SamplerState;
public Sampler Sampler;
public GuestImageResource? GuestImage;
public ulong CpuContentFingerprint;
@@ -1740,11 +1638,11 @@ internal static unsafe class VulkanVideoPresenter
$"{dispatch.GroupCountX}x{dispatch.GroupCountY}x{dispatch.GroupCountZ}";
}
private static string GuestImageDebugName(VulkanGuestRenderTarget target, Format format) =>
private static string GuestImageDebugName(GuestRenderTarget target, Format format) =>
$"SharpEmu guest 0x{target.Address:X16} {target.Width}x{target.Height} " +
$"fmt{target.Format}/{format}";
private static string TextureDebugName(VulkanGuestDrawTexture texture, Format format) =>
private static string TextureDebugName(GuestDrawTexture texture, Format format) =>
$"SharpEmu texture 0x{texture.Address:X16} {texture.Width}x{texture.Height} " +
$"fmt{texture.Format}/{format}";
@@ -3621,7 +3519,7 @@ internal static unsafe class VulkanVideoPresenter
}
[MethodImpl(MethodImplOptions.NoInlining)]
private TextureResource ResolveTextureResource(VulkanGuestDrawTexture texture)
private TextureResource ResolveTextureResource(GuestDrawTexture texture)
{
if (texture.IsStorage)
{
@@ -3695,7 +3593,7 @@ internal static unsafe class VulkanVideoPresenter
}
private bool TryCreateCpuTextureRefreshResource(
VulkanGuestDrawTexture texture,
GuestDrawTexture texture,
GuestImageResource guestImage,
ImageView view,
out TextureResource resource)
@@ -3760,7 +3658,7 @@ internal static unsafe class VulkanVideoPresenter
}
private static bool IsCompatibleGuestImageAlias(
VulkanGuestDrawTexture texture,
GuestDrawTexture texture,
GuestImageResource guestImage)
{
if (guestImage.Width == texture.Width &&
@@ -3781,7 +3679,7 @@ internal static unsafe class VulkanVideoPresenter
}
[MethodImpl(MethodImplOptions.NoInlining)]
private TextureResource ResolveStorageImageResource(VulkanGuestDrawTexture texture)
private TextureResource ResolveStorageImageResource(GuestDrawTexture texture)
{
if (texture.Address == 0)
{
@@ -3858,7 +3756,7 @@ internal static unsafe class VulkanVideoPresenter
return resource;
}
private TextureResource CreateStorageScratchResource(VulkanGuestDrawTexture texture)
private TextureResource CreateStorageScratchResource(GuestDrawTexture texture)
{
var width = Math.Max(texture.Width, 1);
var height = Math.Max(texture.Height, 1);
@@ -3948,7 +3846,7 @@ internal static unsafe class VulkanVideoPresenter
};
}
private GuestImageResource ResolveStorageGuestImage(VulkanGuestDrawTexture texture)
private GuestImageResource ResolveStorageGuestImage(GuestDrawTexture texture)
{
if (texture.Address == 0)
{
@@ -3957,7 +3855,7 @@ internal static unsafe class VulkanVideoPresenter
var format = GetTextureFormat(texture.Format, texture.NumberType);
var guestImage = GetOrCreateGuestImage(
new VulkanGuestRenderTarget(
new GuestRenderTarget(
texture.Address,
texture.Width,
texture.Height,
@@ -3974,7 +3872,7 @@ internal static unsafe class VulkanVideoPresenter
return guestImage;
}
private TextureResource CreateTextureResource(VulkanGuestDrawTexture texture)
private TextureResource CreateTextureResource(GuestDrawTexture texture)
{
var width = Math.Max(texture.Width, 1);
var height = Math.Max(texture.Height, 1);
@@ -4172,7 +4070,7 @@ internal static unsafe class VulkanVideoPresenter
}
private void DumpTextureUpload(
VulkanGuestDrawTexture texture,
GuestDrawTexture texture,
byte[] pixels,
uint rowLength,
uint width,
@@ -4264,7 +4162,7 @@ internal static unsafe class VulkanVideoPresenter
private static void WriteInt32(byte[] output, int offset, int value) =>
WriteUInt32(output, offset, unchecked((uint)value));
private Sampler CreateSampler(VulkanGuestSampler sampler)
private Sampler CreateSampler(GuestSampler sampler)
{
if (_samplers.TryGetValue(sampler, out var cachedSampler))
{
@@ -4342,7 +4240,7 @@ internal static unsafe class VulkanVideoPresenter
}
private GlobalBufferResource CreateGlobalBufferResource(
VulkanGuestMemoryBuffer guestBuffer)
GuestMemoryBuffer guestBuffer)
{
var buffer = CreateHostBuffer(
guestBuffer.Data,
@@ -4371,7 +4269,7 @@ internal static unsafe class VulkanVideoPresenter
}
private VertexBufferResource CreateVertexBufferResource(
VulkanGuestVertexBuffer guestBuffer)
GuestVertexBuffer guestBuffer)
{
var buffer = CreateHostBuffer(
guestBuffer.Data,
@@ -4594,7 +4492,7 @@ internal static unsafe class VulkanVideoPresenter
private static uint GetDrawVertexCount(
uint primitiveType,
uint vertexCount,
VulkanGuestIndexBuffer? indexBuffer)
GuestIndexBuffer? indexBuffer)
{
if (primitiveType == GuestPrimitiveRectList && indexBuffer is null)
{
@@ -4640,41 +4538,41 @@ internal static unsafe class VulkanVideoPresenter
_ => BlendOp.Add,
};
private static uint DecodeSamplerClampX(VulkanGuestSampler sampler) =>
private static uint DecodeSamplerClampX(GuestSampler sampler) =>
sampler.Word0 & 0x7u;
private static uint DecodeSamplerClampY(VulkanGuestSampler sampler) =>
private static uint DecodeSamplerClampY(GuestSampler sampler) =>
(sampler.Word0 >> 3) & 0x7u;
private static uint DecodeSamplerClampZ(VulkanGuestSampler sampler) =>
private static uint DecodeSamplerClampZ(GuestSampler sampler) =>
(sampler.Word0 >> 6) & 0x7u;
private static uint DecodeSamplerDepthCompare(VulkanGuestSampler sampler) =>
private static uint DecodeSamplerDepthCompare(GuestSampler sampler) =>
(sampler.Word0 >> 12) & 0x7u;
private static float DecodeSamplerMinLod(VulkanGuestSampler sampler) =>
private static float DecodeSamplerMinLod(GuestSampler sampler) =>
(sampler.Word1 & 0xFFFu) / 256.0f;
private static float DecodeSamplerMaxLod(VulkanGuestSampler sampler) =>
private static float DecodeSamplerMaxLod(GuestSampler sampler) =>
((sampler.Word1 >> 12) & 0xFFFu) / 256.0f;
private static float DecodeSamplerLodBias(VulkanGuestSampler sampler)
private static float DecodeSamplerLodBias(GuestSampler sampler)
{
var raw = sampler.Word2 & 0x3FFFu;
var signed = (short)((raw ^ 0x2000u) - 0x2000u);
return signed / 256.0f;
}
private static uint DecodeSamplerMagFilter(VulkanGuestSampler sampler) =>
private static uint DecodeSamplerMagFilter(GuestSampler sampler) =>
(sampler.Word2 >> 20) & 0x3u;
private static uint DecodeSamplerMinFilter(VulkanGuestSampler sampler) =>
private static uint DecodeSamplerMinFilter(GuestSampler sampler) =>
(sampler.Word2 >> 22) & 0x3u;
private static uint DecodeSamplerMipFilter(VulkanGuestSampler sampler) =>
private static uint DecodeSamplerMipFilter(GuestSampler sampler) =>
(sampler.Word2 >> 26) & 0x3u;
private static uint DecodeSamplerBorderColor(VulkanGuestSampler sampler) =>
private static uint DecodeSamplerBorderColor(GuestSampler sampler) =>
(sampler.Word3 >> 30) & 0x3u;
private static SamplerAddressMode ToVkSamplerAddressMode(uint mode) =>
@@ -4741,11 +4639,11 @@ internal static unsafe class VulkanVideoPresenter
return flags;
}
private static VulkanGuestRect ClampScissor(VulkanGuestRect? scissor, Extent2D extent)
private static GuestRect ClampScissor(GuestRect? scissor, Extent2D extent)
{
if (scissor is not { } rect)
{
return new VulkanGuestRect(0, 0, extent.Width, extent.Height);
return new GuestRect(0, 0, extent.Width, extent.Height);
}
var left = Math.Clamp(rect.X, 0, checked((int)extent.Width));
@@ -4758,7 +4656,7 @@ internal static unsafe class VulkanVideoPresenter
rect.Y + checked((int)rect.Height),
top,
checked((int)extent.Height));
return new VulkanGuestRect(
return new GuestRect(
left,
top,
checked((uint)(right - left)),
@@ -4773,7 +4671,7 @@ internal static unsafe class VulkanVideoPresenter
? viewportEpsilon
: 0f;
private static Viewport ClampViewport(VulkanGuestViewport? viewport, Extent2D extent)
private static Viewport ClampViewport(GuestViewport? viewport, Extent2D extent)
{
if (viewport is not { } rect)
{
@@ -5476,7 +5374,7 @@ internal static unsafe class VulkanVideoPresenter
[MethodImpl(MethodImplOptions.NoInlining)]
private GuestImageResource GetOrCreateGuestImage(
VulkanGuestRenderTarget target,
GuestRenderTarget target,
Format format)
{
var mipLevels = ClampMipLevels(target.Width, target.Height, target.MipLevels);
@@ -5999,15 +5897,15 @@ internal static unsafe class VulkanVideoPresenter
var gpuInFlight = _pendingGuestSubmissions.Count +
(_presentationInFlight ? 1 : 0);
var readCount = Interlocked.Read(
ref Agc.Gen5ShaderScalarEvaluator.GlobalMemoryReadCount);
ref Gen5ShaderScalarEvaluator.GlobalMemoryReadCount);
var readBytes = Interlocked.Read(
ref Agc.Gen5ShaderScalarEvaluator.GlobalMemoryReadBytes);
ref Gen5ShaderScalarEvaluator.GlobalMemoryReadBytes);
var readHits = Interlocked.Read(
ref Agc.Gen5ShaderScalarEvaluator.GlobalMemoryReadCacheHits);
ref Gen5ShaderScalarEvaluator.GlobalMemoryReadCacheHits);
var readPvmBytes = Interlocked.Read(
ref Agc.Gen5ShaderScalarEvaluator.GlobalMemoryReadPvmBytes);
ref Gen5ShaderScalarEvaluator.GlobalMemoryReadPvmBytes);
var readLibcBytes = Interlocked.Read(
ref Agc.Gen5ShaderScalarEvaluator.GlobalMemoryReadLibcBytes);
ref Gen5ShaderScalarEvaluator.GlobalMemoryReadLibcBytes);
var readsPerSecond =
(readCount - _performanceHudLastReadCount) / elapsedSeconds;
var readMbPerSecond =