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 "include/core/SkSurface.h"
9 #include "include/gpu/GrBackendSurface.h"
10 #include "include/gpu/GrDirectContext.h"
11 #include "include/gpu/GrTypes.h"
12 #include "include/gpu/d3d/GrD3DBackendContext.h"
13 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
14 #include "tools/gpu/d3d/D3DTestUtils.h"
15 #include "tools/window/DisplayParams.h"
16 #include "tools/window/WindowContext.h"
17 #include "tools/window/win/WindowContextFactory_win.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
32 using namespace Microsoft::WRL;
33
34 using skwindow::DisplayParams;
35 using skwindow::WindowContext;
36
37 namespace {
38
39 class D3D12WindowContext : public WindowContext {
40 public:
41 D3D12WindowContext(HWND hwnd, const DisplayParams& params);
42 ~D3D12WindowContext() override;
43 void initializeContext();
44 void destroyContext();
45 void setupSurfaces(int width, int height);
46
isValid()47 bool isValid() override {
48 return fDevice.get() != nullptr;
49 }
50
51 sk_sp<SkSurface> getBackbufferSurface() override;
52
53 void resize(int width, int height) override;
54 void setDisplayParams(const DisplayParams& params) override;
55 private:
56 inline static constexpr int kNumFrames = 2;
57
58 void onSwapBuffers() override;
59
60 HWND fWindow;
61 gr_cp<ID3D12Device> fDevice;
62 gr_cp<ID3D12CommandQueue> fQueue;
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
D3D12WindowContext(HWND hwnd,const DisplayParams & params)74 D3D12WindowContext::D3D12WindowContext(HWND hwnd, const DisplayParams& params)
75 : WindowContext(params)
76 , fWindow(hwnd) {
77
78 this->initializeContext();
79 }
80
~D3D12WindowContext()81 D3D12WindowContext::~D3D12WindowContext() {
82 this->destroyContext();
83 }
84
initializeContext()85 void 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);
92 SkASSERT(fContext);
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
setupSurfaces(int width,int height)143 void D3D12WindowContext::setupSurfaces(int width, int height) {
144 // set up base resource info
145 GrD3DTextureResourceInfo info(nullptr,
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,
163 kTopLeft_GrSurfaceOrigin,
164 fSampleCount,
165 kRGBA_8888_SkColorType,
166 fDisplayParams.fColorSpace,
167 &fDisplayParams.fSurfaceProps);
168 } else {
169 GrBackendRenderTarget backendRT(width, height, info);
170 fSurfaces[i] = SkSurfaces::WrapBackendRenderTarget(fContext.get(),
171 backendRT,
172 kTopLeft_GrSurfaceOrigin,
173 kRGBA_8888_SkColorType,
174 fDisplayParams.fColorSpace,
175 &fDisplayParams.fSurfaceProps);
176 }
177 }
178 }
179
destroyContext()180 void 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
getBackbufferSurface()194 sk_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
onSwapBuffers()211 void D3D12WindowContext::onSwapBuffers() {
212 SkSurface* surface = fSurfaces[fBufferIndex].get();
213
214 GrFlushInfo info;
215 fContext->flush(surface, SkSurfaces::BackendSurfaceAccess::kPresent, info);
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
resize(int width,int height)224 void 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
setDisplayParams(const DisplayParams & params)249 void D3D12WindowContext::setDisplayParams(const DisplayParams& params) {
250 this->destroyContext();
251 fDisplayParams = params;
252 this->initializeContext();
253 }
254
255 } // anonymous namespace
256
257 namespace skwindow {
258
MakeD3D12ForWin(HWND hwnd,const DisplayParams & params)259 std::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
268