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/SwapChain.h"
16 
17 #include "common/Constants.h"
18 #include "dawn_native/Adapter.h"
19 #include "dawn_native/Device.h"
20 #include "dawn_native/Surface.h"
21 #include "dawn_native/Texture.h"
22 #include "dawn_native/ValidationUtils_autogen.h"
23 
24 namespace dawn_native {
25 
26     namespace {
27 
28         class ErrorSwapChain : public SwapChainBase {
29           public:
ErrorSwapChain(DeviceBase * device)30             ErrorSwapChain(DeviceBase* device) : SwapChainBase(device, ObjectBase::kError) {
31             }
32 
33           private:
Configure(wgpu::TextureFormat format,wgpu::TextureUsage allowedUsage,uint32_t width,uint32_t height)34             void Configure(wgpu::TextureFormat format,
35                            wgpu::TextureUsage allowedUsage,
36                            uint32_t width,
37                            uint32_t height) override {
38                 GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain"));
39             }
40 
GetCurrentTextureView()41             TextureViewBase* GetCurrentTextureView() override {
42                 GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain"));
43                 return TextureViewBase::MakeError(GetDevice());
44             }
45 
Present()46             void Present() override {
47                 GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain"));
48             }
49         };
50 
51     }  // anonymous namespace
52 
ValidateSwapChainDescriptor(const DeviceBase * device,const Surface * surface,const SwapChainDescriptor * descriptor)53     MaybeError ValidateSwapChainDescriptor(const DeviceBase* device,
54                                            const Surface* surface,
55                                            const SwapChainDescriptor* descriptor) {
56         if (descriptor->implementation != 0) {
57             if (surface != nullptr) {
58                 return DAWN_VALIDATION_ERROR(
59                     "Exactly one of surface or implementation must be set");
60             }
61 
62             DawnSwapChainImplementation* impl =
63                 reinterpret_cast<DawnSwapChainImplementation*>(descriptor->implementation);
64 
65             if (!impl->Init || !impl->Destroy || !impl->Configure || !impl->GetNextTexture ||
66                 !impl->Present) {
67                 return DAWN_VALIDATION_ERROR("Implementation is incomplete");
68             }
69 
70         } else {
71             if (surface == nullptr) {
72                 return DAWN_VALIDATION_ERROR(
73                     "At least one of surface or implementation must be set");
74             }
75 
76             DAWN_TRY(ValidatePresentMode(descriptor->presentMode));
77 
78             // TODO(cwallez@chromium.org): Lift this restriction once
79             // wgpu::Instance::GetPreferredSurfaceFormat is implemented.
80             if (descriptor->format != wgpu::TextureFormat::BGRA8Unorm) {
81                 return DAWN_VALIDATION_ERROR("Format must (currently) be BGRA8Unorm");
82             }
83 
84             if (descriptor->usage != wgpu::TextureUsage::OutputAttachment) {
85                 return DAWN_VALIDATION_ERROR("Usage must (currently) be OutputAttachment");
86             }
87 
88             if (descriptor->width == 0 || descriptor->height == 0) {
89                 return DAWN_VALIDATION_ERROR("Swapchain size can't be empty");
90             }
91 
92             if (descriptor->width > kMaxTextureSize || descriptor->height > kMaxTextureSize) {
93                 return DAWN_VALIDATION_ERROR("Swapchain size too big");
94             }
95         }
96 
97         return {};
98     }
99 
GetSwapChainBaseTextureDescriptor(NewSwapChainBase * swapChain)100     TextureDescriptor GetSwapChainBaseTextureDescriptor(NewSwapChainBase* swapChain) {
101         TextureDescriptor desc;
102         desc.usage = swapChain->GetUsage();
103         desc.dimension = wgpu::TextureDimension::e2D;
104         desc.size = {swapChain->GetWidth(), swapChain->GetHeight(), 1};
105         desc.arrayLayerCount = 1;
106         desc.format = swapChain->GetFormat();
107         desc.mipLevelCount = 1;
108         desc.sampleCount = 1;
109 
110         return desc;
111     }
112 
113     // SwapChainBase
114 
SwapChainBase(DeviceBase * device)115     SwapChainBase::SwapChainBase(DeviceBase* device) : ObjectBase(device) {
116     }
117 
SwapChainBase(DeviceBase * device,ObjectBase::ErrorTag tag)118     SwapChainBase::SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag)
119         : ObjectBase(device, tag) {
120     }
121 
~SwapChainBase()122     SwapChainBase::~SwapChainBase() {
123     }
124 
125     // static
MakeError(DeviceBase * device)126     SwapChainBase* SwapChainBase::MakeError(DeviceBase* device) {
127         return new ErrorSwapChain(device);
128     }
129 
130     // OldSwapChainBase
131 
OldSwapChainBase(DeviceBase * device,const SwapChainDescriptor * descriptor)132     OldSwapChainBase::OldSwapChainBase(DeviceBase* device, const SwapChainDescriptor* descriptor)
133         : SwapChainBase(device),
134           mImplementation(
135               *reinterpret_cast<DawnSwapChainImplementation*>(descriptor->implementation)) {
136     }
137 
~OldSwapChainBase()138     OldSwapChainBase::~OldSwapChainBase() {
139         if (!IsError()) {
140             const auto& im = GetImplementation();
141             im.Destroy(im.userData);
142         }
143     }
144 
Configure(wgpu::TextureFormat format,wgpu::TextureUsage allowedUsage,uint32_t width,uint32_t height)145     void OldSwapChainBase::Configure(wgpu::TextureFormat format,
146                                      wgpu::TextureUsage allowedUsage,
147                                      uint32_t width,
148                                      uint32_t height) {
149         if (GetDevice()->ConsumedError(ValidateConfigure(format, allowedUsage, width, height))) {
150             return;
151         }
152         ASSERT(!IsError());
153 
154         allowedUsage |= wgpu::TextureUsage::Present;
155 
156         mFormat = format;
157         mAllowedUsage = allowedUsage;
158         mWidth = width;
159         mHeight = height;
160         mImplementation.Configure(mImplementation.userData, static_cast<WGPUTextureFormat>(format),
161                                   static_cast<WGPUTextureUsage>(allowedUsage), width, height);
162     }
163 
GetCurrentTextureView()164     TextureViewBase* OldSwapChainBase::GetCurrentTextureView() {
165         if (GetDevice()->ConsumedError(ValidateGetCurrentTextureView())) {
166             return TextureViewBase::MakeError(GetDevice());
167         }
168         ASSERT(!IsError());
169 
170         // Return the same current texture view until Present is called.
171         if (mCurrentTextureView.Get() != nullptr) {
172             // Calling GetCurrentTextureView always returns a new reference so add it even when
173             // reuse the existing texture view.
174             mCurrentTextureView->Reference();
175             return mCurrentTextureView.Get();
176         }
177 
178         // Create the backing texture and the view.
179         TextureDescriptor descriptor;
180         descriptor.dimension = wgpu::TextureDimension::e2D;
181         descriptor.size.width = mWidth;
182         descriptor.size.height = mHeight;
183         descriptor.size.depth = 1;
184         descriptor.arrayLayerCount = 1;
185         descriptor.sampleCount = 1;
186         descriptor.format = mFormat;
187         descriptor.mipLevelCount = 1;
188         descriptor.usage = mAllowedUsage;
189 
190         // Get the texture but remove the external refcount because it is never passed outside
191         // of dawn_native
192         mCurrentTexture = AcquireRef(GetNextTextureImpl(&descriptor));
193 
194         mCurrentTextureView = mCurrentTexture->CreateView(nullptr);
195         return mCurrentTextureView.Get();
196     }
197 
Present()198     void OldSwapChainBase::Present() {
199         if (GetDevice()->ConsumedError(ValidatePresent())) {
200             return;
201         }
202         ASSERT(!IsError());
203 
204         if (GetDevice()->ConsumedError(OnBeforePresent(mCurrentTexture.Get()))) {
205             return;
206         }
207 
208         mImplementation.Present(mImplementation.userData);
209 
210         mCurrentTexture = nullptr;
211         mCurrentTextureView = nullptr;
212     }
213 
GetImplementation()214     const DawnSwapChainImplementation& OldSwapChainBase::GetImplementation() {
215         ASSERT(!IsError());
216         return mImplementation;
217     }
218 
ValidateConfigure(wgpu::TextureFormat format,wgpu::TextureUsage allowedUsage,uint32_t width,uint32_t height) const219     MaybeError OldSwapChainBase::ValidateConfigure(wgpu::TextureFormat format,
220                                                    wgpu::TextureUsage allowedUsage,
221                                                    uint32_t width,
222                                                    uint32_t height) const {
223         DAWN_TRY(GetDevice()->ValidateIsAlive());
224         DAWN_TRY(GetDevice()->ValidateObject(this));
225 
226         DAWN_TRY(ValidateTextureUsage(allowedUsage));
227         DAWN_TRY(ValidateTextureFormat(format));
228 
229         if (width == 0 || height == 0) {
230             return DAWN_VALIDATION_ERROR("Swap chain cannot be configured to zero size");
231         }
232 
233         return {};
234     }
235 
ValidateGetCurrentTextureView() const236     MaybeError OldSwapChainBase::ValidateGetCurrentTextureView() const {
237         DAWN_TRY(GetDevice()->ValidateIsAlive());
238         DAWN_TRY(GetDevice()->ValidateObject(this));
239 
240         if (mWidth == 0) {
241             // If width is 0, it implies swap chain has never been configured
242             return DAWN_VALIDATION_ERROR("Swap chain needs to be configured before GetNextTexture");
243         }
244 
245         return {};
246     }
247 
ValidatePresent() const248     MaybeError OldSwapChainBase::ValidatePresent() const {
249         DAWN_TRY(GetDevice()->ValidateIsAlive());
250         DAWN_TRY(GetDevice()->ValidateObject(this));
251 
252         if (mCurrentTextureView.Get() == nullptr) {
253             return DAWN_VALIDATION_ERROR(
254                 "Cannot call present without a GetCurrentTextureView call for this frame");
255         }
256 
257         return {};
258     }
259 
260     // Implementation of NewSwapChainBase
261 
NewSwapChainBase(DeviceBase * device,Surface * surface,const SwapChainDescriptor * descriptor)262     NewSwapChainBase::NewSwapChainBase(DeviceBase* device,
263                                        Surface* surface,
264                                        const SwapChainDescriptor* descriptor)
265         : SwapChainBase(device),
266           mAttached(true),
267           mWidth(descriptor->width),
268           mHeight(descriptor->height),
269           mFormat(descriptor->format),
270           mUsage(descriptor->usage),
271           mPresentMode(descriptor->presentMode),
272           mSurface(surface) {
273     }
274 
~NewSwapChainBase()275     NewSwapChainBase::~NewSwapChainBase() {
276         if (mCurrentTextureView.Get() != nullptr) {
277             ASSERT(mCurrentTextureView->GetTexture()->GetTextureState() ==
278                    TextureBase::TextureState::Destroyed);
279         }
280 
281         ASSERT(!mAttached);
282         ASSERT(mSurface == nullptr);
283     }
284 
DetachFromSurface()285     void NewSwapChainBase::DetachFromSurface() {
286         if (mAttached) {
287             DetachFromSurfaceImpl();
288             GetSurface()->SetAttachedSwapChain(nullptr);
289             mSurface = nullptr;
290             mAttached = false;
291         }
292     }
293 
Configure(wgpu::TextureFormat format,wgpu::TextureUsage allowedUsage,uint32_t width,uint32_t height)294     void NewSwapChainBase::Configure(wgpu::TextureFormat format,
295                                      wgpu::TextureUsage allowedUsage,
296                                      uint32_t width,
297                                      uint32_t height) {
298         GetDevice()->ConsumedError(
299             DAWN_VALIDATION_ERROR("Configure is invalid for surface-based swapchains"));
300     }
301 
GetCurrentTextureView()302     TextureViewBase* NewSwapChainBase::GetCurrentTextureView() {
303         if (GetDevice()->ConsumedError(ValidateGetCurrentTextureView())) {
304             return TextureViewBase::MakeError(GetDevice());
305         }
306 
307         if (mCurrentTextureView.Get() != nullptr) {
308             // Calling GetCurrentTextureView always returns a new reference so add it even when
309             // reusing the existing texture view.
310             mCurrentTextureView->Reference();
311             return mCurrentTextureView.Get();
312         }
313 
314         TextureViewBase* view = nullptr;
315         if (GetDevice()->ConsumedError(GetCurrentTextureViewImpl(), &view)) {
316             return TextureViewBase::MakeError(GetDevice());
317         }
318 
319         // Check that the return texture view matches exactly what was given for this descriptor.
320         ASSERT(view->GetTexture()->GetFormat().format == mFormat);
321         ASSERT((view->GetTexture()->GetUsage() & mUsage) == mUsage);
322         ASSERT(view->GetLevelCount() == 1);
323         ASSERT(view->GetLayerCount() == 1);
324         ASSERT(view->GetDimension() == wgpu::TextureViewDimension::e2D);
325         ASSERT(view->GetTexture()->GetMipLevelVirtualSize(view->GetBaseMipLevel()).width == mWidth);
326         ASSERT(view->GetTexture()->GetMipLevelVirtualSize(view->GetBaseMipLevel()).height ==
327                mHeight);
328 
329         mCurrentTextureView = view;
330         return view;
331     }
332 
Present()333     void NewSwapChainBase::Present() {
334         if (GetDevice()->ConsumedError(ValidatePresent())) {
335             return;
336         }
337 
338         if (GetDevice()->ConsumedError(PresentImpl())) {
339             return;
340         }
341 
342         ASSERT(mCurrentTextureView->GetTexture()->GetTextureState() ==
343                TextureBase::TextureState::Destroyed);
344         mCurrentTextureView = nullptr;
345     }
346 
GetWidth() const347     uint32_t NewSwapChainBase::GetWidth() const {
348         return mWidth;
349     }
350 
GetHeight() const351     uint32_t NewSwapChainBase::GetHeight() const {
352         return mHeight;
353     }
354 
GetFormat() const355     wgpu::TextureFormat NewSwapChainBase::GetFormat() const {
356         return mFormat;
357     }
358 
GetUsage() const359     wgpu::TextureUsage NewSwapChainBase::GetUsage() const {
360         return mUsage;
361     }
362 
GetPresentMode() const363     wgpu::PresentMode NewSwapChainBase::GetPresentMode() const {
364         return mPresentMode;
365     }
366 
GetSurface() const367     Surface* NewSwapChainBase::GetSurface() const {
368         return mSurface;
369     }
370 
IsAttached() const371     bool NewSwapChainBase::IsAttached() const {
372         return mAttached;
373     }
374 
GetBackendType() const375     wgpu::BackendType NewSwapChainBase::GetBackendType() const {
376         return GetDevice()->GetAdapter()->GetBackendType();
377     }
378 
ValidatePresent() const379     MaybeError NewSwapChainBase::ValidatePresent() const {
380         DAWN_TRY(GetDevice()->ValidateIsAlive());
381         DAWN_TRY(GetDevice()->ValidateObject(this));
382 
383         if (!mAttached) {
384             return DAWN_VALIDATION_ERROR("Presenting on detached swapchain");
385         }
386 
387         if (mCurrentTextureView.Get() == nullptr) {
388             return DAWN_VALIDATION_ERROR("Presenting without prior GetCurrentTextureView");
389         }
390 
391         return {};
392     }
393 
ValidateGetCurrentTextureView() const394     MaybeError NewSwapChainBase::ValidateGetCurrentTextureView() const {
395         DAWN_TRY(GetDevice()->ValidateIsAlive());
396         DAWN_TRY(GetDevice()->ValidateObject(this));
397 
398         if (!mAttached) {
399             return DAWN_VALIDATION_ERROR("Getting view on detached swapchain");
400         }
401 
402         return {};
403     }
404 
405 }  // namespace dawn_native
406