Cogs.Core
SwapChainD3D11.cpp
1#include "SwapChainD3D11.h"
2
3#include "FormatsD3D11.h"
4#include "GraphicsDeviceD3D11.h"
5
6#include "Foundation/Logging/Logger.h"
7#include "Foundation/Platform/WindowData.h"
8
9namespace {
10 Cogs::Logging::Log logger = Cogs::Logging::getLogger("SwapChainD3D11");
11}
12
13Cogs::SwapChainD3D11::SwapChainD3D11(GraphicsDeviceD3D11* device) : graphicsDevice(device) {
14}
15
16Cogs::SwapChainD3D11::~SwapChainD3D11() {
17 if (isFullscreen()) {
18 setFullscreen(false);
19 }
20}
21
22bool Cogs::SwapChainD3D11::initialize(WindowData* winData) {
23 windowData = winData;
24
25 if (windowData) {
26 const GraphicsDeviceSettings& settings = graphicsDevice->getSettings();
27
28 if (windowData->windowHandle) {
29 ResourcePointer<IDXGIDevice> dxgiDevice;
30 ResourcePointer<IDXGIAdapter> dxgiAdapter;
31 ResourcePointer<IDXGIFactory> dxgiFactory;
32 HRESULT hr;
33
34 hr = graphicsDevice->getDevice()->QueryInterface(__uuidof(IDXGIDevice), dxgiDevice.internalVoidPointer());
35 if (FAILED(hr)) {
36 LOG_FATAL(logger, "Could not obtain IDXGIDevice from ID3D11Device: %s", direct3D11ReturnCodeAsString(hr));
37 return false;
38 }
39
40 hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), dxgiAdapter.internalVoidPointer());
41 if (FAILED(hr)) {
42 LOG_FATAL(logger, "Could not obtain IDXGIAdapter from IDXGIDevice: %s", direct3D11ReturnCodeAsString(hr));
43 return false;
44 }
45
46 hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory), dxgiFactory.internalVoidPointer());
47 if (FAILED(hr)) {
48 LOG_FATAL(logger, "Could not obtain IDXGIFactory from IDXGIAdapter: %s", direct3D11ReturnCodeAsString(hr));
49 return false;
50 }
51
52 DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
53
54 swapChainDesc.BufferCount = 2;
55 swapChainDesc.BufferDesc.Width = this->width;
56 swapChainDesc.BufferDesc.Height = this->height;
57 swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
58 swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
59 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
60 swapChainDesc.OutputWindow = windowData->windowHandle;
61 swapChainDesc.SampleDesc.Quality = 0;
62 swapChainDesc.Windowed = true;
63 swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
64
66 swapChainDesc.BufferDesc.Format = Direct3D11::Formats[static_cast<int>(settings.colorFormat)];
67 swapChainDesc.SampleDesc.Count = settings.numSamples;
68 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
69 }
70 else {
71 switch (settings.colorFormat) {
72 case Format::R10G10B10A2_TYPELESS:
73 case Format::R10G10B10A2_UNORM:
74 case Format::R10G10B10A2_UINT: swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; break;
75 default: swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; break;
76 }
77 swapChainDesc.SampleDesc.Count = 1;
78 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
79 }
80
81 hr = dxgiFactory->CreateSwapChain(graphicsDevice->getDevice(), &swapChainDesc, swapChain.internalPointer());
82
83 if (FAILED(hr)) {
84 LOG_FATAL(logger, "Error creating the swap chain: %s", direct3D11ReturnCodeAsString(hr));
85 return false;
86 }
87
88 hr = dxgiFactory->MakeWindowAssociation(windowData->windowHandle, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER);
89
90 if (FAILED(hr)) {
91 LOG_WARNING(logger, "Failed to associated DXGI with the window when creating the swap chain: %s", direct3D11ReturnCodeAsString(hr));
92 }
93 }
94 return recreateOffscreenBuffers() && createRenderTargetView() && createDepthStencilView();
95 }
96 return false;
97}
98
100 inFrame = true;
101
102 if (needResize) {
103 resize();
104 }
105}
106
107void Cogs::SwapChainD3D11::endFrame(uint32_t syncInterval, uint32_t presentFlags) {
108 if ((presentFlags & PresentFlags::NoSwap) == 0) {
109 if (swapChain && backBuffer) {
110 if ((graphicsDevice->getSettings().flags & GraphicsDeviceFlags::UseSwapEffectDiscard) != GraphicsDeviceFlags::UseSwapEffectDiscard) {
112 ID3D11DeviceContext* context;
113
114 graphicsDevice->getDevice()->GetImmediateContext(&context);
115 swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&displayBuffer);
116 context->ResolveSubresource(displayBuffer, 0, backBuffer, 0, Direct3D11::Formats[static_cast<int>(colorFormat)]);
117 }
118
119 uint32_t dxPresentFlags = 0;
120 HRESULT hr = swapChain->Present(syncInterval, dxPresentFlags);
121
122 if (FAILED(hr)) {
123 LOG_ERROR(logger, "Present failed: 0x%x %s", hr, direct3D11ReturnCodeAsString(hr));
124
125 if (hr == DXGI_ERROR_DEVICE_REMOVED) {
126 hr = graphicsDevice->getDevice()->GetDeviceRemovedReason();
127 LOG_ERROR(logger, "Removed Reason: 0x%x %s", hr, direct3D11ReturnCodeAsString(hr));
128 }
129 }
130 }
131 else if (sharedBackBuffer && backBuffer && (sharedBackBuffer != backBuffer)) {
132 ID3D11DeviceContext* context;
133
134 graphicsDevice->getDevice()->GetImmediateContext(&context);
135
136 if (sharedBackBuffer != backBuffer) {
137 D3D11_TEXTURE2D_DESC textureDesc;
138 D3D11_TEXTURE2D_DESC destD;
139
140 backBuffer->GetDesc(&textureDesc);
141 sharedBackBuffer->GetDesc(&destD);
142 context->ResolveSubresource(sharedBackBuffer, 0, backBuffer, 0, textureDesc.Format);
143 }
144 context->Flush();
145 }
146 }
147 inFrame = false;
148}
149
150void Cogs::SwapChainD3D11::setSize(int newWidth, int newHeight, bool forceIt) {
151 if (!isFullscreen()) {
152 if (forceIt || (newWidth != width) || (newHeight != height)) {
153 width = newWidth;
154 height = newHeight;
155
156 if (inFrame || !swapChain) {
157 resize();
158 }
159 else {
160 needResize = true;
161 }
162 }
163 }
164}
165
170 if (swapChain) {
171 if (enabled != isFullscreen()) {
172 if (enabled && needResize) {
173 resize();
174 needResize = false;
175 }
176 swapChain->SetFullscreenState(enabled, nullptr);
177
178 if (!enabled) {
179 needResize = true;
180 }
181 }
182 }
183}
184
186 BOOL state = false;
187
188 if (swapChain) {
189 swapChain->GetFullscreenState(&state, nullptr);
190 }
191 return state;
192}
193
194void Cogs::SwapChainD3D11::resize() {
195 if (width && height) {
196 // We have to release the render target view on the back buffer before resizing the buffer.
197 if (HandleIsValid(renderTarget)) {
198 graphicsDevice->getRenderTargets()->releaseRenderTarget(renderTarget);
200 }
201
202 if (swapChain) {
203 DXGI_SWAP_CHAIN_DESC swapChainDesc;
204
205 swapChain->GetDesc(&swapChainDesc);
206 swapChain->ResizeBuffers(swapChainDesc.BufferCount, width, height, swapChainDesc.BufferDesc.Format, swapChainDesc.Flags);
207 }
208
209 recreateOffscreenBuffers();
210
211 if ((graphicsDevice->getSettings().flags & GraphicsDeviceFlags::UseSharedSurface) == GraphicsDeviceFlags::UseSharedSurface) {
212 graphicsDevice->resizeSharedSurfaces();
213 }
214
215 createRenderTargetView();
216 createDepthStencilView();
217
218 needResize = false;
219 }
220}
221
222bool Cogs::SwapChainD3D11::recreateOffscreenBuffers() {
223 sharedBackBuffer = {};
224 backBuffer = {};
225
226 const GraphicsDeviceSettings& settings = graphicsDevice->getSettings();
227 DXGI_FORMAT backBufferFormat = Direct3D11::Formats[static_cast<int>(((settings.flags & GraphicsDeviceFlags::UseSharedSurface) == GraphicsDeviceFlags::UseSharedSurface) ? TextureFormat::B8G8R8A8 : settings.colorFormat)];
228 D3D11_TEXTURE2D_DESC textureMsDesc;
229
230 textureMsDesc.Width = width;
231 textureMsDesc.Height = height;
232 textureMsDesc.MipLevels = 1;
233 textureMsDesc.ArraySize = 1;
234 textureMsDesc.Format = backBufferFormat;
235 textureMsDesc.SampleDesc.Count = settings.numSamples;
236 textureMsDesc.SampleDesc.Quality = 0;
237 textureMsDesc.Usage = D3D11_USAGE_DEFAULT;
238 textureMsDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
239 textureMsDesc.CPUAccessFlags = 0;
240 textureMsDesc.MiscFlags = 0;
241
242 HRESULT hr = graphicsDevice->getDevice()->CreateTexture2D(&textureMsDesc, nullptr, backBuffer.internalPointer());
243
244 if (FAILED(hr)) {
245 LOG_WARNING(logger, "Failed to create multisampled off-screen back buffer. Falling back to regular surface: %s", direct3D11ReturnCodeAsString(hr));
246 }
247
248 if (!backBuffer || ((settings.flags & GraphicsDeviceFlags::UseSharedSurface) == GraphicsDeviceFlags::UseSharedSurface)) {
249 D3D11_TEXTURE2D_DESC textureDesc;
250
251 textureDesc.Width = width;
252 textureDesc.Height = height;
253 textureDesc.MipLevels = 1;
254 textureDesc.ArraySize = 1;
255 textureDesc.Format = backBufferFormat;
256 textureDesc.SampleDesc.Count = 1;
257 textureDesc.SampleDesc.Quality = 0;
258 textureDesc.Usage = D3D11_USAGE_DEFAULT;
259 textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
260 textureDesc.CPUAccessFlags = 0;
261 textureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
262
263 hr = graphicsDevice->getDevice()->CreateTexture2D(&textureDesc, nullptr, sharedBackBuffer.internalPointer());
264 if (FAILED(hr)) {
265 LOG_FATAL(logger, "Failed to create off-screen back buffer: %s", direct3D11ReturnCodeAsString(hr));
266 return false;
267 }
268 }
269
270 if (!backBuffer) {
271 backBuffer = sharedBackBuffer;
272 }
273 return true;
274}
275
281 ResourcePointer<ID3D11Texture2D> currentBackBuffer;
282
283 if (((graphicsDevice->getSettings().flags & GraphicsDeviceFlags::UseSwapEffectDiscard) == GraphicsDeviceFlags::UseSwapEffectDiscard) && swapChain) {
284 HRESULT hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), currentBackBuffer.internalVoidPointer());
285
286 if (FAILED(hr)) {
287 LOG_ERROR(logger, "Failed to create render target views: %s", direct3D11ReturnCodeAsString(hr));
288 return false;
289 }
290 }
291 else {
292 currentBackBuffer = backBuffer;
293 }
294
295 RenderTargetsD3D11* renderTargets = static_cast<RenderTargetsD3D11*>(graphicsDevice->getRenderTargets());
296
297 if (HandleIsValid(renderTarget)) {
298 renderTargets->releaseRenderTarget(renderTarget);
299 }
300
301 // Create pinned default render target for back buffer.
302 renderTarget = renderTargets->createRenderTarget(currentBackBuffer, true);
303 return true;
304}
305
310 RenderTargetsD3D11* renderTargets = static_cast<RenderTargetsD3D11*>(graphicsDevice->getRenderTargets());
311
312 if (HandleIsValid(depthStencil)) {
313 renderTargets->releaseDepthStencilTarget(depthStencil);
314 }
315 depthStencil = renderTargets->createDepthStencilTarget(renderTarget);
316
317 // Ensure the default depth stencil target is not released by user code.
318 renderTargets->depthStencilViews.pin(depthStencil);
319 return true;
320}
Log implementation class.
Definition: LogManager.h:139
virtual void beginFrame() override
Signal the beginning of a new frame to the graphics device.
bool createDepthStencilView()
Recreates the depth/stencil view used by directx when rendering.
virtual void endFrame(uint32_t syncInterval=0, uint32_t presentFlags=PresentFlags::None) override
Signal the end of a frame to the graphics device.
virtual void setFullscreen(bool enabled) override
Changes this swapchain's fullscreen state if neccessary.
virtual bool isFullscreen() override
Test whether the current swap chain is fullscreen.
bool createRenderTargetView()
Recreates the render target view used by directx when selecting render targets prior to rendering.
virtual void setSize(int newWidth, int newHeight, bool forceIt=false) override
Set the size of the main drawing buffer used by the graphics device in pixels.
const DXGI_FORMAT Formats[]
Must match up to Format definition.
Definition: FormatsD3D11.cpp:6
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ UseSwapEffectDiscard
Under DX11 the default swap effect is FLIP_DISCARD, however there are some systems where this does no...
@ UseSharedSurface
Use shared surface for D3D9 interop.
static const Handle_t InvalidHandle
Represents an invalid handle.
Definition: Common.h:80
@ NoSwap
Disables frame buffer swapping. This will leave the currently presented buffer as is.
Definition: Common.h:181
void releaseRenderTarget(RenderTargetHandle handle)
Release the render target with the given renderTargetHandle.
DepthStencilHandle createDepthStencilTarget(const RenderTargetHandle handle, const DepthStencilViewDescription &depthStencilView)
Creates a depth stencil view using the given description.
void releaseDepthStencilTarget(DepthStencilHandle handle) override
Release the depth target with the given depthStencilHandle.