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