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/Texture.h" 16 17 #include <algorithm> 18 19 #include "common/Assert.h" 20 #include "common/Constants.h" 21 #include "common/Math.h" 22 #include "dawn_native/Adapter.h" 23 #include "dawn_native/ChainUtils_autogen.h" 24 #include "dawn_native/Device.h" 25 #include "dawn_native/EnumMaskIterator.h" 26 #include "dawn_native/ObjectType_autogen.h" 27 #include "dawn_native/PassResourceUsage.h" 28 #include "dawn_native/ValidationUtils_autogen.h" 29 30 namespace dawn_native { 31 namespace { 32 // WebGPU currently does not have texture format reinterpretation. If it does, the 33 // code to check for it might go here. ValidateTextureViewFormatCompatibility(const TextureBase * texture,const TextureViewDescriptor * descriptor)34 MaybeError ValidateTextureViewFormatCompatibility(const TextureBase* texture, 35 const TextureViewDescriptor* descriptor) { 36 if (texture->GetFormat().format != descriptor->format) { 37 if (descriptor->aspect != wgpu::TextureAspect::All && 38 texture->GetFormat().GetAspectInfo(descriptor->aspect).format == 39 descriptor->format) { 40 return {}; 41 } 42 43 return DAWN_VALIDATION_ERROR( 44 "The format of texture view is not compatible to the original texture"); 45 } 46 47 return {}; 48 } 49 50 // TODO(crbug.com/dawn/814): Implement for 1D texture. IsTextureViewDimensionCompatibleWithTextureDimension(wgpu::TextureViewDimension textureViewDimension,wgpu::TextureDimension textureDimension)51 bool IsTextureViewDimensionCompatibleWithTextureDimension( 52 wgpu::TextureViewDimension textureViewDimension, 53 wgpu::TextureDimension textureDimension) { 54 switch (textureViewDimension) { 55 case wgpu::TextureViewDimension::e2D: 56 case wgpu::TextureViewDimension::e2DArray: 57 case wgpu::TextureViewDimension::Cube: 58 case wgpu::TextureViewDimension::CubeArray: 59 return textureDimension == wgpu::TextureDimension::e2D; 60 61 case wgpu::TextureViewDimension::e3D: 62 return textureDimension == wgpu::TextureDimension::e3D; 63 64 case wgpu::TextureViewDimension::e1D: 65 case wgpu::TextureViewDimension::Undefined: 66 break; 67 } 68 UNREACHABLE(); 69 } 70 71 // TODO(crbug.com/dawn/814): Implement for 1D texture. IsArrayLayerValidForTextureViewDimension(wgpu::TextureViewDimension textureViewDimension,uint32_t textureViewArrayLayer)72 bool IsArrayLayerValidForTextureViewDimension( 73 wgpu::TextureViewDimension textureViewDimension, 74 uint32_t textureViewArrayLayer) { 75 switch (textureViewDimension) { 76 case wgpu::TextureViewDimension::e2D: 77 case wgpu::TextureViewDimension::e3D: 78 return textureViewArrayLayer == 1u; 79 case wgpu::TextureViewDimension::e2DArray: 80 return true; 81 case wgpu::TextureViewDimension::Cube: 82 return textureViewArrayLayer == 6u; 83 case wgpu::TextureViewDimension::CubeArray: 84 return textureViewArrayLayer % 6 == 0; 85 86 case wgpu::TextureViewDimension::e1D: 87 case wgpu::TextureViewDimension::Undefined: 88 break; 89 } 90 UNREACHABLE(); 91 } 92 ValidateSampleCount(const TextureDescriptor * descriptor,wgpu::TextureUsage usage,const Format * format)93 MaybeError ValidateSampleCount(const TextureDescriptor* descriptor, 94 wgpu::TextureUsage usage, 95 const Format* format) { 96 DAWN_INVALID_IF(!IsValidSampleCount(descriptor->sampleCount), 97 "The sample count (%u) of the texture is not supported.", 98 descriptor->sampleCount); 99 100 if (descriptor->sampleCount > 1) { 101 DAWN_INVALID_IF(descriptor->mipLevelCount > 1, 102 "The mip level count (%u) of a multisampled texture is not 1.", 103 descriptor->mipLevelCount); 104 105 // Multisampled 1D and 3D textures are not supported in D3D12/Metal/Vulkan. 106 // Multisampled 2D array texture is not supported because on Metal it requires the 107 // version of macOS be greater than 10.14. 108 DAWN_INVALID_IF(descriptor->dimension != wgpu::TextureDimension::e2D, 109 "The dimension (%s) of a multisampled texture is not 2D.", 110 descriptor->dimension); 111 112 DAWN_INVALID_IF(descriptor->size.depthOrArrayLayers > 1, 113 "The depthOrArrayLayers (%u) of a multisampled texture is not 1.", 114 descriptor->size.depthOrArrayLayers); 115 116 // If a format can support multisample, it must be renderable. Because Vulkan 117 // requires that if the format is not color-renderable or depth/stencil renderable, 118 // sampleCount must be 1. 119 DAWN_INVALID_IF(!format->isRenderable, 120 "The texture format (%s) does not support multisampling.", 121 format->format); 122 123 // Compressed formats are not renderable. They cannot support multisample. 124 ASSERT(!format->isCompressed); 125 126 DAWN_INVALID_IF(usage & wgpu::TextureUsage::StorageBinding, 127 "The sample count (%u) of a storage textures is not 1.", 128 descriptor->sampleCount); 129 } 130 131 return {}; 132 } 133 ValidateTextureViewDimensionCompatibility(const TextureBase * texture,const TextureViewDescriptor * descriptor)134 MaybeError ValidateTextureViewDimensionCompatibility( 135 const TextureBase* texture, 136 const TextureViewDescriptor* descriptor) { 137 DAWN_INVALID_IF( 138 !IsArrayLayerValidForTextureViewDimension(descriptor->dimension, 139 descriptor->arrayLayerCount), 140 "The dimension (%s) of the texture view is not compatible with the layer count " 141 "(%u) of %s.", 142 descriptor->dimension, descriptor->arrayLayerCount, texture); 143 144 DAWN_INVALID_IF( 145 !IsTextureViewDimensionCompatibleWithTextureDimension(descriptor->dimension, 146 texture->GetDimension()), 147 "The dimension (%s) of the texture view is not compatible with the dimension (%s) " 148 "of %s.", 149 descriptor->dimension, texture->GetDimension(), texture); 150 151 switch (descriptor->dimension) { 152 case wgpu::TextureViewDimension::Cube: 153 case wgpu::TextureViewDimension::CubeArray: 154 DAWN_INVALID_IF( 155 texture->GetSize().width != texture->GetSize().height, 156 "A %s texture view is not compatible with %s because the texture's width " 157 "(%u) and height (%u) are not equal.", 158 descriptor->dimension, texture, texture->GetSize().width, 159 texture->GetSize().height); 160 break; 161 162 case wgpu::TextureViewDimension::e2D: 163 case wgpu::TextureViewDimension::e2DArray: 164 case wgpu::TextureViewDimension::e3D: 165 break; 166 167 case wgpu::TextureViewDimension::e1D: 168 case wgpu::TextureViewDimension::Undefined: 169 UNREACHABLE(); 170 break; 171 } 172 173 return {}; 174 } 175 ValidateTextureSize(const DeviceBase * device,const TextureDescriptor * descriptor,const Format * format)176 MaybeError ValidateTextureSize(const DeviceBase* device, 177 const TextureDescriptor* descriptor, 178 const Format* format) { 179 ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0 && 180 descriptor->size.depthOrArrayLayers != 0); 181 const CombinedLimits& limits = device->GetLimits(); 182 Extent3D maxExtent; 183 switch (descriptor->dimension) { 184 case wgpu::TextureDimension::e2D: 185 maxExtent = {limits.v1.maxTextureDimension2D, limits.v1.maxTextureDimension2D, 186 limits.v1.maxTextureArrayLayers}; 187 break; 188 case wgpu::TextureDimension::e3D: 189 maxExtent = {limits.v1.maxTextureDimension3D, limits.v1.maxTextureDimension3D, 190 limits.v1.maxTextureDimension3D}; 191 break; 192 case wgpu::TextureDimension::e1D: 193 default: 194 UNREACHABLE(); 195 } 196 DAWN_INVALID_IF(descriptor->size.width > maxExtent.width || 197 descriptor->size.height > maxExtent.height || 198 descriptor->size.depthOrArrayLayers > maxExtent.depthOrArrayLayers, 199 "Texture size (%s) exceeded maximum texture size (%s).", 200 &descriptor->size, &maxExtent); 201 202 uint32_t maxMippedDimension = descriptor->size.width; 203 if (descriptor->dimension != wgpu::TextureDimension::e1D) { 204 maxMippedDimension = std::max(maxMippedDimension, descriptor->size.height); 205 } 206 if (descriptor->dimension == wgpu::TextureDimension::e3D) { 207 maxMippedDimension = 208 std::max(maxMippedDimension, descriptor->size.depthOrArrayLayers); 209 } 210 DAWN_INVALID_IF( 211 Log2(maxMippedDimension) + 1 < descriptor->mipLevelCount, 212 "Texture mip level count (%u) exceeds the maximum (%u) for its size (%s).", 213 descriptor->mipLevelCount, Log2(maxMippedDimension) + 1, &descriptor->size); 214 215 if (format->isCompressed) { 216 const TexelBlockInfo& blockInfo = 217 format->GetAspectInfo(wgpu::TextureAspect::All).block; 218 DAWN_INVALID_IF( 219 descriptor->size.width % blockInfo.width != 0 || 220 descriptor->size.height % blockInfo.height != 0, 221 "The size (%s) of the texture is not a multiple of the block width (%u) and " 222 "height (%u) of the texture format (%s).", 223 &descriptor->size, blockInfo.width, blockInfo.height, format->format); 224 } 225 226 return {}; 227 } 228 ValidateTextureUsage(const TextureDescriptor * descriptor,wgpu::TextureUsage usage,const Format * format)229 MaybeError ValidateTextureUsage(const TextureDescriptor* descriptor, 230 wgpu::TextureUsage usage, 231 const Format* format) { 232 DAWN_TRY(dawn_native::ValidateTextureUsage(usage)); 233 234 constexpr wgpu::TextureUsage kValidCompressedUsages = 235 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc | 236 wgpu::TextureUsage::CopyDst; 237 DAWN_INVALID_IF( 238 format->isCompressed && !IsSubset(usage, kValidCompressedUsages), 239 "The texture usage (%s) is incompatible with the compressed texture format (%s).", 240 usage, format->format); 241 242 DAWN_INVALID_IF( 243 !format->isRenderable && (usage & wgpu::TextureUsage::RenderAttachment), 244 "The texture usage (%s) includes %s, which is incompatible with the non-renderable " 245 "format (%s).", 246 usage, wgpu::TextureUsage::RenderAttachment, format->format); 247 248 DAWN_INVALID_IF( 249 !format->supportsStorageUsage && (usage & wgpu::TextureUsage::StorageBinding), 250 "The texture usage (%s) includes %s, which is incompatible with the format (%s).", 251 usage, wgpu::TextureUsage::StorageBinding, format->format); 252 253 constexpr wgpu::TextureUsage kValidMultiPlanarUsages = 254 wgpu::TextureUsage::TextureBinding; 255 DAWN_INVALID_IF( 256 format->IsMultiPlanar() && !IsSubset(usage, kValidMultiPlanarUsages), 257 "The texture usage (%s) is incompatible with the multi-planar format (%s).", usage, 258 format->format); 259 260 return {}; 261 } 262 263 } // anonymous namespace 264 ValidateTextureDescriptor(const DeviceBase * device,const TextureDescriptor * descriptor)265 MaybeError ValidateTextureDescriptor(const DeviceBase* device, 266 const TextureDescriptor* descriptor) { 267 DAWN_TRY(ValidateSingleSType(descriptor->nextInChain, 268 wgpu::SType::DawnTextureInternalUsageDescriptor)); 269 270 const DawnTextureInternalUsageDescriptor* internalUsageDesc = nullptr; 271 FindInChain(descriptor->nextInChain, &internalUsageDesc); 272 273 DAWN_INVALID_IF(descriptor->dimension == wgpu::TextureDimension::e1D, 274 "1D textures aren't supported (yet)."); 275 276 DAWN_INVALID_IF( 277 internalUsageDesc != nullptr && !device->IsFeatureEnabled(Feature::DawnInternalUsages), 278 "The dawn-internal-usages feature is not enabled"); 279 280 const Format* format; 281 DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format)); 282 283 wgpu::TextureUsage usage = descriptor->usage; 284 if (internalUsageDesc != nullptr) { 285 usage |= internalUsageDesc->internalUsage; 286 } 287 288 DAWN_TRY(ValidateTextureUsage(descriptor, usage, format)); 289 DAWN_TRY(ValidateTextureDimension(descriptor->dimension)); 290 DAWN_TRY(ValidateSampleCount(descriptor, usage, format)); 291 292 DAWN_INVALID_IF(descriptor->size.width == 0 || descriptor->size.height == 0 || 293 descriptor->size.depthOrArrayLayers == 0 || 294 descriptor->mipLevelCount == 0, 295 "The texture size (%s) or mipLevelCount (%u) is empty.", &descriptor->size, 296 descriptor->mipLevelCount); 297 298 DAWN_INVALID_IF( 299 descriptor->dimension != wgpu::TextureDimension::e2D && format->isCompressed, 300 "The dimension (%s) of a texture with a compressed format (%s) is not 2D.", 301 descriptor->dimension, format->format); 302 303 // Depth/stencil formats are valid for 2D textures only. Metal has this limit. And D3D12 304 // doesn't support depth/stencil formats on 3D textures. 305 DAWN_INVALID_IF( 306 descriptor->dimension != wgpu::TextureDimension::e2D && 307 (format->aspects & (Aspect::Depth | Aspect::Stencil)) != 0, 308 "The dimension (%s) of a texture with a depth/stencil format (%s) is not 2D.", 309 descriptor->dimension, format->format); 310 311 DAWN_TRY(ValidateTextureSize(device, descriptor, format)); 312 313 // TODO(crbug.com/dawn/838): Implement a workaround for this issue. 314 // Readbacks from the non-zero mip of a stencil texture may contain garbage data. 315 DAWN_INVALID_IF( 316 device->IsToggleEnabled(Toggle::DisallowUnsafeAPIs) && format->HasStencil() && 317 descriptor->mipLevelCount > 1 && 318 device->GetAdapter()->GetBackendType() == wgpu::BackendType::Metal, 319 "https://crbug.com/dawn/838: Stencil textures with more than one mip level are " 320 "disabled on Metal."); 321 322 DAWN_INVALID_IF( 323 device->IsToggleEnabled(Toggle::DisableR8RG8Mipmaps) && descriptor->mipLevelCount > 1 && 324 (descriptor->format == wgpu::TextureFormat::R8Unorm || 325 descriptor->format == wgpu::TextureFormat::RG8Unorm), 326 "https://crbug.com/dawn/1071: r8unorm and rg8unorm textures with more than one mip " 327 "level are disabled on Metal."); 328 329 return {}; 330 } 331 ValidateTextureViewDescriptor(const DeviceBase * device,const TextureBase * texture,const TextureViewDescriptor * descriptor)332 MaybeError ValidateTextureViewDescriptor(const DeviceBase* device, 333 const TextureBase* texture, 334 const TextureViewDescriptor* descriptor) { 335 DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr."); 336 337 // Parent texture should have been already validated. 338 ASSERT(texture); 339 ASSERT(!texture->IsError()); 340 341 DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension)); 342 DAWN_INVALID_IF(descriptor->dimension == wgpu::TextureViewDimension::e1D, 343 "1D texture views aren't supported (yet)."); 344 345 DAWN_TRY(ValidateTextureFormat(descriptor->format)); 346 347 DAWN_TRY(ValidateTextureAspect(descriptor->aspect)); 348 DAWN_INVALID_IF( 349 SelectFormatAspects(texture->GetFormat(), descriptor->aspect) == Aspect::None, 350 "Texture format (%s) does not have the texture view's selected aspect (%s).", 351 texture->GetFormat().format, descriptor->aspect); 352 353 DAWN_INVALID_IF(descriptor->arrayLayerCount == 0 || descriptor->mipLevelCount == 0, 354 "The texture view's arrayLayerCount (%u) or mipLevelCount (%u) is zero.", 355 descriptor->arrayLayerCount, descriptor->mipLevelCount); 356 357 DAWN_INVALID_IF( 358 uint64_t(descriptor->baseArrayLayer) + uint64_t(descriptor->arrayLayerCount) > 359 uint64_t(texture->GetArrayLayers()), 360 "Texture view array layer range (baseArrayLayer: %u, arrayLayerCount: %u) exceeds the " 361 "texture's array layer count (%u).", 362 descriptor->baseArrayLayer, descriptor->arrayLayerCount, texture->GetArrayLayers()); 363 364 DAWN_INVALID_IF( 365 uint64_t(descriptor->baseMipLevel) + uint64_t(descriptor->mipLevelCount) > 366 uint64_t(texture->GetNumMipLevels()), 367 "Texture view mip level range (baseMipLevel: %u, mipLevelCount: %u) exceeds the " 368 "texture's mip level count (%u).", 369 descriptor->baseMipLevel, descriptor->mipLevelCount, texture->GetNumMipLevels()); 370 371 DAWN_TRY(ValidateTextureViewFormatCompatibility(texture, descriptor)); 372 DAWN_TRY(ValidateTextureViewDimensionCompatibility(texture, descriptor)); 373 374 return {}; 375 } 376 GetTextureViewDescriptorWithDefaults(const TextureBase * texture,const TextureViewDescriptor * descriptor)377 TextureViewDescriptor GetTextureViewDescriptorWithDefaults( 378 const TextureBase* texture, 379 const TextureViewDescriptor* descriptor) { 380 ASSERT(texture); 381 382 TextureViewDescriptor desc = {}; 383 if (descriptor) { 384 desc = *descriptor; 385 } 386 387 // The default value for the view dimension depends on the texture's dimension with a 388 // special case for 2DArray being chosen automatically if arrayLayerCount is unspecified. 389 if (desc.dimension == wgpu::TextureViewDimension::Undefined) { 390 switch (texture->GetDimension()) { 391 case wgpu::TextureDimension::e1D: 392 desc.dimension = wgpu::TextureViewDimension::e1D; 393 break; 394 395 case wgpu::TextureDimension::e2D: 396 desc.dimension = wgpu::TextureViewDimension::e2D; 397 break; 398 399 case wgpu::TextureDimension::e3D: 400 desc.dimension = wgpu::TextureViewDimension::e3D; 401 break; 402 } 403 } 404 405 if (desc.format == wgpu::TextureFormat::Undefined) { 406 // TODO(dawn:682): Use GetAspectInfo(aspect). 407 desc.format = texture->GetFormat().format; 408 } 409 if (desc.arrayLayerCount == wgpu::kArrayLayerCountUndefined) { 410 switch (desc.dimension) { 411 case wgpu::TextureViewDimension::e1D: 412 case wgpu::TextureViewDimension::e2D: 413 case wgpu::TextureViewDimension::e3D: 414 desc.arrayLayerCount = 1; 415 break; 416 case wgpu::TextureViewDimension::Cube: 417 desc.arrayLayerCount = 6; 418 break; 419 case wgpu::TextureViewDimension::e2DArray: 420 case wgpu::TextureViewDimension::CubeArray: 421 desc.arrayLayerCount = texture->GetArrayLayers() - desc.baseArrayLayer; 422 break; 423 default: 424 // We don't put UNREACHABLE() here because we validate enums only after this 425 // function sets default values. Otherwise, the UNREACHABLE() will be hit. 426 break; 427 } 428 } 429 430 if (desc.mipLevelCount == wgpu::kMipLevelCountUndefined) { 431 desc.mipLevelCount = texture->GetNumMipLevels() - desc.baseMipLevel; 432 } 433 return desc; 434 } 435 436 // WebGPU only supports sample counts of 1 and 4. We could expand to more based on 437 // platform support, but it would probably be a feature. IsValidSampleCount(uint32_t sampleCount)438 bool IsValidSampleCount(uint32_t sampleCount) { 439 switch (sampleCount) { 440 case 1: 441 case 4: 442 return true; 443 444 default: 445 return false; 446 } 447 } 448 449 // TextureBase 450 TextureBase(DeviceBase * device,const TextureDescriptor * descriptor,TextureState state)451 TextureBase::TextureBase(DeviceBase* device, 452 const TextureDescriptor* descriptor, 453 TextureState state) 454 : ApiObjectBase(device, descriptor->label), 455 mDimension(descriptor->dimension), 456 mFormat(device->GetValidInternalFormat(descriptor->format)), 457 mSize(descriptor->size), 458 mMipLevelCount(descriptor->mipLevelCount), 459 mSampleCount(descriptor->sampleCount), 460 mUsage(descriptor->usage), 461 mInternalUsage(mUsage), 462 mState(state) { 463 uint32_t subresourceCount = 464 mMipLevelCount * GetArrayLayers() * GetAspectCount(mFormat.aspects); 465 mIsSubresourceContentInitializedAtIndex = std::vector<bool>(subresourceCount, false); 466 467 const DawnTextureInternalUsageDescriptor* internalUsageDesc = nullptr; 468 FindInChain(descriptor->nextInChain, &internalUsageDesc); 469 if (internalUsageDesc != nullptr) { 470 mInternalUsage |= internalUsageDesc->internalUsage; 471 } 472 TrackInDevice(); 473 } 474 475 static Format kUnusedFormat; 476 TextureBase(DeviceBase * device,TextureState state)477 TextureBase::TextureBase(DeviceBase* device, TextureState state) 478 : ApiObjectBase(device, kLabelNotImplemented), mFormat(kUnusedFormat), mState(state) { 479 TrackInDevice(); 480 } 481 TextureBase(DeviceBase * device,ObjectBase::ErrorTag tag)482 TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag) 483 : ApiObjectBase(device, tag), mFormat(kUnusedFormat) { 484 } 485 DestroyImpl()486 void TextureBase::DestroyImpl() { 487 mState = TextureState::Destroyed; 488 } 489 490 // static MakeError(DeviceBase * device)491 TextureBase* TextureBase::MakeError(DeviceBase* device) { 492 return new TextureBase(device, ObjectBase::kError); 493 } 494 GetType() const495 ObjectType TextureBase::GetType() const { 496 return ObjectType::Texture; 497 } 498 GetDimension() const499 wgpu::TextureDimension TextureBase::GetDimension() const { 500 ASSERT(!IsError()); 501 return mDimension; 502 } 503 GetFormat() const504 const Format& TextureBase::GetFormat() const { 505 ASSERT(!IsError()); 506 return mFormat; 507 } GetSize() const508 const Extent3D& TextureBase::GetSize() const { 509 ASSERT(!IsError()); 510 return mSize; 511 } GetWidth() const512 uint32_t TextureBase::GetWidth() const { 513 ASSERT(!IsError()); 514 return mSize.width; 515 } GetHeight() const516 uint32_t TextureBase::GetHeight() const { 517 ASSERT(!IsError()); 518 ASSERT(mDimension != wgpu::TextureDimension::e1D); 519 return mSize.height; 520 } GetDepth() const521 uint32_t TextureBase::GetDepth() const { 522 ASSERT(!IsError()); 523 ASSERT(mDimension == wgpu::TextureDimension::e3D); 524 return mSize.depthOrArrayLayers; 525 } GetArrayLayers() const526 uint32_t TextureBase::GetArrayLayers() const { 527 ASSERT(!IsError()); 528 // TODO(crbug.com/dawn/814): Update for 1D textures when they are supported. 529 ASSERT(mDimension != wgpu::TextureDimension::e1D); 530 if (mDimension == wgpu::TextureDimension::e3D) { 531 return 1; 532 } 533 return mSize.depthOrArrayLayers; 534 } GetNumMipLevels() const535 uint32_t TextureBase::GetNumMipLevels() const { 536 ASSERT(!IsError()); 537 return mMipLevelCount; 538 } GetAllSubresources() const539 SubresourceRange TextureBase::GetAllSubresources() const { 540 ASSERT(!IsError()); 541 return {mFormat.aspects, {0, GetArrayLayers()}, {0, mMipLevelCount}}; 542 } GetSampleCount() const543 uint32_t TextureBase::GetSampleCount() const { 544 ASSERT(!IsError()); 545 return mSampleCount; 546 } GetSubresourceCount() const547 uint32_t TextureBase::GetSubresourceCount() const { 548 ASSERT(!IsError()); 549 return static_cast<uint32_t>(mIsSubresourceContentInitializedAtIndex.size()); 550 } GetUsage() const551 wgpu::TextureUsage TextureBase::GetUsage() const { 552 ASSERT(!IsError()); 553 return mUsage; 554 } GetInternalUsage() const555 wgpu::TextureUsage TextureBase::GetInternalUsage() const { 556 ASSERT(!IsError()); 557 return mInternalUsage; 558 } 559 GetTextureState() const560 TextureBase::TextureState TextureBase::GetTextureState() const { 561 ASSERT(!IsError()); 562 return mState; 563 } 564 GetSubresourceIndex(uint32_t mipLevel,uint32_t arraySlice,Aspect aspect) const565 uint32_t TextureBase::GetSubresourceIndex(uint32_t mipLevel, 566 uint32_t arraySlice, 567 Aspect aspect) const { 568 ASSERT(HasOneBit(aspect)); 569 return mipLevel + 570 GetNumMipLevels() * (arraySlice + GetArrayLayers() * GetAspectIndex(aspect)); 571 } 572 IsSubresourceContentInitialized(const SubresourceRange & range) const573 bool TextureBase::IsSubresourceContentInitialized(const SubresourceRange& range) const { 574 ASSERT(!IsError()); 575 for (Aspect aspect : IterateEnumMask(range.aspects)) { 576 for (uint32_t arrayLayer = range.baseArrayLayer; 577 arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) { 578 for (uint32_t mipLevel = range.baseMipLevel; 579 mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) { 580 uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer, aspect); 581 ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); 582 if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) { 583 return false; 584 } 585 } 586 } 587 } 588 return true; 589 } 590 SetIsSubresourceContentInitialized(bool isInitialized,const SubresourceRange & range)591 void TextureBase::SetIsSubresourceContentInitialized(bool isInitialized, 592 const SubresourceRange& range) { 593 ASSERT(!IsError()); 594 for (Aspect aspect : IterateEnumMask(range.aspects)) { 595 for (uint32_t arrayLayer = range.baseArrayLayer; 596 arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) { 597 for (uint32_t mipLevel = range.baseMipLevel; 598 mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) { 599 uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer, aspect); 600 ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); 601 mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized; 602 } 603 } 604 } 605 } 606 ValidateCanUseInSubmitNow() const607 MaybeError TextureBase::ValidateCanUseInSubmitNow() const { 608 ASSERT(!IsError()); 609 DAWN_INVALID_IF(mState == TextureState::Destroyed, "Destroyed texture %s used in a submit.", 610 this); 611 return {}; 612 } 613 IsMultisampledTexture() const614 bool TextureBase::IsMultisampledTexture() const { 615 ASSERT(!IsError()); 616 return mSampleCount > 1; 617 } 618 GetMipLevelVirtualSize(uint32_t level) const619 Extent3D TextureBase::GetMipLevelVirtualSize(uint32_t level) const { 620 Extent3D extent = {std::max(mSize.width >> level, 1u), 1u, 1u}; 621 if (mDimension == wgpu::TextureDimension::e1D) { 622 return extent; 623 } 624 625 extent.height = std::max(mSize.height >> level, 1u); 626 if (mDimension == wgpu::TextureDimension::e2D) { 627 return extent; 628 } 629 630 extent.depthOrArrayLayers = std::max(mSize.depthOrArrayLayers >> level, 1u); 631 return extent; 632 } 633 GetMipLevelPhysicalSize(uint32_t level) const634 Extent3D TextureBase::GetMipLevelPhysicalSize(uint32_t level) const { 635 Extent3D extent = GetMipLevelVirtualSize(level); 636 637 // Compressed Textures will have paddings if their width or height is not a multiple of 638 // 4 at non-zero mipmap levels. 639 if (mFormat.isCompressed && level != 0) { 640 // If |level| is non-zero, then each dimension of |extent| is at most half of 641 // the max texture dimension. Computations here which add the block width/height 642 // to the extent cannot overflow. 643 const TexelBlockInfo& blockInfo = mFormat.GetAspectInfo(wgpu::TextureAspect::All).block; 644 extent.width = (extent.width + blockInfo.width - 1) / blockInfo.width * blockInfo.width; 645 extent.height = 646 (extent.height + blockInfo.height - 1) / blockInfo.height * blockInfo.height; 647 } 648 649 return extent; 650 } 651 ClampToMipLevelVirtualSize(uint32_t level,const Origin3D & origin,const Extent3D & extent) const652 Extent3D TextureBase::ClampToMipLevelVirtualSize(uint32_t level, 653 const Origin3D& origin, 654 const Extent3D& extent) const { 655 const Extent3D virtualSizeAtLevel = GetMipLevelVirtualSize(level); 656 ASSERT(origin.x <= virtualSizeAtLevel.width); 657 ASSERT(origin.y <= virtualSizeAtLevel.height); 658 uint32_t clampedCopyExtentWidth = (extent.width > virtualSizeAtLevel.width - origin.x) 659 ? (virtualSizeAtLevel.width - origin.x) 660 : extent.width; 661 uint32_t clampedCopyExtentHeight = (extent.height > virtualSizeAtLevel.height - origin.y) 662 ? (virtualSizeAtLevel.height - origin.y) 663 : extent.height; 664 return {clampedCopyExtentWidth, clampedCopyExtentHeight, extent.depthOrArrayLayers}; 665 } 666 APICreateView(const TextureViewDescriptor * descriptor)667 TextureViewBase* TextureBase::APICreateView(const TextureViewDescriptor* descriptor) { 668 DeviceBase* device = GetDevice(); 669 670 Ref<TextureViewBase> result; 671 if (device->ConsumedError(device->CreateTextureView(this, descriptor), &result, 672 "calling %s.CreateView(%s).", this, descriptor)) { 673 return TextureViewBase::MakeError(device); 674 } 675 return result.Detach(); 676 } 677 APIDestroy()678 void TextureBase::APIDestroy() { 679 if (GetDevice()->ConsumedError(ValidateDestroy(), "calling %s.Destroy().", this)) { 680 return; 681 } 682 ASSERT(!IsError()); 683 Destroy(); 684 } 685 ValidateDestroy() const686 MaybeError TextureBase::ValidateDestroy() const { 687 DAWN_TRY(GetDevice()->ValidateObject(this)); 688 return {}; 689 } 690 691 // TextureViewBase 692 TextureViewBase(TextureBase * texture,const TextureViewDescriptor * descriptor)693 TextureViewBase::TextureViewBase(TextureBase* texture, const TextureViewDescriptor* descriptor) 694 : ApiObjectBase(texture->GetDevice(), descriptor->label), 695 mTexture(texture), 696 mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)), 697 mDimension(descriptor->dimension), 698 mRange({ConvertViewAspect(mFormat, descriptor->aspect), 699 {descriptor->baseArrayLayer, descriptor->arrayLayerCount}, 700 {descriptor->baseMipLevel, descriptor->mipLevelCount}}) { 701 TrackInDevice(); 702 } 703 TextureViewBase(TextureBase * texture)704 TextureViewBase::TextureViewBase(TextureBase* texture) 705 : ApiObjectBase(texture->GetDevice(), kLabelNotImplemented), 706 mTexture(texture), 707 mFormat(kUnusedFormat) { 708 TrackInDevice(); 709 } 710 TextureViewBase(DeviceBase * device,ObjectBase::ErrorTag tag)711 TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag) 712 : ApiObjectBase(device, tag), mFormat(kUnusedFormat) { 713 } 714 DestroyImpl()715 void TextureViewBase::DestroyImpl() { 716 } 717 718 // static MakeError(DeviceBase * device)719 TextureViewBase* TextureViewBase::MakeError(DeviceBase* device) { 720 return new TextureViewBase(device, ObjectBase::kError); 721 } 722 GetType() const723 ObjectType TextureViewBase::GetType() const { 724 return ObjectType::TextureView; 725 } 726 GetTexture() const727 const TextureBase* TextureViewBase::GetTexture() const { 728 ASSERT(!IsError()); 729 return mTexture.Get(); 730 } 731 GetTexture()732 TextureBase* TextureViewBase::GetTexture() { 733 ASSERT(!IsError()); 734 return mTexture.Get(); 735 } 736 GetAspects() const737 Aspect TextureViewBase::GetAspects() const { 738 ASSERT(!IsError()); 739 return mRange.aspects; 740 } 741 GetFormat() const742 const Format& TextureViewBase::GetFormat() const { 743 ASSERT(!IsError()); 744 return mFormat; 745 } 746 GetDimension() const747 wgpu::TextureViewDimension TextureViewBase::GetDimension() const { 748 ASSERT(!IsError()); 749 return mDimension; 750 } 751 GetBaseMipLevel() const752 uint32_t TextureViewBase::GetBaseMipLevel() const { 753 ASSERT(!IsError()); 754 return mRange.baseMipLevel; 755 } 756 GetLevelCount() const757 uint32_t TextureViewBase::GetLevelCount() const { 758 ASSERT(!IsError()); 759 return mRange.levelCount; 760 } 761 GetBaseArrayLayer() const762 uint32_t TextureViewBase::GetBaseArrayLayer() const { 763 ASSERT(!IsError()); 764 return mRange.baseArrayLayer; 765 } 766 GetLayerCount() const767 uint32_t TextureViewBase::GetLayerCount() const { 768 ASSERT(!IsError()); 769 return mRange.layerCount; 770 } 771 GetSubresourceRange() const772 const SubresourceRange& TextureViewBase::GetSubresourceRange() const { 773 ASSERT(!IsError()); 774 return mRange; 775 } 776 777 } // namespace dawn_native 778