Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
swap_chain.cpp
Go to the documentation of this file.
1// swap_chain.cpp — フリップモデルの待てるスワップチェイン(T-0004)。使い方は swap_chain.h。
2#include "gpu/swap_chain.h"
3
4#include "core/hresult.h"
5#include "core/log.h"
6#include "gpu/com_ptr.h"
7
8namespace bicameral::gpu {
9 namespace {
10
11 constexpr UINT SWAP_CHAIN_FLAGS_BASE = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
12
13 bool IsTearingSupported(IDXGIFactory6* factory) {
14 BOOL allowTearing = FALSE;
15 return SUCCEEDED(factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing,
16 sizeof(allowTearing))) &&
17 allowTearing == TRUE;
18 }
19
20 } // namespace
21
22 std::expected<SwapChain, std::string> SwapChain::Create(ID3D12Device* device, IDXGIFactory6* factory,
23 ID3D12CommandQueue* directQueue, HWND window,
24 uint32_t maxFrameLatency) {
25 SwapChain swapChain;
26 swapChain.m_tearingSupported = IsTearingSupported(factory);
27 const DXGI_SWAP_CHAIN_DESC1 desc{
28 .Width = 0, // 0 = 窓のクライアント領域の大きさ
29 .Height = 0,
30 .Format = FORMAT,
31 .SampleDesc = {.Count = 1},
32 .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
33 .BufferCount = BUFFER_COUNT,
34 .Scaling = DXGI_SCALING_NONE,
35 .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
36 .AlphaMode = DXGI_ALPHA_MODE_IGNORE,
37 .Flags = SWAP_CHAIN_FLAGS_BASE | (swapChain.m_tearingSupported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0u),
38 };
39
40 ComPtr<IDXGISwapChain1> swapChain1;
41 HRESULT result = factory->CreateSwapChainForHwnd(directQueue, window, &desc, nullptr, nullptr, &swapChain1);
42 if (FAILED(result))
43 return std::unexpected("スワップチェインを作れない: " + DescribeHresult(result));
44
45 factory->MakeWindowAssociation(window, DXGI_MWA_NO_ALT_ENTER); // 全画面の切り替えは自分でやる(今は無し)
46 result = swapChain1.As(&swapChain.m_swapChain);
47 if (FAILED(result))
48 return std::unexpected("IDXGISwapChain3 が無い: " + DescribeHresult(result));
49
50 result = swapChain.m_swapChain->SetMaximumFrameLatency(maxFrameLatency);
51 if (FAILED(result))
52 return std::unexpected("先行するフレームの数を設定できない: " + DescribeHresult(result));
53
54 swapChain.m_frameLatencyWaitable.reset(swapChain.m_swapChain->GetFrameLatencyWaitableObject());
55
56 const D3D12_DESCRIPTOR_HEAP_DESC heapDesc{.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
57 .NumDescriptors = BUFFER_COUNT};
58 result = device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&swapChain.m_rtvHeap));
59 if (FAILED(result))
60 return std::unexpected("RTV のヒープを作れない: " + DescribeHresult(result));
61
62 swapChain.m_rtvStride = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
63 if (!swapChain.AcquireBuffers(device))
64 return std::unexpected("バックバッファを取れない");
65
66 Log(Channel::Gpu, Level::Info, "スワップチェイン: {}×{} {} 枚 先行 {} フレーム tearing {}",
67 swapChain.m_width, swapChain.m_height, BUFFER_COUNT, maxFrameLatency,
68 swapChain.m_tearingSupported ? "可" : "不可");
69
70 return swapChain;
71 }
72
73 bool SwapChain::AcquireBuffers(ID3D12Device* device) {
74 DXGI_SWAP_CHAIN_DESC1 desc{};
75 if (!BICAMERAL_CHECK_HR(Channel::Gpu, m_swapChain->GetDesc1(&desc)))
76 return false;
77
78 m_width = desc.Width;
79 m_height = desc.Height;
80 for (uint32_t index = 0; index < BUFFER_COUNT; ++index) {
81 if (!BICAMERAL_CHECK_HR(Channel::Gpu, m_swapChain->GetBuffer(index, IID_PPV_ARGS(&m_buffers[index]))))
82 return false;
83
84 m_buffers[index]->SetName(std::format(L"BackBuffer{}", index).c_str());
85 device->CreateRenderTargetView(m_buffers[index].Get(), nullptr, RenderTargetView(index));
86 }
87
88 return true;
89 }
90
91 D3D12_CPU_DESCRIPTOR_HANDLE SwapChain::RenderTargetView(uint32_t index) const {
92 D3D12_CPU_DESCRIPTOR_HANDLE handle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart();
93 handle.ptr += size_t{index} * m_rtvStride;
94
95 return handle;
96 }
97
98 bool SwapChain::WaitForFrame(uint32_t timeoutMs) const {
99 return WaitForSingleObjectEx(m_frameLatencyWaitable.get(), timeoutMs, TRUE) == WAIT_OBJECT_0;
100 }
101
102 bool SwapChain::Present(bool vsync) const {
103 const UINT flags = !vsync && m_tearingSupported ? DXGI_PRESENT_ALLOW_TEARING : 0;
104 return BICAMERAL_CHECK_HR(Channel::Gpu, m_swapChain->Present(vsync ? 1 : 0, flags));
105 }
106
107 bool SwapChain::Resize(ID3D12Device* device, uint32_t width, uint32_t height) {
108 for (auto& buffer : m_buffers)
109 buffer.Reset();
110
111 const UINT flags = SWAP_CHAIN_FLAGS_BASE | (m_tearingSupported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0u);
112 if (!BICAMERAL_CHECK_HR(Channel::Gpu, m_swapChain->ResizeBuffers(BUFFER_COUNT, width, height, FORMAT, flags)))
113 return false;
114
115 return AcquireBuffers(device);
116 }
117
118} // namespace bicameral::gpu
bool AcquireBuffers(ID3D12Device *device)
bool Present(bool vsync) const
static constexpr DXGI_FORMAT FORMAT
Definition swap_chain.h:22
std::array< ComPtr< ID3D12Resource >, BUFFER_COUNT > m_buffers
Definition swap_chain.h:55
std::unique_ptr< void, HandleCloser > m_frameLatencyWaitable
Definition swap_chain.h:59
ComPtr< ID3D12DescriptorHeap > m_rtvHeap
Definition swap_chain.h:54
static constexpr uint32_t BUFFER_COUNT
Definition swap_chain.h:21
bool WaitForFrame(uint32_t timeoutMs) const
D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetView(uint32_t index) const
ComPtr< IDXGISwapChain3 > m_swapChain
Definition swap_chain.h:53
bool Resize(ID3D12Device *device, uint32_t width, uint32_t height)
static std::expected< SwapChain, std::string > Create(ID3D12Device *device, IDXGIFactory6 *factory, ID3D12CommandQueue *directQueue, HWND window, uint32_t maxFrameLatency)
#define BICAMERAL_CHECK_HR(channel, expression)
Definition hresult.h:32
std::string DescribeHresult(HResult result)
Definition hresult.cpp:88
void Log(Channel channel, Level level, FormatWithLocation< std::type_identity_t< Args >... > pattern, Args &&... args)
Definition log.h:140