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
8 #include "tools/sk_app/WindowContext.h"
9 #include "tools/sk_app/win/WindowContextFactory_win.h"
10
11 #include "tools/gpu/d3d/D3DTestUtils.h"
12
13 #include "include/core/SkSurface.h"
14 #include "include/gpu/GrDirectContext.h"
15 #include "include/gpu/d3d/GrD3DBackendContext.h"
16
17 #include <d3d12.h>
18 #include <dxgi1_4.h>
19 #include <wrl/client.h>
20
21 #define GR_D3D_CALL_ERRCHECK(X) \
22 do { \
23 HRESULT result = X; \
24 SkASSERT(SUCCEEDED(result)); \
25 if (!SUCCEEDED(result)) { \
26 SkDebugf("Failed Direct3D call. Error: 0x%08lx\n", result); \
27 } \
28 } while (false)
29
30 using namespace Microsoft::WRL;
31
32 namespace sk_app {
33
34 class D3D12WindowContext : public WindowContext {
35 public:
36 D3D12WindowContext(HWND hwnd, const DisplayParams& params);
37 ~D3D12WindowContext() override;
38 void initializeContext();
39 void destroyContext();
40 void setupSurfaces(int width, int height);
41
isValid()42 bool isValid() override {
43 return fDevice.get() != nullptr;
44 }
45
46 sk_sp<SkSurface> getBackbufferSurface() override;
47 void swapBuffers() override;
48
49 void resize(int width, int height) override;
50 void setDisplayParams(const DisplayParams& params) override;
51 private:
52 inline static constexpr int kNumFrames = 2;
53
54 HWND fWindow;
55 gr_cp<ID3D12Device> fDevice;
56 gr_cp<ID3D12CommandQueue> fQueue;
57 gr_cp<IDXGISwapChain3> fSwapChain;
58 gr_cp<ID3D12Resource> fBuffers[kNumFrames];
59 sk_sp<SkSurface> fSurfaces[kNumFrames];
60
61 // Synchronization objects.
62 unsigned int fBufferIndex;
63 HANDLE fFenceEvent;
64 gr_cp<ID3D12Fence> fFence;
65 uint64_t fFenceValues[kNumFrames];
66 };
67
D3D12WindowContext(HWND hwnd,const DisplayParams & params)68 D3D12WindowContext::D3D12WindowContext(HWND hwnd, const DisplayParams& params)
69 : WindowContext(params)
70 , fWindow(hwnd) {
71
72 this->initializeContext();
73 }
74
~D3D12WindowContext()75 D3D12WindowContext::~D3D12WindowContext() {
76 this->destroyContext();
77 }
78
initializeContext()79 void D3D12WindowContext::initializeContext() {
80 GrD3DBackendContext backendContext;
81 sk_gpu_test::CreateD3DBackendContext(&backendContext);
82 fDevice = backendContext.fDevice;
83 fQueue = backendContext.fQueue;
84
85 fContext = GrDirectContext::MakeDirect3D(backendContext, fDisplayParams.fGrContextOptions);
86 SkASSERT(fContext);
87
88 // Make the swapchain
89 RECT windowRect;
90 GetWindowRect(fWindow, &windowRect);
91 unsigned int width = windowRect.right - windowRect.left;
92 unsigned int height = windowRect.bottom - windowRect.top;
93
94 UINT dxgiFactoryFlags = 0;
95 SkDEBUGCODE(dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;)
96
97 gr_cp<IDXGIFactory4> factory;
98 GR_D3D_CALL_ERRCHECK(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
99
100 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
101 swapChainDesc.BufferCount = kNumFrames;
102 swapChainDesc.Width = width;
103 swapChainDesc.Height = height;
104 swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
105 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
106 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
107 swapChainDesc.SampleDesc.Count = 1;
108
109 gr_cp<IDXGISwapChain1> swapChain;
110 GR_D3D_CALL_ERRCHECK(factory->CreateSwapChainForHwnd(
111 fQueue.get(), fWindow, &swapChainDesc, nullptr, nullptr, &swapChain));
112
113 // We don't support fullscreen transitions.
114 GR_D3D_CALL_ERRCHECK(factory->MakeWindowAssociation(fWindow, DXGI_MWA_NO_ALT_ENTER));
115
116 GR_D3D_CALL_ERRCHECK(swapChain->QueryInterface(IID_PPV_ARGS(&fSwapChain)));
117
118 fBufferIndex = fSwapChain->GetCurrentBackBufferIndex();
119
120 fSampleCount = fDisplayParams.fMSAASampleCount;
121
122 this->setupSurfaces(width, height);
123
124 for (int i = 0; i < kNumFrames; ++i) {
125 fFenceValues[i] = 10000; // use a high value to make it easier to track these in PIX
126 }
127 GR_D3D_CALL_ERRCHECK(fDevice->CreateFence(fFenceValues[fBufferIndex], D3D12_FENCE_FLAG_NONE,
128 IID_PPV_ARGS(&fFence)));
129
130 fFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
131 SkASSERT(fFenceEvent);
132
133 fWidth = width;
134 fHeight = height;
135 }
136
setupSurfaces(int width,int height)137 void D3D12WindowContext::setupSurfaces(int width, int height) {
138 // set up base resource info
139 GrD3DTextureResourceInfo info(nullptr,
140 nullptr,
141 D3D12_RESOURCE_STATE_PRESENT,
142 DXGI_FORMAT_R8G8B8A8_UNORM,
143 1,
144 1,
145 0);
146 for (int i = 0; i < kNumFrames; ++i) {
147 GR_D3D_CALL_ERRCHECK(fSwapChain->GetBuffer(i, IID_PPV_ARGS(&fBuffers[i])));
148
149 SkASSERT(fBuffers[i]->GetDesc().Width == (UINT64)width &&
150 fBuffers[i]->GetDesc().Height == (UINT64)height);
151
152 info.fResource = fBuffers[i];
153 if (fSampleCount > 1) {
154 GrBackendTexture backendTexture(width, height, info);
155 fSurfaces[i] = SkSurface::MakeFromBackendTexture(
156 fContext.get(), backendTexture, kTopLeft_GrSurfaceOrigin, fSampleCount,
157 kRGBA_8888_SkColorType, fDisplayParams.fColorSpace, &fDisplayParams.fSurfaceProps);
158 } else {
159 GrBackendRenderTarget backendRT(width, height, info);
160 fSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(
161 fContext.get(), backendRT, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType,
162 fDisplayParams.fColorSpace, &fDisplayParams.fSurfaceProps);
163 }
164 }
165 }
166
destroyContext()167 void D3D12WindowContext::destroyContext() {
168 CloseHandle(fFenceEvent);
169 fFence.reset(nullptr);
170
171 for (int i = 0; i < kNumFrames; ++i) {
172 fSurfaces[i].reset(nullptr);
173 fBuffers[i].reset(nullptr);
174 }
175
176 fSwapChain.reset(nullptr);
177 fQueue.reset(nullptr);
178 fDevice.reset(nullptr);
179 }
180
getBackbufferSurface()181 sk_sp<SkSurface> D3D12WindowContext::getBackbufferSurface() {
182 // Update the frame index.
183 const UINT64 currentFenceValue = fFenceValues[fBufferIndex];
184 fBufferIndex = fSwapChain->GetCurrentBackBufferIndex();
185
186 // If the last frame for this buffer index is not done, wait until it is ready.
187 if (fFence->GetCompletedValue() < fFenceValues[fBufferIndex]) {
188 GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fFenceValues[fBufferIndex], fFenceEvent));
189 WaitForSingleObjectEx(fFenceEvent, INFINITE, FALSE);
190 }
191
192 // Set the fence value for the next frame.
193 fFenceValues[fBufferIndex] = currentFenceValue + 1;
194
195 return fSurfaces[fBufferIndex];
196 }
197
swapBuffers()198 void D3D12WindowContext::swapBuffers() {
199 SkSurface* surface = fSurfaces[fBufferIndex].get();
200
201 GrFlushInfo info;
202 surface->flush(SkSurface::BackendSurfaceAccess::kPresent, info);
203 fContext->submit();
204
205 GR_D3D_CALL_ERRCHECK(fSwapChain->Present(1, 0));
206
207 // Schedule a Signal command in the queue.
208 GR_D3D_CALL_ERRCHECK(fQueue->Signal(fFence.get(), fFenceValues[fBufferIndex]));
209 }
210
resize(int width,int height)211 void D3D12WindowContext::resize(int width, int height) {
212 // Clean up any outstanding resources in command lists
213 fContext->flush({});
214 fContext->submit(true);
215
216 // release the previous surface and backbuffer resources
217 for (int i = 0; i < kNumFrames; ++i) {
218 // Let present complete
219 if (fFence->GetCompletedValue() < fFenceValues[i]) {
220 GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fFenceValues[i], fFenceEvent));
221 WaitForSingleObjectEx(fFenceEvent, INFINITE, FALSE);
222 }
223 fSurfaces[i].reset(nullptr);
224 fBuffers[i].reset(nullptr);
225 }
226
227 GR_D3D_CALL_ERRCHECK(fSwapChain->ResizeBuffers(0, width, height,
228 DXGI_FORMAT_R8G8B8A8_UNORM, 0));
229
230 this->setupSurfaces(width, height);
231
232 fWidth = width;
233 fHeight = height;
234 }
235
setDisplayParams(const DisplayParams & params)236 void D3D12WindowContext::setDisplayParams(const DisplayParams& params) {
237 this->destroyContext();
238 fDisplayParams = params;
239 this->initializeContext();
240 }
241
242 namespace window_context_factory {
243
MakeD3D12ForWin(HWND hwnd,const DisplayParams & params)244 std::unique_ptr<WindowContext> MakeD3D12ForWin(HWND hwnd, const DisplayParams& params) {
245 std::unique_ptr<WindowContext> ctx(new D3D12WindowContext(hwnd, params));
246 if (!ctx->isValid()) {
247 return nullptr;
248 }
249 return ctx;
250 }
251
252 }
253
254 } //namespace sk_app
255