• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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