xref: /aosp_15_r20/external/skia/tools/window/win/D3D12WindowContext_win.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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