Flutter Engine
The Flutter Engine
D3D12WindowContext_win.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
11#include "include/gpu/GrTypes.h"
18
19#include <d3d12.h>
20#include <dxgi1_4.h>
21#include <wrl/client.h>
22
23#define GR_D3D_CALL_ERRCHECK(X) \
24 do { \
25 HRESULT result = X; \
26 SkASSERT(SUCCEEDED(result)); \
27 if (!SUCCEEDED(result)) { \
28 SkDebugf("Failed Direct3D call. Error: 0x%08lx\n", result); \
29 } \
30 } while (false)
31
32using namespace Microsoft::WRL;
33
36
37namespace {
38
39class D3D12WindowContext : public WindowContext {
40public:
41 D3D12WindowContext(HWND hwnd, const DisplayParams& params);
42 ~D3D12WindowContext() override;
43 void initializeContext();
44 void destroyContext();
45 void setupSurfaces(int width, int height);
46
47 bool isValid() override {
48 return fDevice.get() != nullptr;
49 }
50
52
53 void resize(int width, int height) override;
54 void setDisplayParams(const DisplayParams& params) override;
55private:
56 inline static constexpr int kNumFrames = 2;
57
58 void onSwapBuffers() override;
59
60 HWND fWindow;
61 gr_cp<ID3D12Device> fDevice;
63 gr_cp<IDXGISwapChain3> fSwapChain;
64 gr_cp<ID3D12Resource> fBuffers[kNumFrames];
65 sk_sp<SkSurface> fSurfaces[kNumFrames];
66
67 // Synchronization objects.
68 unsigned int fBufferIndex;
69 HANDLE fFenceEvent;
70 gr_cp<ID3D12Fence> fFence;
71 uint64_t fFenceValues[kNumFrames];
72};
73
74D3D12WindowContext::D3D12WindowContext(HWND hwnd, const DisplayParams& params)
76 , fWindow(hwnd) {
77
78 this->initializeContext();
79}
80
81D3D12WindowContext::~D3D12WindowContext() {
82 this->destroyContext();
83}
84
85void D3D12WindowContext::initializeContext() {
86 GrD3DBackendContext backendContext;
87 sk_gpu_test::CreateD3DBackendContext(&backendContext);
88 fDevice = backendContext.fDevice;
89 fQueue = backendContext.fQueue;
90
91 fContext = GrDirectContext::MakeDirect3D(backendContext, fDisplayParams.fGrContextOptions);
93
94 // Make the swapchain
95 RECT windowRect;
96 GetWindowRect(fWindow, &windowRect);
97 unsigned int width = windowRect.right - windowRect.left;
98 unsigned int height = windowRect.bottom - windowRect.top;
99
100 UINT dxgiFactoryFlags = 0;
101 SkDEBUGCODE(dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;)
102
103 gr_cp<IDXGIFactory4> factory;
104 GR_D3D_CALL_ERRCHECK(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
105
106 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
107 swapChainDesc.BufferCount = kNumFrames;
108 swapChainDesc.Width = width;
109 swapChainDesc.Height = height;
110 swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
111 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
112 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
113 swapChainDesc.SampleDesc.Count = 1;
114
115 gr_cp<IDXGISwapChain1> swapChain;
116 GR_D3D_CALL_ERRCHECK(factory->CreateSwapChainForHwnd(
117 fQueue.get(), fWindow, &swapChainDesc, nullptr, nullptr, &swapChain));
118
119 // We don't support fullscreen transitions.
120 GR_D3D_CALL_ERRCHECK(factory->MakeWindowAssociation(fWindow, DXGI_MWA_NO_ALT_ENTER));
121
122 GR_D3D_CALL_ERRCHECK(swapChain->QueryInterface(IID_PPV_ARGS(&fSwapChain)));
123
124 fBufferIndex = fSwapChain->GetCurrentBackBufferIndex();
125
126 fSampleCount = fDisplayParams.fMSAASampleCount;
127
128 this->setupSurfaces(width, height);
129
130 for (int i = 0; i < kNumFrames; ++i) {
131 fFenceValues[i] = 10000; // use a high value to make it easier to track these in PIX
132 }
133 GR_D3D_CALL_ERRCHECK(fDevice->CreateFence(fFenceValues[fBufferIndex], D3D12_FENCE_FLAG_NONE,
134 IID_PPV_ARGS(&fFence)));
135
136 fFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
137 SkASSERT(fFenceEvent);
138
139 fWidth = width;
140 fHeight = height;
141}
142
143void D3D12WindowContext::setupSurfaces(int width, int height) {
144 // set up base resource info
146 nullptr,
147 D3D12_RESOURCE_STATE_PRESENT,
148 DXGI_FORMAT_R8G8B8A8_UNORM,
149 1,
150 1,
151 0);
152 for (int i = 0; i < kNumFrames; ++i) {
153 GR_D3D_CALL_ERRCHECK(fSwapChain->GetBuffer(i, IID_PPV_ARGS(&fBuffers[i])));
154
155 SkASSERT(fBuffers[i]->GetDesc().Width == (UINT64)width &&
156 fBuffers[i]->GetDesc().Height == (UINT64)height);
157
158 info.fResource = fBuffers[i];
159 if (fSampleCount > 1) {
160 GrBackendTexture backendTexture(width, height, info);
161 fSurfaces[i] = SkSurfaces::WrapBackendTexture(fContext.get(),
162 backendTexture,
164 fSampleCount,
166 fDisplayParams.fColorSpace,
167 &fDisplayParams.fSurfaceProps);
168 } else {
171 backendRT,
174 fDisplayParams.fColorSpace,
175 &fDisplayParams.fSurfaceProps);
176 }
177 }
178}
179
180void D3D12WindowContext::destroyContext() {
181 CloseHandle(fFenceEvent);
182 fFence.reset(nullptr);
183
184 for (int i = 0; i < kNumFrames; ++i) {
185 fSurfaces[i].reset(nullptr);
186 fBuffers[i].reset(nullptr);
187 }
188
189 fSwapChain.reset(nullptr);
190 fQueue.reset(nullptr);
191 fDevice.reset(nullptr);
192}
193
194sk_sp<SkSurface> D3D12WindowContext::getBackbufferSurface() {
195 // Update the frame index.
196 const UINT64 currentFenceValue = fFenceValues[fBufferIndex];
197 fBufferIndex = fSwapChain->GetCurrentBackBufferIndex();
198
199 // If the last frame for this buffer index is not done, wait until it is ready.
200 if (fFence->GetCompletedValue() < fFenceValues[fBufferIndex]) {
201 GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fFenceValues[fBufferIndex], fFenceEvent));
202 WaitForSingleObjectEx(fFenceEvent, INFINITE, FALSE);
203 }
204
205 // Set the fence value for the next frame.
206 fFenceValues[fBufferIndex] = currentFenceValue + 1;
207
208 return fSurfaces[fBufferIndex];
209}
210
211void D3D12WindowContext::onSwapBuffers() {
212 SkSurface* surface = fSurfaces[fBufferIndex].get();
213
216 fContext->submit();
217
218 GR_D3D_CALL_ERRCHECK(fSwapChain->Present(1, 0));
219
220 // Schedule a Signal command in the queue.
221 GR_D3D_CALL_ERRCHECK(fQueue->Signal(fFence.get(), fFenceValues[fBufferIndex]));
222}
223
224void D3D12WindowContext::resize(int width, int height) {
225 // Clean up any outstanding resources in command lists
226 fContext->flush();
227 fContext->submit(GrSyncCpu::kYes);
228
229 // release the previous surface and backbuffer resources
230 for (int i = 0; i < kNumFrames; ++i) {
231 // Let present complete
232 if (fFence->GetCompletedValue() < fFenceValues[i]) {
233 GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fFenceValues[i], fFenceEvent));
234 WaitForSingleObjectEx(fFenceEvent, INFINITE, FALSE);
235 }
236 fSurfaces[i].reset(nullptr);
237 fBuffers[i].reset(nullptr);
238 }
239
240 GR_D3D_CALL_ERRCHECK(fSwapChain->ResizeBuffers(0, width, height,
241 DXGI_FORMAT_R8G8B8A8_UNORM, 0));
242
243 this->setupSurfaces(width, height);
244
245 fWidth = width;
246 fHeight = height;
247}
248
249void D3D12WindowContext::setDisplayParams(const DisplayParams& params) {
250 this->destroyContext();
251 fDisplayParams = params;
252 this->initializeContext();
253}
254
255} // anonymous namespace
256
257namespace skwindow {
258
259std::unique_ptr<WindowContext> MakeD3D12ForWin(HWND hwnd, const DisplayParams& params) {
260 std::unique_ptr<WindowContext> ctx(new D3D12WindowContext(hwnd, params));
261 if (!ctx->isValid()) {
262 return nullptr;
263 }
264 return ctx;
265}
266
267} // namespace skwindow
#define GR_D3D_CALL_ERRCHECK(X)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
const Context & fContext
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
virtual bool isValid()=0
virtual sk_sp< SkSurface > getBackbufferSurface()=0
virtual void setDisplayParams(const DisplayParams &params)=0
virtual void resize(int w, int h)=0
virtual void onSwapBuffers()=0
const EmbeddedViewParams * params
VkSurfaceKHR surface
Definition: main.cc:49
return FALSE
SK_API sk_sp< SkSurface > WrapBackendRenderTarget(GrRecordingContext *context, const GrBackendRenderTarget &backendRenderTarget, GrSurfaceOrigin origin, SkColorType colorType, sk_sp< SkColorSpace > colorSpace, const SkSurfaceProps *surfaceProps, RenderTargetReleaseProc releaseProc=nullptr, ReleaseContext releaseContext=nullptr)
@ kPresent
back-end surface will be used for presenting to screen
SK_API sk_sp< SkSurface > WrapBackendTexture(GrRecordingContext *context, const GrBackendTexture &backendTexture, GrSurfaceOrigin origin, int sampleCnt, SkColorType colorType, sk_sp< SkColorSpace > colorSpace, const SkSurfaceProps *surfaceProps, TextureReleaseProc textureReleaseProc=nullptr, ReleaseContext releaseContext=nullptr)
std::unique_ptr< WindowContext > MakeD3D12ForWin(HWND hwnd, const DisplayParams &params)
int32_t height
int32_t width
gr_cp< ID3D12Device > fDevice
gr_cp< ID3D12CommandQueue > fQueue
unsigned int UINT
Definition: windows_types.h:32
#define CreateEvent
void * HANDLE
Definition: windows_types.h:36