fix: force B8G8R8A8_UNORM swapchain — fixes acid video colors

Root cause: driver returned format=58 (A2B10G10R10_PACK32) as fallback
when B8G8R8A8_SRGB wasn't found. 10-bit packed format has different
byte layout → vkCmdCopyImageToBuffer gives garbage when interpreted as
8-bit BGRA → acid colors in video.

Fix:
- QuerySurfaceFormat: try B8G8R8A8_UNORM first (NVIDIA supports it)
- Fallback: B8G8R8A8_SRGB, then first available
- Removed SRGB LUT conversion — UNORM data is already linear
- Raw BGRA bytes go directly to FFmpeg (pixel_format=bgra)
This commit is contained in:
emil28092005
2026-06-19 09:21:26 +03:00
parent 9531b1e9d5
commit e813e5e90b
3 changed files with 23 additions and 29 deletions
Binary file not shown.
+23 -3
View File
@@ -359,21 +359,41 @@ internal sealed unsafe class VulkanContext : IDisposable
var formats = stackalloc VkSurfaceFormatKHR[(int)formatCount];
Vk.vkGetPhysicalDeviceSurfaceFormatsKHR(PhysicalDevice, Surface, &formatCount, formats);
SurfaceFormat = VkFormat.B8G8R8A8Srgb;
// Prefer B8G8R8A8_UNORM for compatibility with video capture (raw byte layout)
SurfaceFormat = VkFormat.B8G8R8A8Unorm;
SurfaceColorSpace = VkColorSpaceKHR.SrgbNonlinearKHR;
bool found = false;
for (uint i = 0; i < formatCount; i++)
{
if (formats[(int)i].format == VkFormat.B8G8R8A8Srgb &&
if (formats[(int)i].format == VkFormat.B8G8R8A8Unorm &&
formats[(int)i].colorSpace == VkColorSpaceKHR.SrgbNonlinearKHR)
{
SurfaceFormat = formats[(int)i].format;
SurfaceColorSpace = formats[(int)i].colorSpace;
found = true;
break;
}
}
if (SurfaceFormat == VkFormat.B8G8R8A8Srgb)
// Fallback to SRGB if UNORM not available
if (!found)
{
for (uint i = 0; i < formatCount; i++)
{
if (formats[(int)i].format == VkFormat.B8G8R8A8Srgb &&
formats[(int)i].colorSpace == VkColorSpaceKHR.SrgbNonlinearKHR)
{
SurfaceFormat = formats[(int)i].format;
SurfaceColorSpace = formats[(int)i].colorSpace;
found = true;
break;
}
}
}
// Last resort: first available
if (!found)
{
SurfaceFormat = formats[0].format;
SurfaceColorSpace = formats[0].colorSpace;
@@ -149,35 +149,9 @@ public sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreensh
Marshal.Copy((nint)pData, result, 0, (int)_screenshotBufferSize);
Vk.vkUnmapMemory(_ctx.Device, _screenshotMemory);
// Swapchain is B8G8R8A8_SRGB — raw bytes are SRGB-encoded
// Convert SRGB→linear for FFmpeg (which expects linear bgra)
for (int i = 0; i < result.Length; i += 4)
{
result[i] = SrgbToLinear(result[i]); // B
result[i + 1] = SrgbToLinear(result[i + 1]); // G
result[i + 2] = SrgbToLinear(result[i + 2]); // R
// result[i + 3] = A (keep as-is)
}
return result;
}
private static readonly byte[] SrgbLut = BuildSrgbLut();
private static byte[] BuildSrgbLut()
{
var lut = new byte[256];
for (int i = 0; i < 256; i++)
{
float s = i / 255.0f;
float l = s <= 0.04045f ? s / 12.92f : MathF.Pow((s + 0.055f) / 1.055f, 2.4f);
lut[i] = (byte)Math.Clamp(l * 255.0f, 0, 255);
}
return lut;
}
private static byte SrgbToLinear(byte srgb) => SrgbLut[srgb];
public void BeginImGuiFrame()
{
_imGui?.NewFrame();