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/ganesh/GrBackendSurface.h"
10 #include "include/gpu/ganesh/GrDirectContext.h"
11 #include "include/gpu/ganesh/GrTypes.h"
12 #include "include/gpu/ganesh/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, std::unique_ptr<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(std::unique_ptr<const DisplayParams> params) override;
55
56 private:
57 inline static constexpr int kNumFrames = 2;
58
59 void onSwapBuffers() override;
60
61 HWND fWindow;
62 gr_cp<ID3D12Device> fDevice;
63 gr_cp<ID3D12CommandQueue> fQueue;
64 gr_cp<IDXGISwapChain3> fSwapChain;
65 gr_cp<ID3D12Resource> fBuffers[kNumFrames];
66 sk_sp<SkSurface> fSurfaces[kNumFrames];
67
68 // Synchronization objects.
69 unsigned int fBufferIndex;
70 HANDLE fFenceEvent;
71 gr_cp<ID3D12Fence> fFence;
72 uint64_t fFenceValues[kNumFrames];
73 };
74
D3D12WindowContext(HWND hwnd,std::unique_ptr<const DisplayParams> params)75 D3D12WindowContext::D3D12WindowContext(HWND hwnd, std::unique_ptr<const DisplayParams> params)
76 : WindowContext(std::move(params)), fWindow(hwnd) {
77 this->initializeContext();
78 }
79
~D3D12WindowContext()80 D3D12WindowContext::~D3D12WindowContext() {
81 this->destroyContext();
82 }
83
initializeContext()84 void D3D12WindowContext::initializeContext() {
85 GrD3DBackendContext backendContext;
86 sk_gpu_test::CreateD3DBackendContext(&backendContext);
87 fDevice = backendContext.fDevice;
88 fQueue = backendContext.fQueue;
89
90 fContext = GrDirectContext::MakeDirect3D(backendContext, fDisplayParams->grContextOptions());
91 SkASSERT(fContext);
92
93 // Make the swapchain
94 RECT windowRect;
95 GetWindowRect(fWindow, &windowRect);
96 unsigned int width = windowRect.right - windowRect.left;
97 unsigned int height = windowRect.bottom - windowRect.top;
98
99 UINT dxgiFactoryFlags = 0;
100 SkDEBUGCODE(dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;)
101
102 gr_cp<IDXGIFactory4> factory;
103 GR_D3D_CALL_ERRCHECK(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
104
105 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
106 swapChainDesc.BufferCount = kNumFrames;
107 swapChainDesc.Width = width;
108 swapChainDesc.Height = height;
109 swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
110 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
111 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
112 swapChainDesc.SampleDesc.Count = 1;
113
114 gr_cp<IDXGISwapChain1> swapChain;
115 GR_D3D_CALL_ERRCHECK(factory->CreateSwapChainForHwnd(
116 fQueue.get(), fWindow, &swapChainDesc, nullptr, nullptr, &swapChain));
117
118 // We don't support fullscreen transitions.
119 GR_D3D_CALL_ERRCHECK(factory->MakeWindowAssociation(fWindow, DXGI_MWA_NO_ALT_ENTER));
120
121 GR_D3D_CALL_ERRCHECK(swapChain->QueryInterface(IID_PPV_ARGS(&fSwapChain)));
122
123 fBufferIndex = fSwapChain->GetCurrentBackBufferIndex();
124
125 fSampleCount = fDisplayParams->msaaSampleCount();
126
127 this->setupSurfaces(width, height);
128
129 for (int i = 0; i < kNumFrames; ++i) {
130 fFenceValues[i] = 10000; // use a high value to make it easier to track these in PIX
131 }
132 GR_D3D_CALL_ERRCHECK(fDevice->CreateFence(fFenceValues[fBufferIndex], D3D12_FENCE_FLAG_NONE,
133 IID_PPV_ARGS(&fFence)));
134
135 fFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
136 SkASSERT(fFenceEvent);
137
138 fWidth = width;
139 fHeight = height;
140 }
141
setupSurfaces(int width,int height)142 void D3D12WindowContext::setupSurfaces(int width, int height) {
143 // set up base resource info
144 GrD3DTextureResourceInfo info(nullptr,
145 nullptr,
146 D3D12_RESOURCE_STATE_PRESENT,
147 DXGI_FORMAT_R8G8B8A8_UNORM,
148 1,
149 1,
150 0);
151 for (int i = 0; i < kNumFrames; ++i) {
152 GR_D3D_CALL_ERRCHECK(fSwapChain->GetBuffer(i, IID_PPV_ARGS(&fBuffers[i])));
153
154 SkASSERT(fBuffers[i]->GetDesc().Width == (UINT64)width &&
155 fBuffers[i]->GetDesc().Height == (UINT64)height);
156
157 info.fResource = fBuffers[i];
158 if (fSampleCount > 1) {
159 GrBackendTexture backendTexture(width, height, info);
160 fSurfaces[i] = SkSurfaces::WrapBackendTexture(fContext.get(),
161 backendTexture,
162 kTopLeft_GrSurfaceOrigin,
163 fSampleCount,
164 kRGBA_8888_SkColorType,
165 fDisplayParams->colorSpace(),
166 &fDisplayParams->surfaceProps());
167 } else {
168 GrBackendRenderTarget backendRT(width, height, info);
169 fSurfaces[i] = SkSurfaces::WrapBackendRenderTarget(fContext.get(),
170 backendRT,
171 kTopLeft_GrSurfaceOrigin,
172 kRGBA_8888_SkColorType,
173 fDisplayParams->colorSpace(),
174 &fDisplayParams->surfaceProps());
175 }
176 }
177 }
178
destroyContext()179 void D3D12WindowContext::destroyContext() {
180 CloseHandle(fFenceEvent);
181 fFence.reset(nullptr);
182
183 for (int i = 0; i < kNumFrames; ++i) {
184 fSurfaces[i].reset(nullptr);
185 fBuffers[i].reset(nullptr);
186 }
187
188 fSwapChain.reset(nullptr);
189 fQueue.reset(nullptr);
190 fDevice.reset(nullptr);
191 }
192
getBackbufferSurface()193 sk_sp<SkSurface> D3D12WindowContext::getBackbufferSurface() {
194 // Update the frame index.
195 const UINT64 currentFenceValue = fFenceValues[fBufferIndex];
196 fBufferIndex = fSwapChain->GetCurrentBackBufferIndex();
197
198 // If the last frame for this buffer index is not done, wait until it is ready.
199 if (fFence->GetCompletedValue() < fFenceValues[fBufferIndex]) {
200 GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fFenceValues[fBufferIndex], fFenceEvent));
201 WaitForSingleObjectEx(fFenceEvent, INFINITE, FALSE);
202 }
203
204 // Set the fence value for the next frame.
205 fFenceValues[fBufferIndex] = currentFenceValue + 1;
206
207 return fSurfaces[fBufferIndex];
208 }
209
onSwapBuffers()210 void D3D12WindowContext::onSwapBuffers() {
211 SkSurface* surface = fSurfaces[fBufferIndex].get();
212
213 GrFlushInfo info;
214 fContext->flush(surface, SkSurfaces::BackendSurfaceAccess::kPresent, info);
215 fContext->submit();
216
217 GR_D3D_CALL_ERRCHECK(fSwapChain->Present(1, 0));
218
219 // Schedule a Signal command in the queue.
220 GR_D3D_CALL_ERRCHECK(fQueue->Signal(fFence.get(), fFenceValues[fBufferIndex]));
221 }
222
resize(int width,int height)223 void D3D12WindowContext::resize(int width, int height) {
224 // Clean up any outstanding resources in command lists
225 fContext->flush();
226 fContext->submit(GrSyncCpu::kYes);
227
228 // release the previous surface and backbuffer resources
229 for (int i = 0; i < kNumFrames; ++i) {
230 // Let present complete
231 if (fFence->GetCompletedValue() < fFenceValues[i]) {
232 GR_D3D_CALL_ERRCHECK(fFence->SetEventOnCompletion(fFenceValues[i], fFenceEvent));
233 WaitForSingleObjectEx(fFenceEvent, INFINITE, FALSE);
234 }
235 fSurfaces[i].reset(nullptr);
236 fBuffers[i].reset(nullptr);
237 }
238
239 GR_D3D_CALL_ERRCHECK(fSwapChain->ResizeBuffers(0, width, height,
240 DXGI_FORMAT_R8G8B8A8_UNORM, 0));
241
242 this->setupSurfaces(width, height);
243
244 fWidth = width;
245 fHeight = height;
246 }
247
setDisplayParams(std::unique_ptr<const DisplayParams> params)248 void D3D12WindowContext::setDisplayParams(std::unique_ptr<const DisplayParams> params) {
249 this->destroyContext();
250 fDisplayParams = std::move(params);
251 this->initializeContext();
252 }
253
254 } // anonymous namespace
255
256 namespace skwindow {
257
MakeD3D12ForWin(HWND hwnd,std::unique_ptr<const DisplayParams> params)258 std::unique_ptr<WindowContext> MakeD3D12ForWin(HWND hwnd,
259 std::unique_ptr<const DisplayParams> params) {
260 std::unique_ptr<WindowContext> ctx(new D3D12WindowContext(hwnd, std::move(params)));
261 if (!ctx->isValid()) {
262 return nullptr;
263 }
264 return ctx;
265 }
266
267 } // namespace skwindow
268