diff --git a/Videos/cortex_20260619_091747.mp4 b/Videos/cortex_20260619_091747.mp4 new file mode 100644 index 0000000..1c73761 Binary files /dev/null and b/Videos/cortex_20260619_091747.mp4 differ diff --git a/src/Engine.Graphics.Vulkan/VulkanContext.cs b/src/Engine.Graphics.Vulkan/VulkanContext.cs index f6d05dc..4f497ae 100644 --- a/src/Engine.Graphics.Vulkan/VulkanContext.cs +++ b/src/Engine.Graphics.Vulkan/VulkanContext.cs @@ -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; diff --git a/src/Engine.Graphics.Vulkan/VulkanRenderer.cs b/src/Engine.Graphics.Vulkan/VulkanRenderer.cs index 72dae1e..6e8ce23 100644 --- a/src/Engine.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Engine.Graphics.Vulkan/VulkanRenderer.cs @@ -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();