#include "D3D12Surface.h" #include "D3D12Core.h" namespace XEngine::graphics::d3d12 { namespace { constexpr DXGI_FORMAT to_non_srgb(DXGI_FORMAT format) { if (format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) return DXGI_FORMAT_R8G8B8A8_UNORM; return format; } } // anonymous namespace void d3d12_surface::create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd_queue, DXGI_FORMAT format) { assert(factory && cmd_queue); // 应为可以多次调用,所以需要先释放旧的 swap chain release(); if (SUCCEEDED(factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &_allow_tearing, sizeof(u32))) && _allow_tearing) { _present_flags = DXGI_PRESENT_ALLOW_TEARING; } DXGI_SWAP_CHAIN_DESC1 desc{}; desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; // Alpha通道模式(窗口透明度相关) desc.BufferCount = buffer_count; // 后台缓冲区数量(用于双缓冲/多缓冲) desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 缓冲区用途(作为渲染目标输出) desc.Flags = _allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; // 交换链标志位(无) desc.Format = to_non_srgb(format); // 像素格式(转换为非SRGB格式) desc.Height = _window.height(); // 交换链高度(与窗口高度一致) desc.Width = _window.width(); // 交换链宽度(与窗口宽度一致) desc.SampleDesc.Count = 1; // 多重采样数量(1表示禁用MSAA) desc.SampleDesc.Quality = 0; // 多重采样质量等级(0表示禁用) desc.Scaling = DXGI_SCALING_STRETCH; // 缓冲区缩放模式(窗口大小变化时的处理方式) desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // 交换链效果(翻转并丢弃旧缓冲区) desc.Stereo = false; // 立体显示模式(3D显示) IDXGISwapChain1* swap_chain; HWND hwnd{ (HWND)_window.handle() }; // 为窗口创建交换链,指定交换链描述结构体 DXCall(factory->CreateSwapChainForHwnd(cmd_queue, hwnd, &desc, nullptr, nullptr, &swap_chain)); // 控制 DXGI 如何响应特定的系统按键,阻止 DXGI 响应 Alt+Enter 按键序列,由应用程序手动处理全屏切换逻辑。 DXCall(factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER)); // 将局部变量 swap_chain 创建的交换链对象“转移”给类成员 _swap_chain 来长期持有。 // 免了直接赋值(_swap_chain = swap_chain)可能导致的引用计数问题:直接赋值不增加 // 计数,后续释放 swap_chain 会使对象计数归零而被销毁,_swap_chain 就会变成悬空指针。 DXCall(swap_chain->QueryInterface(IID_PPV_ARGS(&_swap_chain))); core::release(swap_chain); _current_bb_index = _swap_chain->GetCurrentBackBufferIndex(); for (u32 i = 0; i < buffer_count; ++i) { _render_target_data[i].rtv = core::rtv_heap().allocate(); } finalize(); } void d3d12_surface::present() const { assert(_swap_chain); // 设置是否使用垂直同步 // 0 表示不使用垂直同步,1 表示使用垂直同步 // 第二个参数设置有无特殊行为,0 表示无特殊行为 DXCall(_swap_chain->Present(0, _present_flags)); _current_bb_index = _swap_chain->GetCurrentBackBufferIndex(); } void d3d12_surface::finalize() { // 为每个缓冲区创建渲染目标视图 for (u32 i = 0; i < buffer_count; ++i) { render_target_data& data{ _render_target_data[i] }; assert(!data.resource); DXCall(_swap_chain->GetBuffer(i, IID_PPV_ARGS(&data.resource))); D3D12_RENDER_TARGET_VIEW_DESC desc{}; desc.Format = core::default_render_target_format(); desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; core::device()->CreateRenderTargetView(data.resource, &desc, data.rtv.cpu); } DXGI_SWAP_CHAIN_DESC desc{}; DXCall(_swap_chain->GetDesc(&desc)); const u32 width{ desc.BufferDesc.Width }; const u32 height{ desc.BufferDesc.Height }; assert(_window.width() == width && _window.height() == height); _viewport.TopLeftX = 0.0f; _viewport.TopLeftY = 0.0f; _viewport.Width = (float)width; _viewport.Height = (float)height; _viewport.MaxDepth = 1.0f; _viewport.MinDepth = 0.0f; _scissor_rect = { 0, 0, (s32)width, (s32)height }; } void d3d12_surface::release() { for (u32 i = 0; i < buffer_count; ++i) { render_target_data& data{ _render_target_data[i] }; core::release(data.resource); core::rtv_heap().free(data.rtv); } core::release(_swap_chain); } void d3d12_surface::resize() { } u32 d3d12_surface::width() { return _window.width(); } u32 d3d12_surface::height() { return _window.height(); } } // namespace XEngine::graphics::d3d12