• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "dawn_native/d3d12/SwapChainD3D12.h"
16 
17 #include "dawn_native/Surface.h"
18 #include "dawn_native/d3d12/D3D12Error.h"
19 #include "dawn_native/d3d12/DeviceD3D12.h"
20 #include "dawn_native/d3d12/TextureD3D12.h"
21 
22 #include <dawn/dawn_wsi.h>
23 
24 #include <windows.ui.xaml.media.dxinterop.h>
25 
26 namespace dawn_native { namespace d3d12 {
27     namespace {
28 
PresentModeToBufferCount(wgpu::PresentMode mode)29         uint32_t PresentModeToBufferCount(wgpu::PresentMode mode) {
30             switch (mode) {
31                 case wgpu::PresentMode::Immediate:
32                 case wgpu::PresentMode::Fifo:
33                     return 2;
34                 case wgpu::PresentMode::Mailbox:
35                     return 3;
36             }
37         }
38 
PresentModeToSwapInterval(wgpu::PresentMode mode)39         uint32_t PresentModeToSwapInterval(wgpu::PresentMode mode) {
40             switch (mode) {
41                 case wgpu::PresentMode::Immediate:
42                 case wgpu::PresentMode::Mailbox:
43                     return 0;
44                 case wgpu::PresentMode::Fifo:
45                     return 1;
46             }
47         }
48 
PresentModeToSwapChainFlags(wgpu::PresentMode mode)49         UINT PresentModeToSwapChainFlags(wgpu::PresentMode mode) {
50             UINT flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
51 
52             if (mode == wgpu::PresentMode::Immediate) {
53                 flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
54             }
55 
56             return flags;
57         }
58 
ToDXGIUsage(wgpu::TextureUsage usage)59         DXGI_USAGE ToDXGIUsage(wgpu::TextureUsage usage) {
60             DXGI_USAGE dxgiUsage = DXGI_CPU_ACCESS_NONE;
61             if (usage & wgpu::TextureUsage::TextureBinding) {
62                 dxgiUsage |= DXGI_USAGE_SHADER_INPUT;
63             }
64             if (usage & wgpu::TextureUsage::StorageBinding) {
65                 dxgiUsage |= DXGI_USAGE_UNORDERED_ACCESS;
66             }
67             if (usage & wgpu::TextureUsage::RenderAttachment) {
68                 dxgiUsage |= DXGI_USAGE_RENDER_TARGET_OUTPUT;
69             }
70             return dxgiUsage;
71         }
72 
73     }  // namespace
74 
75     // OldSwapChain
76 
77     // static
Create(Device * device,const SwapChainDescriptor * descriptor)78     Ref<OldSwapChain> OldSwapChain::Create(Device* device, const SwapChainDescriptor* descriptor) {
79         return AcquireRef(new OldSwapChain(device, descriptor));
80     }
81 
OldSwapChain(Device * device,const SwapChainDescriptor * descriptor)82     OldSwapChain::OldSwapChain(Device* device, const SwapChainDescriptor* descriptor)
83         : OldSwapChainBase(device, descriptor) {
84         const auto& im = GetImplementation();
85         DawnWSIContextD3D12 wsiContext = {};
86         wsiContext.device = ToAPI(GetDevice());
87         im.Init(im.userData, &wsiContext);
88 
89         ASSERT(im.textureUsage != WGPUTextureUsage_None);
90         mTextureUsage = static_cast<wgpu::TextureUsage>(im.textureUsage);
91     }
92 
93     OldSwapChain::~OldSwapChain() = default;
94 
GetNextTextureImpl(const TextureDescriptor * descriptor)95     TextureBase* OldSwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor) {
96         DeviceBase* device = GetDevice();
97         const auto& im = GetImplementation();
98         DawnSwapChainNextTexture next = {};
99         DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
100         if (error) {
101             device->HandleError(InternalErrorType::Internal, error);
102             return nullptr;
103         }
104 
105         ComPtr<ID3D12Resource> d3d12Texture = static_cast<ID3D12Resource*>(next.texture.ptr);
106         Ref<Texture> dawnTexture;
107         if (device->ConsumedError(
108                 Texture::Create(ToBackend(GetDevice()), descriptor, std::move(d3d12Texture)),
109                 &dawnTexture)) {
110             return nullptr;
111         }
112 
113         return dawnTexture.Detach();
114     }
115 
OnBeforePresent(TextureViewBase * view)116     MaybeError OldSwapChain::OnBeforePresent(TextureViewBase* view) {
117         Device* device = ToBackend(GetDevice());
118 
119         CommandRecordingContext* commandContext;
120         DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
121 
122         // Perform the necessary transition for the texture to be presented.
123         ToBackend(view->GetTexture())
124             ->TrackUsageAndTransitionNow(commandContext, mTextureUsage,
125                                          view->GetSubresourceRange());
126 
127         DAWN_TRY(device->ExecutePendingCommandContext());
128 
129         return {};
130     }
131 
132     // SwapChain
133 
134     // static
Create(Device * device,Surface * surface,NewSwapChainBase * previousSwapChain,const SwapChainDescriptor * descriptor)135     ResultOrError<Ref<SwapChain>> SwapChain::Create(Device* device,
136                                                 Surface* surface,
137                                                 NewSwapChainBase* previousSwapChain,
138                                                 const SwapChainDescriptor* descriptor) {
139         Ref<SwapChain> swapchain = AcquireRef(new SwapChain(device, surface, descriptor));
140         DAWN_TRY(swapchain->Initialize(previousSwapChain));
141         return swapchain;
142     }
143 
144     SwapChain::~SwapChain() = default;
145 
DestroyImpl()146     void SwapChain::DestroyImpl() {
147         SwapChainBase::DestroyImpl();
148         DetachFromSurface();
149     }
150 
151     // Initializes the swapchain on the surface. Note that `previousSwapChain` may or may not be
152     // nullptr. If it is not nullptr it means that it is the swapchain previously in use on the
153     // surface and that we have a chance to reuse it's underlying IDXGISwapChain and "buffers".
Initialize(NewSwapChainBase * previousSwapChain)154     MaybeError SwapChain::Initialize(NewSwapChainBase* previousSwapChain) {
155         ASSERT(GetSurface()->GetType() == Surface::Type::WindowsHWND);
156 
157         // Precompute the configuration parameters we want for the DXGI swapchain.
158         mConfig.bufferCount = PresentModeToBufferCount(GetPresentMode());
159         mConfig.format = D3D12TextureFormat(GetFormat());
160         mConfig.swapChainFlags = PresentModeToSwapChainFlags(GetPresentMode());
161         mConfig.usage = ToDXGIUsage(GetUsage());
162 
163         // There is no previous swapchain so we can create one directly and don't have anything else
164         // to do.
165         if (previousSwapChain == nullptr) {
166             return InitializeSwapChainFromScratch();
167         }
168 
169         // TODO(crbug.com/dawn/269): figure out what should happen when surfaces are used by
170         // multiple backends one after the other. It probably needs to block until the backend
171         // and GPU are completely finished with the previous swapchain.
172         DAWN_INVALID_IF(previousSwapChain->GetBackendType() != wgpu::BackendType::D3D12,
173                         "D3D12 SwapChain cannot switch backend types from %s to %s.",
174                         previousSwapChain->GetBackendType(), wgpu::BackendType::D3D12);
175 
176         // TODO(crbug.com/dawn/269): use ToBackend once OldSwapChainBase is removed.
177         SwapChain* previousD3D12SwapChain = static_cast<SwapChain*>(previousSwapChain);
178 
179         // TODO(crbug.com/dawn/269): Figure out switching an HWND between devices, it might
180         // require just losing the reference to the swapchain, but might also need to wait for
181         // all previous operations to complete.
182         DAWN_INVALID_IF(GetDevice() != previousSwapChain->GetDevice(),
183                         "D3D12 SwapChain cannot switch between D3D Devices");
184 
185         // The previous swapchain is on the same device so we want to reuse it but it is still not
186         // always possible. Because DXGI requires that a new swapchain be created if the
187         // DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING flag is changed.
188         bool canReuseSwapChain =
189             ((mConfig.swapChainFlags ^ previousD3D12SwapChain->mConfig.swapChainFlags) &
190              DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) == 0;
191 
192         // We can't reuse the previous swapchain, so we destroy it and wait for all of its reference
193         // to be forgotten (otherwise DXGI complains that there are outstanding references).
194         if (!canReuseSwapChain) {
195             DAWN_TRY(previousD3D12SwapChain->DetachAndWaitForDeallocation());
196             return InitializeSwapChainFromScratch();
197         }
198 
199         // After all this we know we can reuse the swapchain, see if it is possible to also reuse
200         // the buffers.
201         mDXGISwapChain = std::move(previousD3D12SwapChain->mDXGISwapChain);
202 
203         bool canReuseBuffers = GetWidth() == previousSwapChain->GetWidth() &&
204                                GetHeight() == previousSwapChain->GetHeight() &&
205                                GetFormat() == previousSwapChain->GetFormat() &&
206                                GetPresentMode() == previousSwapChain->GetPresentMode();
207         if (canReuseBuffers) {
208             mBuffers = std::move(previousD3D12SwapChain->mBuffers);
209             mBufferLastUsedSerials = std::move(previousD3D12SwapChain->mBufferLastUsedSerials);
210             mCurrentBuffer = previousD3D12SwapChain->mCurrentBuffer;
211             return {};
212         }
213 
214         // We can't reuse the buffers so we need to resize, IDXGSwapChain->ResizeBuffers requires
215         // that all references to buffers are lost before it is called. Contrary to D3D11, the
216         // application is responsible for keeping references to the buffers until the GPU is done
217         // using them so we have no choice but to synchrounously wait for all operations to complete
218         // on the previous swapchain and then lose references to its buffers.
219         DAWN_TRY(previousD3D12SwapChain->DetachAndWaitForDeallocation());
220         DAWN_TRY(
221             CheckHRESULT(mDXGISwapChain->ResizeBuffers(mConfig.bufferCount, GetWidth(), GetHeight(),
222                                                        mConfig.format, mConfig.swapChainFlags),
223                          "IDXGISwapChain::ResizeBuffer"));
224         return CollectSwapChainBuffers();
225     }
226 
InitializeSwapChainFromScratch()227     MaybeError SwapChain::InitializeSwapChainFromScratch() {
228         ASSERT(mDXGISwapChain == nullptr);
229 
230         Device* device = ToBackend(GetDevice());
231 
232         DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
233         swapChainDesc.Width = GetWidth();
234         swapChainDesc.Height = GetHeight();
235         swapChainDesc.Format = mConfig.format;
236         swapChainDesc.Stereo = false;
237         swapChainDesc.SampleDesc.Count = 1;
238         swapChainDesc.SampleDesc.Quality = 0;
239         swapChainDesc.BufferUsage = mConfig.usage;
240         swapChainDesc.BufferCount = mConfig.bufferCount;
241         swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
242         swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
243         swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
244         swapChainDesc.Flags = mConfig.swapChainFlags;
245 
246         ComPtr<IDXGIFactory2> factory2 = nullptr;
247         DAWN_TRY(CheckHRESULT(device->GetFactory()->QueryInterface(IID_PPV_ARGS(&factory2)),
248                               "Getting IDXGIFactory2"));
249 
250         ComPtr<IDXGISwapChain1> swapChain1;
251         switch (GetSurface()->GetType()) {
252             case Surface::Type::WindowsHWND: {
253                 DAWN_TRY(CheckHRESULT(
254                     factory2->CreateSwapChainForHwnd(device->GetCommandQueue().Get(),
255                                                      static_cast<HWND>(GetSurface()->GetHWND()),
256                                                      &swapChainDesc, nullptr, nullptr, &swapChain1),
257                     "Creating the IDXGISwapChain1"));
258                 break;
259             }
260             case Surface::Type::WindowsCoreWindow: {
261                 DAWN_TRY(CheckHRESULT(
262                     factory2->CreateSwapChainForCoreWindow(device->GetCommandQueue().Get(),
263                                                            GetSurface()->GetCoreWindow(),
264                                                            &swapChainDesc, nullptr, &swapChain1),
265                     "Creating the IDXGISwapChain1"));
266                 break;
267             }
268             case Surface::Type::WindowsSwapChainPanel: {
269                 DAWN_TRY(CheckHRESULT(
270                     factory2->CreateSwapChainForComposition(device->GetCommandQueue().Get(),
271                                                             &swapChainDesc, nullptr, &swapChain1),
272                     "Creating the IDXGISwapChain1"));
273                 ComPtr<ISwapChainPanelNative> swapChainPanelNative;
274                 DAWN_TRY(CheckHRESULT(GetSurface()->GetSwapChainPanel()->QueryInterface(
275                                           IID_PPV_ARGS(&swapChainPanelNative)),
276                                       "Getting ISwapChainPanelNative"));
277                 DAWN_TRY(CheckHRESULT(swapChainPanelNative->SetSwapChain(swapChain1.Get()),
278                                       "Setting SwapChain"));
279                 break;
280             }
281             default:
282                 UNREACHABLE();
283         }
284 
285         DAWN_TRY(CheckHRESULT(swapChain1.As(&mDXGISwapChain), "Gettting IDXGISwapChain1"));
286 
287         return CollectSwapChainBuffers();
288     }
289 
CollectSwapChainBuffers()290     MaybeError SwapChain::CollectSwapChainBuffers() {
291         ASSERT(mDXGISwapChain != nullptr);
292         ASSERT(mBuffers.empty());
293 
294         mBuffers.resize(mConfig.bufferCount);
295         for (uint32_t i = 0; i < mConfig.bufferCount; i++) {
296             DAWN_TRY(CheckHRESULT(mDXGISwapChain->GetBuffer(i, IID_PPV_ARGS(&mBuffers[i])),
297                                   "Getting IDXGISwapChain buffer"));
298         }
299 
300         // Pretend all the buffers were last used at the beginning of time.
301         mBufferLastUsedSerials.resize(mConfig.bufferCount, ExecutionSerial(0));
302         return {};
303     }
304 
PresentImpl()305     MaybeError SwapChain::PresentImpl() {
306         Device* device = ToBackend(GetDevice());
307 
308         // Transition the texture to the present state as required by IDXGISwapChain1::Present()
309         // TODO(crbug.com/dawn/269): Remove the need for this by eagerly transitioning the
310         // presentable texture to present at the end of submits that use them.
311         CommandRecordingContext* commandContext;
312         DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
313         mApiTexture->TrackUsageAndTransitionNow(commandContext, kPresentTextureUsage,
314                                                 mApiTexture->GetAllSubresources());
315         DAWN_TRY(device->ExecutePendingCommandContext());
316 
317         // Do the actual present. DXGI_STATUS_OCCLUDED is a valid return value that's just a
318         // message to the application that it could stop rendering.
319         HRESULT presentResult =
320             mDXGISwapChain->Present(PresentModeToSwapInterval(GetPresentMode()), 0);
321         if (presentResult != DXGI_STATUS_OCCLUDED) {
322             DAWN_TRY(CheckHRESULT(presentResult, "IDXGISwapChain::Present"));
323         }
324 
325         // Record that "new" is the last time the buffer has been used.
326         DAWN_TRY(device->NextSerial());
327         mBufferLastUsedSerials[mCurrentBuffer] = device->GetPendingCommandSerial();
328 
329         mApiTexture->APIDestroy();
330         mApiTexture = nullptr;
331 
332         return {};
333     }
334 
GetCurrentTextureViewImpl()335     ResultOrError<TextureViewBase*> SwapChain::GetCurrentTextureViewImpl() {
336         Device* device = ToBackend(GetDevice());
337 
338         // Synchronously wait until previous operations on the next swapchain buffer are finished.
339         // This is the logic that performs frame pacing.
340         // TODO(crbug.com/dawn/269): Consider whether this should  be lifted for Mailbox so that
341         // there is not frame pacing.
342         mCurrentBuffer = mDXGISwapChain->GetCurrentBackBufferIndex();
343         DAWN_TRY(device->WaitForSerial(mBufferLastUsedSerials[mCurrentBuffer]));
344 
345         // Create the API side objects for this use of the swapchain's buffer.
346         TextureDescriptor descriptor = GetSwapChainBaseTextureDescriptor(this);
347         DAWN_TRY_ASSIGN(mApiTexture, Texture::Create(ToBackend(GetDevice()), &descriptor,
348                                                      mBuffers[mCurrentBuffer]));
349 
350         // TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
351         return mApiTexture->APICreateView();
352     }
353 
DetachAndWaitForDeallocation()354     MaybeError SwapChain::DetachAndWaitForDeallocation() {
355         DetachFromSurface();
356 
357         // DetachFromSurface calls Texture->Destroy that enqueues the D3D12 resource in a
358         // SerialQueue with the current "pending serial" so that we don't destroy the texture
359         // before it is finished being used. Flush the commands and wait for that serial to be
360         // passed, then Tick the device to make sure the reference to the D3D12 texture is removed.
361         Device* device = ToBackend(GetDevice());
362         DAWN_TRY(device->NextSerial());
363         DAWN_TRY(device->WaitForSerial(device->GetLastSubmittedCommandSerial()));
364         return device->TickImpl();
365     }
366 
DetachFromSurfaceImpl()367     void SwapChain::DetachFromSurfaceImpl() {
368         if (mApiTexture != nullptr) {
369             mApiTexture->APIDestroy();
370             mApiTexture = nullptr;
371         }
372 
373         mDXGISwapChain = nullptr;
374         mBuffers.clear();
375     }
376 
377 }}  // namespace dawn_native::d3d12
378