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