178 lines
6.4 KiB
C#
178 lines
6.4 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.Libs.Gpu;
|
|
using SharpEmu.ShaderCompiler;
|
|
using Silk.NET.Vulkan;
|
|
|
|
namespace SharpEmu.Libs.VideoOut;
|
|
|
|
internal static unsafe partial class VulkanVideoPresenter
|
|
{
|
|
|
|
private static long _perfDrawCount;
|
|
private static long _perfDrawTicks;
|
|
private static long _perfPipelineCreations;
|
|
private static long _perfSpirvCompilations;
|
|
|
|
internal static (long Draws, double DrawMs, long Pipelines, long SpirvCompilations)
|
|
ReadAndResetPerfCounters()
|
|
{
|
|
var draws = Interlocked.Exchange(ref _perfDrawCount, 0);
|
|
var ticks = Interlocked.Exchange(ref _perfDrawTicks, 0);
|
|
var pipelines = Interlocked.Exchange(ref _perfPipelineCreations, 0);
|
|
var spirv = Interlocked.Exchange(ref _perfSpirvCompilations, 0);
|
|
return (draws, ticks * 1000.0 / System.Diagnostics.Stopwatch.Frequency, pipelines, spirv);
|
|
}
|
|
|
|
internal static void CountSpirvCompilation() =>
|
|
Interlocked.Increment(ref _perfSpirvCompilations);
|
|
|
|
internal static ulong GetGuestImageByteCount(uint format, uint width, uint height)
|
|
{
|
|
var blockBytes = format switch
|
|
{
|
|
169 or 170 or 175 or 176 => 8UL,
|
|
171 or 172 or 173 or 174 or
|
|
177 or 178 or 179 or 180 or 181 or 182 => 16UL,
|
|
_ => 0UL,
|
|
};
|
|
if (blockBytes != 0)
|
|
{
|
|
return checked(((ulong)width + 3) / 4 * (((ulong)height + 3) / 4) * blockBytes);
|
|
}
|
|
|
|
var bytesPerPixel = format switch
|
|
{
|
|
1 => 1UL,
|
|
2 or 3 or 16 or 17 or 19 => 2UL,
|
|
11 or 12 => 8UL,
|
|
13 => 12UL,
|
|
14 => 16UL,
|
|
_ => 4UL,
|
|
};
|
|
return checked((ulong)width * height * bytesPerPixel);
|
|
}
|
|
|
|
internal static ulong GetGuestImageByteCount(
|
|
uint format,
|
|
uint width,
|
|
uint height,
|
|
uint depth) =>
|
|
checked(GetGuestImageByteCount(format, width, height) * Math.Max(depth, 1u));
|
|
|
|
internal static bool TryDecodeRenderTargetFormat(
|
|
uint dataFormat,
|
|
uint numberType,
|
|
out VulkanRenderTargetFormat result) =>
|
|
TryDecodeRenderTargetFormat(
|
|
dataFormat,
|
|
numberType,
|
|
componentSwap: 0,
|
|
out result);
|
|
|
|
internal static bool TryDecodeRenderTargetFormat(
|
|
uint dataFormat,
|
|
uint numberType,
|
|
uint componentSwap,
|
|
out VulkanRenderTargetFormat result)
|
|
{
|
|
var format = (dataFormat, numberType, componentSwap) switch
|
|
{
|
|
// Early geometry and scene targets use these color formats.
|
|
(2, 0, _) => Format.R16Unorm,
|
|
(2, 1, _) => Format.R16SNorm,
|
|
(2, 2, _) => Format.R16Uscaled,
|
|
(2, 3, _) => Format.R16Sscaled,
|
|
(2, 4, _) => Format.R16Uint,
|
|
(2, 5, _) => Format.R16Sint,
|
|
(2, 7, _) => Format.R16Sfloat,
|
|
(4, 4, _) => Format.R32Uint,
|
|
(4, 5, _) => Format.R32Sint,
|
|
(4, 7, _) => Format.R32Sfloat,
|
|
(5, 4, _) => Format.R16G16Uint,
|
|
(5, 5, _) => Format.R16G16Sint,
|
|
(5, 7, _) => Format.R16G16Sfloat,
|
|
(6, 7, _) or (7, 7, _) => Format.B10G11R11UfloatPack32,
|
|
(9, _, 1) => Format.A2R10G10B10UnormPack32,
|
|
(9, _, _) => Format.A2B10G10R10UnormPack32,
|
|
(10, 4, _) => Format.R8G8B8A8Uint,
|
|
(10, 5, _) => Format.R8G8B8A8Sint,
|
|
(10, 6 or 9, 1) => Format.B8G8R8A8Srgb,
|
|
(10, 6 or 9, _) => Format.R8G8B8A8Srgb,
|
|
(10, 0, 1) => Format.B8G8R8A8Unorm,
|
|
(10, _, _) => Format.R8G8B8A8Unorm,
|
|
(11, 4, _) => Format.R32G32Uint,
|
|
(11, 5, _) => Format.R32G32Sint,
|
|
(11, 7, _) => Format.R32G32Sfloat,
|
|
(12, 4, _) => Format.R16G16B16A16Uint,
|
|
(12, 5, _) => Format.R16G16B16A16Sint,
|
|
(12, 7, _) => Format.R16G16B16A16Sfloat,
|
|
(13, 7, _) or (14, 7, _) => Format.R32G32B32A32Sfloat,
|
|
(20, 0, _) => Format.R32Uint,
|
|
(29, 0, _) or (4, 0, _) => Format.R32Sfloat,
|
|
(1, 0, _) or (36, 0, _) => Format.R8Unorm,
|
|
(1, 4, _) or (49, 0, _) => Format.R8Uint,
|
|
(3, 0, _) => Format.R8G8Unorm,
|
|
(5, 0, _) => Format.R16G16Unorm,
|
|
(7, 0, _) => Format.B10G11R11UfloatPack32,
|
|
(12, 0, _) => Format.R16G16B16A16Unorm,
|
|
(13, 0, _) or (14, 0, _) => Format.R32G32B32A32Sfloat,
|
|
(22, 0, _) or (71, 0, _) => Format.R16G16B16A16Sfloat,
|
|
(56, 0, _) or (62, 0, _) or (64, 0, _) => Format.R8G8B8A8Unorm,
|
|
(75, 0, _) => Format.R32G32Sfloat,
|
|
_ => Format.Undefined,
|
|
};
|
|
|
|
if (format == Format.Undefined ||
|
|
!TryGetRenderTargetComponentCount(dataFormat, out var componentCount) ||
|
|
!Gen5ColorComponentMapping.TryResolveRenderTarget(
|
|
componentSwap,
|
|
componentCount,
|
|
out var orderMapping))
|
|
{
|
|
result = default;
|
|
return false;
|
|
}
|
|
|
|
var outputKind = format switch
|
|
{
|
|
Format.R8Uint or Format.R16Uint or Format.R32Uint or Format.R16G16Uint or
|
|
Format.R32G32Uint or Format.R8G8B8A8Uint or Format.R16G16B16A16Uint =>
|
|
Gen5PixelOutputKind.Uint,
|
|
Format.R16Sint or Format.R32Sint or Format.R16G16Sint or Format.R32G32Sint or
|
|
Format.R8G8B8A8Sint or Format.R16G16B16A16Sint => Gen5PixelOutputKind.Sint,
|
|
_ => Gen5PixelOutputKind.Float,
|
|
};
|
|
|
|
var hostToStorage = dataFormat switch
|
|
{
|
|
9 when componentSwap == 1 => new Gen5ColorComponentMapping(0xC6),
|
|
10 when componentSwap == 1 && numberType is 0 or 6 or 9 =>
|
|
new Gen5ColorComponentMapping(0xC6),
|
|
_ => Gen5ColorComponentMapping.Identity,
|
|
};
|
|
result = new VulkanRenderTargetFormat(
|
|
format,
|
|
outputKind,
|
|
hostToStorage.Then(orderMapping));
|
|
return true;
|
|
}
|
|
|
|
private static bool TryGetRenderTargetComponentCount(
|
|
uint dataFormat,
|
|
out uint componentCount)
|
|
{
|
|
componentCount = dataFormat switch
|
|
{
|
|
1 or 2 or 4 or 20 or 29 or 36 or 49 => 1,
|
|
3 or 5 or 11 or 75 => 2,
|
|
6 or 7 => 3,
|
|
9 or 10 or 12 or 13 or 14 or 22 or 56 or 62 or 64 or 71 => 4,
|
|
_ => 0,
|
|
};
|
|
return componentCount != 0;
|
|
}
|
|
|
|
}
|