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:
Binary file not shown.
@@ -359,9 +359,26 @@ 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.B8G8R8A8Unorm &&
|
||||
formats[(int)i].colorSpace == VkColorSpaceKHR.SrgbNonlinearKHR)
|
||||
{
|
||||
SurfaceFormat = formats[(int)i].format;
|
||||
SurfaceColorSpace = formats[(int)i].colorSpace;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to SRGB if UNORM not available
|
||||
if (!found)
|
||||
{
|
||||
for (uint i = 0; i < formatCount; i++)
|
||||
{
|
||||
if (formats[(int)i].format == VkFormat.B8G8R8A8Srgb &&
|
||||
@@ -369,11 +386,14 @@ internal sealed unsafe class VulkanContext : IDisposable
|
||||
{
|
||||
SurfaceFormat = formats[(int)i].format;
|
||||
SurfaceColorSpace = formats[(int)i].colorSpace;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SurfaceFormat == VkFormat.B8G8R8A8Srgb)
|
||||
// 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();
|
||||
|
||||
Reference in New Issue
Block a user