DX11中如何结合DXGI_SWAP_EFFECT_FLIP_DISCARD使用SRGB格式?
我有一个DX11项目,想要将颜色格式从DXGI_FORMAT_R8G8B8A8_UNORM改为DXGI_FORMAT_R8G8B8A8_UNORM_SRGB。遇到的问题:
- 启用
DXGI_SWAP_EFFECT_FLIP_DISCARD时,创建设备和交换链会返回0x887a0001(DXGI_ERROR_INVALID_CALL)错误。 - 使用
DXGI_SWAP_EFFECT_DISCARD替代翻转丢弃模式可以正常运行,但我希望使用翻转模型。
曾看到资料提到:
应仅为渲染目标视图提供*_SRGB格式,而非后台缓冲区本身。
但尝试这种方法后,CreateRenderTargetView调用崩溃了,想知道这种方式是否可行,是否存在理解误区?
编辑1
发布后得到控制台输出:
DXGI错误:IDXGIFactory::CreateSwapChain: 翻转模型交换链(DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL和DXGI_SWAP_EFFECT_FLIP_DISCARD)仅支持以下格式:(DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM),前提是底层设备也支持这些格式。
这是否意味着无法同时使用SRGB和翻转丢弃模式?
编辑2
以下是创建设备、交换链和渲染目标视图的代码,当前遇到的错误是E_INVALIDARG 一个或多个参数无效。根据CreateRenderTargetView文档,仅指出不能使用无类型格式或DXGI_FORMAT_R32G32B32,而SRGB格式不属于这两类,是不是我的D3D11_RENDER_TARGET_VIEW_DESC配置有误?
bool GraphicsEngine::CreateDeviceAndSwapChain() { DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; swapChainDesc.BufferCount = 2; // 翻转模式下替换成srgb会崩溃 //swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; // 不能和翻转模式一起用 swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.OutputWindow = *myWindowHandle; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.Windowed = true; //swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // 缓冲区数量>1时可以切换到此模式 UINT creationFlags = 0; #if defined(REPORT_DX_WARNINGS) creationFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif HRESULT result = D3D11CreateDeviceAndSwapChain( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, creationFlags, nullptr, 0, D3D11_SDK_VERSION, &swapChainDesc, &mySwapChain, &myDevice, nullptr, &myContext); if (FAILED(result)) { return false; } return true; } bool GraphicsEngine::CreateRenderTarget(HRESULT& aHresult) { ID3D11Texture2D* backBufferTexture{}; aHresult = mySwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBufferTexture)); if (FAILED(aHresult)) { return false; } // 这行是导致崩溃的代码,原本我传的是nullptr而不是desc D3D11_RENDER_TARGET_VIEW_DESC desc; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; aHresult = myDevice->CreateRenderTargetView(backBufferTexture, &desc, &myBackBuffer); if (FAILED(aHresult)) // 这里崩溃,错误是"E_INVALIDARG 一个或多个参数无效" { backBufferTexture->Release(); return false; } D3D11_TEXTURE2D_DESC textureDesc; backBufferTexture->GetDesc(&textureDesc); backBufferTexture->Release(); myBackBufferTextureHeight = textureDesc.Height; myBackBufferTextureWidth = textureDesc.Width; return true; }
解答
核心结论
可以同时使用翻转交换链和SRGB颜色空间,你的方向是对的——后台缓冲区用非SRGB格式,渲染目标视图用SRGB格式,但问题出在D3D11_RENDER_TARGET_VIEW_DESC的不完整初始化上。
具体修复
你只设置了desc.Format,但D3D11_RENDER_TARGET_VIEW_DESC是一个结构体,必须完整初始化所有成员,否则未初始化的垃圾值会导致CreateRenderTargetView参数无效。
修改CreateRenderTarget中的视图描述初始化代码:
// 完整初始化结构体,避免未定义行为 D3D11_RENDER_TARGET_VIEW_DESC desc = {}; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; // 对于2D纹理的完整视图,设置视图类型和纹理维度 desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; desc.Texture2D.MipSlice = 0; // 使用第0级mipmap
原理说明
- 翻转交换链确实不支持将后台缓冲区格式设为SRGB(如控制台输出所示),所以后台缓冲区必须用
DXGI_FORMAT_R8G8B8A8_UNORM。 - 通过渲染目标视图指定SRGB格式,D3D11会自动在渲染时完成线性空间到SRGB空间的gamma校正,最终输出到后台缓冲区的颜色会是正确的SRGB编码。
- 结构体未完整初始化是常见陷阱,DX11的API对参数完整性要求严格,未赋值的成员会被视为无效参数,导致
E_INVALIDARG错误。
内容的提问来源于stack exchange,提问作者lolgube010

