• 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 "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