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/Device.h" 23 #include "dawn_native/ValidationUtils_autogen.h" 24 25 namespace dawn_native { 26 namespace { 27 // TODO(jiawei.shao@intel.com): implement texture view format compatibility rule ValidateTextureViewFormatCompatibility(const TextureBase * texture,const TextureViewDescriptor * descriptor)28 MaybeError ValidateTextureViewFormatCompatibility(const TextureBase* texture, 29 const TextureViewDescriptor* descriptor) { 30 if (texture->GetFormat().format != descriptor->format) { 31 return DAWN_VALIDATION_ERROR( 32 "The format of texture view is not compatible to the original texture"); 33 } 34 35 return {}; 36 } 37 38 // TODO(jiawei.shao@intel.com): support validation on all texture view dimensions IsTextureViewDimensionCompatibleWithTextureDimension(dawn::TextureViewDimension textureViewDimension,dawn::TextureDimension textureDimension)39 bool IsTextureViewDimensionCompatibleWithTextureDimension( 40 dawn::TextureViewDimension textureViewDimension, 41 dawn::TextureDimension textureDimension) { 42 switch (textureViewDimension) { 43 case dawn::TextureViewDimension::e2D: 44 case dawn::TextureViewDimension::e2DArray: 45 case dawn::TextureViewDimension::Cube: 46 case dawn::TextureViewDimension::CubeArray: 47 return textureDimension == dawn::TextureDimension::e2D; 48 default: 49 UNREACHABLE(); 50 return false; 51 } 52 } 53 54 // TODO(jiawei.shao@intel.com): support validation on all texture view dimensions IsArrayLayerValidForTextureViewDimension(dawn::TextureViewDimension textureViewDimension,uint32_t textureViewArrayLayer)55 bool IsArrayLayerValidForTextureViewDimension( 56 dawn::TextureViewDimension textureViewDimension, 57 uint32_t textureViewArrayLayer) { 58 switch (textureViewDimension) { 59 case dawn::TextureViewDimension::e2D: 60 return textureViewArrayLayer == 1u; 61 case dawn::TextureViewDimension::e2DArray: 62 return true; 63 case dawn::TextureViewDimension::Cube: 64 return textureViewArrayLayer == 6u; 65 case dawn::TextureViewDimension::CubeArray: 66 return textureViewArrayLayer % 6 == 0; 67 default: 68 UNREACHABLE(); 69 return false; 70 } 71 } 72 IsTextureSizeValidForTextureViewDimension(dawn::TextureViewDimension textureViewDimension,const Extent3D & textureSize)73 bool IsTextureSizeValidForTextureViewDimension( 74 dawn::TextureViewDimension textureViewDimension, 75 const Extent3D& textureSize) { 76 switch (textureViewDimension) { 77 case dawn::TextureViewDimension::Cube: 78 case dawn::TextureViewDimension::CubeArray: 79 return textureSize.width == textureSize.height; 80 case dawn::TextureViewDimension::e2D: 81 case dawn::TextureViewDimension::e2DArray: 82 return true; 83 default: 84 UNREACHABLE(); 85 return false; 86 } 87 } 88 89 // TODO(jiawei.shao@intel.com): support more sample count. ValidateSampleCount(const TextureDescriptor * descriptor,const Format * format)90 MaybeError ValidateSampleCount(const TextureDescriptor* descriptor, const Format* format) { 91 if (!IsValidSampleCount(descriptor->sampleCount)) { 92 return DAWN_VALIDATION_ERROR("The sample count of the texture is not supported."); 93 } 94 95 if (descriptor->sampleCount > 1) { 96 if (descriptor->mipLevelCount > 1) { 97 return DAWN_VALIDATION_ERROR( 98 "The mipmap level count of a multisampled texture must be 1."); 99 } 100 101 // Multisampled 2D array texture is not supported because on Metal it requires the 102 // version of macOS be greater than 10.14. 103 if (descriptor->arrayLayerCount > 1) { 104 return DAWN_VALIDATION_ERROR("Multisampled 2D array texture is not supported."); 105 } 106 107 if (format->isCompressed) { 108 return DAWN_VALIDATION_ERROR( 109 "The sample counts of the textures in BC formats must be 1."); 110 } 111 } 112 113 return {}; 114 } 115 ValidateTextureViewDimensionCompatibility(const TextureBase * texture,const TextureViewDescriptor * descriptor)116 MaybeError ValidateTextureViewDimensionCompatibility( 117 const TextureBase* texture, 118 const TextureViewDescriptor* descriptor) { 119 if (!IsArrayLayerValidForTextureViewDimension(descriptor->dimension, 120 descriptor->arrayLayerCount)) { 121 return DAWN_VALIDATION_ERROR( 122 "The dimension of the texture view is not compatible with the layer count"); 123 } 124 125 if (!IsTextureViewDimensionCompatibleWithTextureDimension(descriptor->dimension, 126 texture->GetDimension())) { 127 return DAWN_VALIDATION_ERROR( 128 "The dimension of the texture view is not compatible with the dimension of the" 129 "original texture"); 130 } 131 132 if (!IsTextureSizeValidForTextureViewDimension(descriptor->dimension, 133 texture->GetSize())) { 134 return DAWN_VALIDATION_ERROR( 135 "The dimension of the texture view is not compatible with the size of the" 136 "original texture"); 137 } 138 139 return {}; 140 } 141 MakeDefaultTextureViewDescriptor(const TextureBase * texture)142 TextureViewDescriptor MakeDefaultTextureViewDescriptor(const TextureBase* texture) { 143 TextureViewDescriptor descriptor; 144 descriptor.format = texture->GetFormat().format; 145 descriptor.baseArrayLayer = 0; 146 descriptor.arrayLayerCount = texture->GetArrayLayers(); 147 descriptor.baseMipLevel = 0; 148 descriptor.mipLevelCount = texture->GetNumMipLevels(); 149 150 // TODO(jiawei.shao@intel.com): support all texture dimensions. 151 switch (texture->GetDimension()) { 152 case dawn::TextureDimension::e2D: 153 if (texture->GetArrayLayers() == 1u) { 154 descriptor.dimension = dawn::TextureViewDimension::e2D; 155 } else { 156 descriptor.dimension = dawn::TextureViewDimension::e2DArray; 157 } 158 break; 159 default: 160 UNREACHABLE(); 161 } 162 163 return descriptor; 164 } 165 ValidateTextureSize(const TextureDescriptor * descriptor,const Format * format)166 MaybeError ValidateTextureSize(const TextureDescriptor* descriptor, const Format* format) { 167 ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0); 168 169 if (Log2(std::max(descriptor->size.width, descriptor->size.height)) + 1 < 170 descriptor->mipLevelCount) { 171 return DAWN_VALIDATION_ERROR("Texture has too many mip levels"); 172 } 173 174 if (format->isCompressed && (descriptor->size.width % format->blockWidth != 0 || 175 descriptor->size.height % format->blockHeight != 0)) { 176 return DAWN_VALIDATION_ERROR( 177 "The size of the texture is incompatible with the texture format"); 178 } 179 180 return {}; 181 } 182 ValidateTextureUsage(const TextureDescriptor * descriptor,const Format * format)183 MaybeError ValidateTextureUsage(const TextureDescriptor* descriptor, const Format* format) { 184 DAWN_TRY(ValidateTextureUsageBit(descriptor->usage)); 185 186 constexpr dawn::TextureUsageBit kValidCompressedUsages = 187 dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::CopySrc | 188 dawn::TextureUsageBit::CopyDst; 189 if (format->isCompressed && (descriptor->usage & (~kValidCompressedUsages))) { 190 return DAWN_VALIDATION_ERROR( 191 "Compressed texture format is incompatible with the texture usage"); 192 } 193 194 if (!format->isRenderable && 195 (descriptor->usage & dawn::TextureUsageBit::OutputAttachment)) { 196 return DAWN_VALIDATION_ERROR( 197 "Non-renderable format used with OutputAttachment usage"); 198 } 199 200 if (descriptor->usage & dawn::TextureUsageBit::Storage) { 201 return DAWN_VALIDATION_ERROR("storage textures aren't supported (yet)"); 202 } 203 204 return {}; 205 } 206 207 } // anonymous namespace 208 ValidateTextureDescriptor(const DeviceBase * device,const TextureDescriptor * descriptor)209 MaybeError ValidateTextureDescriptor(const DeviceBase* device, 210 const TextureDescriptor* descriptor) { 211 if (descriptor->nextInChain != nullptr) { 212 return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); 213 } 214 215 const Format* format; 216 DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format)); 217 218 DAWN_TRY(ValidateTextureUsage(descriptor, format)); 219 DAWN_TRY(ValidateTextureDimension(descriptor->dimension)); 220 DAWN_TRY(ValidateSampleCount(descriptor, format)); 221 222 // TODO(jiawei.shao@intel.com): check stuff based on the dimension 223 if (descriptor->size.width == 0 || descriptor->size.height == 0 || 224 descriptor->size.depth == 0 || descriptor->arrayLayerCount == 0 || 225 descriptor->mipLevelCount == 0) { 226 return DAWN_VALIDATION_ERROR("Cannot create an empty texture"); 227 } 228 229 if (descriptor->dimension != dawn::TextureDimension::e2D) { 230 return DAWN_VALIDATION_ERROR("Texture dimension must be 2D (for now)"); 231 } 232 233 DAWN_TRY(ValidateTextureSize(descriptor, format)); 234 235 return {}; 236 } 237 ValidateTextureViewDescriptor(const DeviceBase * device,const TextureBase * texture,const TextureViewDescriptor * descriptor)238 MaybeError ValidateTextureViewDescriptor(const DeviceBase* device, 239 const TextureBase* texture, 240 const TextureViewDescriptor* descriptor) { 241 if (descriptor->nextInChain != nullptr) { 242 return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); 243 } 244 245 DAWN_TRY(device->ValidateObject(texture)); 246 if (texture->GetTextureState() == TextureBase::TextureState::Destroyed) { 247 return DAWN_VALIDATION_ERROR("Destroyed texture used to create texture view"); 248 } 249 250 DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension)); 251 if (descriptor->dimension == dawn::TextureViewDimension::e1D || 252 descriptor->dimension == dawn::TextureViewDimension::e3D) { 253 return DAWN_VALIDATION_ERROR("Texture view dimension must be 2D compatible."); 254 } 255 256 DAWN_TRY(ValidateTextureFormat(descriptor->format)); 257 258 // TODO(jiawei.shao@intel.com): check stuff based on resource limits 259 if (descriptor->arrayLayerCount == 0 || descriptor->mipLevelCount == 0) { 260 return DAWN_VALIDATION_ERROR("Cannot create an empty texture view"); 261 } 262 263 if (uint64_t(descriptor->baseArrayLayer) + uint64_t(descriptor->arrayLayerCount) > 264 uint64_t(texture->GetArrayLayers())) { 265 return DAWN_VALIDATION_ERROR("Texture view array-layer out of range"); 266 } 267 268 if (uint64_t(descriptor->baseMipLevel) + uint64_t(descriptor->mipLevelCount) > 269 uint64_t(texture->GetNumMipLevels())) { 270 return DAWN_VALIDATION_ERROR("Texture view mip-level out of range"); 271 } 272 273 DAWN_TRY(ValidateTextureViewFormatCompatibility(texture, descriptor)); 274 DAWN_TRY(ValidateTextureViewDimensionCompatibility(texture, descriptor)); 275 276 return {}; 277 } 278 IsValidSampleCount(uint32_t sampleCount)279 bool IsValidSampleCount(uint32_t sampleCount) { 280 switch (sampleCount) { 281 case 1: 282 case 4: 283 return true; 284 285 default: 286 return false; 287 } 288 } 289 290 // TextureBase 291 TextureBase(DeviceBase * device,const TextureDescriptor * descriptor,TextureState state)292 TextureBase::TextureBase(DeviceBase* device, 293 const TextureDescriptor* descriptor, 294 TextureState state) 295 : ObjectBase(device), 296 mDimension(descriptor->dimension), 297 mFormat(device->GetValidInternalFormat(descriptor->format)), 298 mSize(descriptor->size), 299 mArrayLayerCount(descriptor->arrayLayerCount), 300 mMipLevelCount(descriptor->mipLevelCount), 301 mSampleCount(descriptor->sampleCount), 302 mUsage(descriptor->usage), 303 mState(state) { 304 uint32_t subresourceCount = 305 GetSubresourceIndex(descriptor->mipLevelCount, descriptor->arrayLayerCount); 306 mIsSubresourceContentInitializedAtIndex = std::vector<bool>(subresourceCount, false); 307 } 308 309 static Format kUnusedFormat; 310 TextureBase(DeviceBase * device,ObjectBase::ErrorTag tag)311 TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag) 312 : ObjectBase(device, tag), mFormat(kUnusedFormat) { 313 } 314 315 // static MakeError(DeviceBase * device)316 TextureBase* TextureBase::MakeError(DeviceBase* device) { 317 return new TextureBase(device, ObjectBase::kError); 318 } 319 GetDimension() const320 dawn::TextureDimension TextureBase::GetDimension() const { 321 ASSERT(!IsError()); 322 return mDimension; 323 } 324 325 // TODO(jiawei.shao@intel.com): return more information about texture format GetFormat() const326 const Format& TextureBase::GetFormat() const { 327 ASSERT(!IsError()); 328 return mFormat; 329 } GetSize() const330 const Extent3D& TextureBase::GetSize() const { 331 ASSERT(!IsError()); 332 return mSize; 333 } GetArrayLayers() const334 uint32_t TextureBase::GetArrayLayers() const { 335 ASSERT(!IsError()); 336 return mArrayLayerCount; 337 } GetNumMipLevels() const338 uint32_t TextureBase::GetNumMipLevels() const { 339 ASSERT(!IsError()); 340 return mMipLevelCount; 341 } GetSampleCount() const342 uint32_t TextureBase::GetSampleCount() const { 343 ASSERT(!IsError()); 344 return mSampleCount; 345 } GetUsage() const346 dawn::TextureUsageBit TextureBase::GetUsage() const { 347 ASSERT(!IsError()); 348 return mUsage; 349 } 350 GetTextureState() const351 TextureBase::TextureState TextureBase::GetTextureState() const { 352 ASSERT(!IsError()); 353 return mState; 354 } 355 GetSubresourceIndex(uint32_t mipLevel,uint32_t arraySlice) const356 uint32_t TextureBase::GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const { 357 ASSERT(arraySlice <= kMaxTexture2DArrayLayers); 358 ASSERT(mipLevel <= kMaxTexture2DMipLevels); 359 static_assert(kMaxTexture2DMipLevels <= 360 std::numeric_limits<uint32_t>::max() / kMaxTexture2DArrayLayers, 361 "texture size overflows uint32_t"); 362 return GetNumMipLevels() * arraySlice + mipLevel; 363 } 364 IsSubresourceContentInitialized(uint32_t baseMipLevel,uint32_t levelCount,uint32_t baseArrayLayer,uint32_t layerCount) const365 bool TextureBase::IsSubresourceContentInitialized(uint32_t baseMipLevel, 366 uint32_t levelCount, 367 uint32_t baseArrayLayer, 368 uint32_t layerCount) const { 369 ASSERT(!IsError()); 370 for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) { 371 for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount; 372 ++arrayLayer) { 373 uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer); 374 ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); 375 if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) { 376 return false; 377 } 378 } 379 } 380 return true; 381 } 382 SetIsSubresourceContentInitialized(uint32_t baseMipLevel,uint32_t levelCount,uint32_t baseArrayLayer,uint32_t layerCount)383 void TextureBase::SetIsSubresourceContentInitialized(uint32_t baseMipLevel, 384 uint32_t levelCount, 385 uint32_t baseArrayLayer, 386 uint32_t layerCount) { 387 ASSERT(!IsError()); 388 for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) { 389 for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount; 390 ++arrayLayer) { 391 uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer); 392 ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); 393 mIsSubresourceContentInitializedAtIndex[subresourceIndex] = true; 394 } 395 } 396 } 397 ValidateCanUseInSubmitNow() const398 MaybeError TextureBase::ValidateCanUseInSubmitNow() const { 399 ASSERT(!IsError()); 400 if (mState == TextureState::Destroyed) { 401 return DAWN_VALIDATION_ERROR("Destroyed texture used in a submit"); 402 } 403 return {}; 404 } 405 IsMultisampledTexture() const406 bool TextureBase::IsMultisampledTexture() const { 407 ASSERT(!IsError()); 408 return mSampleCount > 1; 409 } 410 GetMipLevelVirtualSize(uint64_t level) const411 Extent3D TextureBase::GetMipLevelVirtualSize(uint64_t level) const { 412 Extent3D extent; 413 extent.width = std::max(mSize.width >> level, 1u); 414 extent.height = std::max(mSize.height >> level, 1u); 415 extent.depth = std::max(mSize.depth >> level, 1u); 416 return extent; 417 } 418 GetMipLevelPhysicalSize(uint64_t level) const419 Extent3D TextureBase::GetMipLevelPhysicalSize(uint64_t level) const { 420 Extent3D extent = GetMipLevelVirtualSize(level); 421 422 // Compressed Textures will have paddings if their width or height is not a multiple of 423 // 4 at non-zero mipmap levels. 424 if (mFormat.isCompressed) { 425 // TODO(jiawei.shao@intel.com): check if there are any overflows. 426 uint32_t blockWidth = mFormat.blockWidth; 427 uint32_t blockHeight = mFormat.blockHeight; 428 extent.width = (extent.width + blockWidth - 1) / blockWidth * blockWidth; 429 extent.height = (extent.height + blockHeight - 1) / blockHeight * blockHeight; 430 } 431 432 return extent; 433 } 434 CreateDefaultView()435 TextureViewBase* TextureBase::CreateDefaultView() { 436 TextureViewDescriptor descriptor = {}; 437 438 if (!IsError()) { 439 descriptor = MakeDefaultTextureViewDescriptor(this); 440 } 441 442 return GetDevice()->CreateTextureView(this, &descriptor); 443 } 444 CreateView(const TextureViewDescriptor * descriptor)445 TextureViewBase* TextureBase::CreateView(const TextureViewDescriptor* descriptor) { 446 return GetDevice()->CreateTextureView(this, descriptor); 447 } 448 Destroy()449 void TextureBase::Destroy() { 450 if (GetDevice()->ConsumedError(ValidateDestroy())) { 451 return; 452 } 453 ASSERT(!IsError()); 454 DestroyInternal(); 455 } 456 DestroyImpl()457 void TextureBase::DestroyImpl() { 458 } 459 DestroyInternal()460 void TextureBase::DestroyInternal() { 461 if (mState == TextureState::OwnedInternal) { 462 DestroyImpl(); 463 } 464 mState = TextureState::Destroyed; 465 } 466 ValidateDestroy() const467 MaybeError TextureBase::ValidateDestroy() const { 468 DAWN_TRY(GetDevice()->ValidateObject(this)); 469 return {}; 470 } 471 472 // TextureViewBase 473 TextureViewBase(TextureBase * texture,const TextureViewDescriptor * descriptor)474 TextureViewBase::TextureViewBase(TextureBase* texture, const TextureViewDescriptor* descriptor) 475 : ObjectBase(texture->GetDevice()), 476 mTexture(texture), 477 mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)), 478 mBaseMipLevel(descriptor->baseMipLevel), 479 mMipLevelCount(descriptor->mipLevelCount), 480 mBaseArrayLayer(descriptor->baseArrayLayer), 481 mArrayLayerCount(descriptor->arrayLayerCount) { 482 } 483 TextureViewBase(DeviceBase * device,ObjectBase::ErrorTag tag)484 TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag) 485 : ObjectBase(device, tag), mFormat(kUnusedFormat) { 486 } 487 488 // static MakeError(DeviceBase * device)489 TextureViewBase* TextureViewBase::MakeError(DeviceBase* device) { 490 return new TextureViewBase(device, ObjectBase::kError); 491 } 492 GetTexture() const493 const TextureBase* TextureViewBase::GetTexture() const { 494 ASSERT(!IsError()); 495 return mTexture.Get(); 496 } 497 GetTexture()498 TextureBase* TextureViewBase::GetTexture() { 499 ASSERT(!IsError()); 500 return mTexture.Get(); 501 } 502 GetFormat() const503 const Format& TextureViewBase::GetFormat() const { 504 ASSERT(!IsError()); 505 return mFormat; 506 } 507 GetBaseMipLevel() const508 uint32_t TextureViewBase::GetBaseMipLevel() const { 509 ASSERT(!IsError()); 510 return mBaseMipLevel; 511 } 512 GetLevelCount() const513 uint32_t TextureViewBase::GetLevelCount() const { 514 ASSERT(!IsError()); 515 return mMipLevelCount; 516 } 517 GetBaseArrayLayer() const518 uint32_t TextureViewBase::GetBaseArrayLayer() const { 519 ASSERT(!IsError()); 520 return mBaseArrayLayer; 521 } 522 GetLayerCount() const523 uint32_t TextureViewBase::GetLayerCount() const { 524 ASSERT(!IsError()); 525 return mArrayLayerCount; 526 } 527 } // namespace dawn_native 528