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/BindGroup.h" 16 17 #include "common/Assert.h" 18 #include "common/Math.h" 19 #include "common/ityp_bitset.h" 20 #include "dawn_native/BindGroupLayout.h" 21 #include "dawn_native/Buffer.h" 22 #include "dawn_native/ChainUtils_autogen.h" 23 #include "dawn_native/Device.h" 24 #include "dawn_native/ExternalTexture.h" 25 #include "dawn_native/ObjectBase.h" 26 #include "dawn_native/ObjectType_autogen.h" 27 #include "dawn_native/Sampler.h" 28 #include "dawn_native/Texture.h" 29 30 namespace dawn_native { 31 32 namespace { 33 34 // Helper functions to perform binding-type specific validation 35 ValidateBufferBinding(const DeviceBase * device,const BindGroupEntry & entry,const BindingInfo & bindingInfo)36 MaybeError ValidateBufferBinding(const DeviceBase* device, 37 const BindGroupEntry& entry, 38 const BindingInfo& bindingInfo) { 39 DAWN_INVALID_IF(entry.buffer == nullptr, "Binding entry buffer not set."); 40 41 DAWN_INVALID_IF(entry.sampler != nullptr || entry.textureView != nullptr, 42 "Expected only buffer to be set for binding entry."); 43 44 DAWN_INVALID_IF(entry.nextInChain != nullptr, "nextInChain must be nullptr."); 45 46 DAWN_TRY(device->ValidateObject(entry.buffer)); 47 48 ASSERT(bindingInfo.bindingType == BindingInfoType::Buffer); 49 50 wgpu::BufferUsage requiredUsage; 51 uint64_t maxBindingSize; 52 uint64_t requiredBindingAlignment; 53 switch (bindingInfo.buffer.type) { 54 case wgpu::BufferBindingType::Uniform: 55 requiredUsage = wgpu::BufferUsage::Uniform; 56 maxBindingSize = device->GetLimits().v1.maxUniformBufferBindingSize; 57 requiredBindingAlignment = 58 device->GetLimits().v1.minUniformBufferOffsetAlignment; 59 break; 60 case wgpu::BufferBindingType::Storage: 61 case wgpu::BufferBindingType::ReadOnlyStorage: 62 requiredUsage = wgpu::BufferUsage::Storage; 63 maxBindingSize = device->GetLimits().v1.maxStorageBufferBindingSize; 64 requiredBindingAlignment = 65 device->GetLimits().v1.minStorageBufferOffsetAlignment; 66 break; 67 case kInternalStorageBufferBinding: 68 requiredUsage = kInternalStorageBuffer; 69 maxBindingSize = device->GetLimits().v1.maxStorageBufferBindingSize; 70 requiredBindingAlignment = 71 device->GetLimits().v1.minStorageBufferOffsetAlignment; 72 break; 73 case wgpu::BufferBindingType::Undefined: 74 UNREACHABLE(); 75 } 76 77 uint64_t bufferSize = entry.buffer->GetSize(); 78 79 // Handle wgpu::WholeSize, avoiding overflows. 80 DAWN_INVALID_IF(entry.offset > bufferSize, 81 "Binding offset (%u) is larger than the size (%u) of %s.", entry.offset, 82 bufferSize, entry.buffer); 83 84 uint64_t bindingSize = 85 (entry.size == wgpu::kWholeSize) ? bufferSize - entry.offset : entry.size; 86 87 DAWN_INVALID_IF(bindingSize > bufferSize, 88 "Binding size (%u) is larger than the size (%u) of %s.", bindingSize, 89 bufferSize, entry.buffer); 90 91 DAWN_INVALID_IF(bindingSize == 0, "Binding size is zero"); 92 93 // Note that no overflow can happen because we already checked that 94 // bufferSize >= bindingSize 95 DAWN_INVALID_IF( 96 entry.offset > bufferSize - bindingSize, 97 "Binding range (offset: %u, size: %u) doesn't fit in the size (%u) of %s.", 98 entry.offset, bufferSize, bindingSize, entry.buffer); 99 100 DAWN_INVALID_IF(!IsAligned(entry.offset, requiredBindingAlignment), 101 "Offset (%u) does not satisfy the minimum %s alignment (%u).", 102 entry.offset, bindingInfo.buffer.type, requiredBindingAlignment); 103 104 DAWN_INVALID_IF(!(entry.buffer->GetUsage() & requiredUsage), 105 "Binding usage (%s) of %s doesn't match expected usage (%s).", 106 entry.buffer->GetUsage(), entry.buffer, requiredUsage); 107 108 DAWN_INVALID_IF(bindingSize < bindingInfo.buffer.minBindingSize, 109 "Binding size (%u) is smaller than the minimum binding size (%u).", 110 bindingSize, bindingInfo.buffer.minBindingSize); 111 112 DAWN_INVALID_IF(bindingSize > maxBindingSize, 113 "Binding size (%u) is larger than the maximum binding size (%u).", 114 bindingSize, maxBindingSize); 115 116 return {}; 117 } 118 ValidateTextureBinding(DeviceBase * device,const BindGroupEntry & entry,const BindingInfo & bindingInfo)119 MaybeError ValidateTextureBinding(DeviceBase* device, 120 const BindGroupEntry& entry, 121 const BindingInfo& bindingInfo) { 122 DAWN_INVALID_IF(entry.textureView == nullptr, "Binding entry textureView not set."); 123 124 DAWN_INVALID_IF(entry.sampler != nullptr || entry.buffer != nullptr, 125 "Expected only textureView to be set for binding entry."); 126 127 DAWN_INVALID_IF(entry.nextInChain != nullptr, "nextInChain must be nullptr."); 128 129 DAWN_TRY(device->ValidateObject(entry.textureView)); 130 131 TextureViewBase* view = entry.textureView; 132 133 Aspect aspect = view->GetAspects(); 134 // TODO(dawn:563): Format Aspects 135 DAWN_INVALID_IF(!HasOneBit(aspect), "Multiple aspects selected in %s.", view); 136 137 TextureBase* texture = view->GetTexture(); 138 switch (bindingInfo.bindingType) { 139 case BindingInfoType::Texture: { 140 SampleTypeBit supportedTypes = 141 texture->GetFormat().GetAspectInfo(aspect).supportedSampleTypes; 142 SampleTypeBit requiredType = 143 SampleTypeToSampleTypeBit(bindingInfo.texture.sampleType); 144 145 DAWN_INVALID_IF( 146 !(texture->GetUsage() & wgpu::TextureUsage::TextureBinding), 147 "Usage (%s) of %s doesn't include TextureUsage::TextureBinding.", 148 texture->GetUsage(), texture); 149 150 DAWN_INVALID_IF( 151 texture->IsMultisampledTexture() != bindingInfo.texture.multisampled, 152 "Sample count (%u) of %s doesn't match expectation (multisampled: %d).", 153 texture->GetSampleCount(), texture, bindingInfo.texture.multisampled); 154 155 // TODO(dawn:563): Improve error message. 156 DAWN_INVALID_IF((supportedTypes & requiredType) == 0, 157 "Texture component type usage mismatch."); 158 159 DAWN_INVALID_IF( 160 entry.textureView->GetDimension() != bindingInfo.texture.viewDimension, 161 "Dimension (%s) of %s doesn't match the expected dimension (%s).", 162 entry.textureView->GetDimension(), entry.textureView, 163 bindingInfo.texture.viewDimension); 164 break; 165 } 166 case BindingInfoType::StorageTexture: { 167 DAWN_INVALID_IF( 168 !(texture->GetUsage() & wgpu::TextureUsage::StorageBinding), 169 "Usage (%s) of %s doesn't include TextureUsage::StorageBinding.", 170 texture->GetUsage(), texture); 171 172 ASSERT(!texture->IsMultisampledTexture()); 173 174 DAWN_INVALID_IF( 175 texture->GetFormat().format != bindingInfo.storageTexture.format, 176 "Format (%s) of %s expected to be (%s).", texture->GetFormat().format, 177 texture, bindingInfo.storageTexture.format); 178 179 DAWN_INVALID_IF( 180 entry.textureView->GetDimension() != 181 bindingInfo.storageTexture.viewDimension, 182 "Dimension (%s) of %s doesn't match the expected dimension (%s).", 183 entry.textureView->GetDimension(), entry.textureView, 184 bindingInfo.storageTexture.viewDimension); 185 186 DAWN_INVALID_IF(entry.textureView->GetLevelCount() != 1, 187 "mipLevelCount (%u) of %s expected to be 1.", 188 entry.textureView->GetLevelCount(), entry.textureView); 189 break; 190 } 191 default: 192 UNREACHABLE(); 193 break; 194 } 195 196 return {}; 197 } 198 ValidateSamplerBinding(const DeviceBase * device,const BindGroupEntry & entry,const BindingInfo & bindingInfo)199 MaybeError ValidateSamplerBinding(const DeviceBase* device, 200 const BindGroupEntry& entry, 201 const BindingInfo& bindingInfo) { 202 DAWN_INVALID_IF(entry.sampler == nullptr, "Binding entry sampler not set."); 203 204 DAWN_INVALID_IF(entry.textureView != nullptr || entry.buffer != nullptr, 205 "Expected only sampler to be set for binding entry."); 206 207 DAWN_INVALID_IF(entry.nextInChain != nullptr, "nextInChain must be nullptr."); 208 209 DAWN_TRY(device->ValidateObject(entry.sampler)); 210 211 ASSERT(bindingInfo.bindingType == BindingInfoType::Sampler); 212 213 switch (bindingInfo.sampler.type) { 214 case wgpu::SamplerBindingType::NonFiltering: 215 DAWN_INVALID_IF( 216 entry.sampler->IsFiltering(), 217 "Filtering sampler %s is incompatible with non-filtering sampler " 218 "binding.", 219 entry.sampler); 220 DAWN_FALLTHROUGH; 221 case wgpu::SamplerBindingType::Filtering: 222 DAWN_INVALID_IF( 223 entry.sampler->IsComparison(), 224 "Comparison sampler %s is incompatible with non-comparison sampler " 225 "binding.", 226 entry.sampler); 227 break; 228 case wgpu::SamplerBindingType::Comparison: 229 DAWN_INVALID_IF( 230 !entry.sampler->IsComparison(), 231 "Non-comparison sampler %s is imcompatible with comparison sampler " 232 "binding.", 233 entry.sampler); 234 break; 235 default: 236 UNREACHABLE(); 237 break; 238 } 239 240 return {}; 241 } 242 ValidateExternalTextureBinding(const DeviceBase * device,const BindGroupEntry & entry,const BindingInfo & bindingInfo)243 MaybeError ValidateExternalTextureBinding(const DeviceBase* device, 244 const BindGroupEntry& entry, 245 const BindingInfo& bindingInfo) { 246 const ExternalTextureBindingEntry* externalTextureBindingEntry = nullptr; 247 FindInChain(entry.nextInChain, &externalTextureBindingEntry); 248 249 DAWN_INVALID_IF(externalTextureBindingEntry == nullptr, 250 "Binding entry external texture not set."); 251 252 DAWN_INVALID_IF( 253 entry.sampler != nullptr || entry.textureView != nullptr || entry.buffer != nullptr, 254 "Expected only external texture to be set for binding entry."); 255 256 DAWN_TRY(ValidateSingleSType(externalTextureBindingEntry->nextInChain, 257 wgpu::SType::ExternalTextureBindingEntry)); 258 259 DAWN_TRY(device->ValidateObject(externalTextureBindingEntry->externalTexture)); 260 261 return {}; 262 } 263 264 } // anonymous namespace 265 ValidateBindGroupDescriptor(DeviceBase * device,const BindGroupDescriptor * descriptor)266 MaybeError ValidateBindGroupDescriptor(DeviceBase* device, 267 const BindGroupDescriptor* descriptor) { 268 DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr."); 269 270 DAWN_TRY(device->ValidateObject(descriptor->layout)); 271 272 DAWN_INVALID_IF( 273 BindingIndex(descriptor->entryCount) != descriptor->layout->GetBindingCount(), 274 "Number of entries (%u) did not match the number of entries (%u) specified in %s", 275 descriptor->entryCount, static_cast<uint32_t>(descriptor->layout->GetBindingCount()), 276 descriptor->layout); 277 278 const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap(); 279 ASSERT(bindingMap.size() <= kMaxBindingsPerPipelineLayout); 280 281 ityp::bitset<BindingIndex, kMaxBindingsPerPipelineLayout> bindingsSet; 282 for (uint32_t i = 0; i < descriptor->entryCount; ++i) { 283 const BindGroupEntry& entry = descriptor->entries[i]; 284 285 const auto& it = bindingMap.find(BindingNumber(entry.binding)); 286 DAWN_INVALID_IF(it == bindingMap.end(), 287 "In entries[%u], binding index %u not present in the bind group layout", 288 i, entry.binding); 289 290 BindingIndex bindingIndex = it->second; 291 ASSERT(bindingIndex < descriptor->layout->GetBindingCount()); 292 293 DAWN_INVALID_IF(bindingsSet[bindingIndex], 294 "In entries[%u], binding index %u already used by a previous entry", i, 295 entry.binding); 296 297 bindingsSet.set(bindingIndex); 298 299 const BindingInfo& bindingInfo = descriptor->layout->GetBindingInfo(bindingIndex); 300 301 // Perform binding-type specific validation. 302 switch (bindingInfo.bindingType) { 303 case BindingInfoType::Buffer: 304 DAWN_TRY_CONTEXT(ValidateBufferBinding(device, entry, bindingInfo), 305 "validating entries[%u] as a Buffer", i); 306 break; 307 case BindingInfoType::Texture: 308 case BindingInfoType::StorageTexture: 309 DAWN_TRY_CONTEXT(ValidateTextureBinding(device, entry, bindingInfo), 310 "validating entries[%u] as a Texture", i); 311 break; 312 case BindingInfoType::Sampler: 313 DAWN_TRY_CONTEXT(ValidateSamplerBinding(device, entry, bindingInfo), 314 "validating entries[%u] as a Sampler", i); 315 break; 316 case BindingInfoType::ExternalTexture: 317 DAWN_TRY_CONTEXT(ValidateExternalTextureBinding(device, entry, bindingInfo), 318 "validating entries[%u] as an ExternalTexture", i); 319 break; 320 } 321 } 322 323 // This should always be true because 324 // - numBindings has to match between the bind group and its layout. 325 // - Each binding must be set at most once 326 // 327 // We don't validate the equality because it wouldn't be possible to cover it with a test. 328 ASSERT(bindingsSet.count() == bindingMap.size()); 329 330 return {}; 331 } // anonymous namespace 332 333 // BindGroup 334 BindGroupBase(DeviceBase * device,const BindGroupDescriptor * descriptor,void * bindingDataStart)335 BindGroupBase::BindGroupBase(DeviceBase* device, 336 const BindGroupDescriptor* descriptor, 337 void* bindingDataStart) 338 : ApiObjectBase(device, descriptor->label), 339 mLayout(descriptor->layout), 340 mBindingData(mLayout->ComputeBindingDataPointers(bindingDataStart)) { 341 for (BindingIndex i{0}; i < mLayout->GetBindingCount(); ++i) { 342 // TODO(enga): Shouldn't be needed when bindings are tightly packed. 343 // This is to fill Ref<ObjectBase> holes with nullptrs. 344 new (&mBindingData.bindings[i]) Ref<ObjectBase>(); 345 } 346 347 for (uint32_t i = 0; i < descriptor->entryCount; ++i) { 348 const BindGroupEntry& entry = descriptor->entries[i]; 349 350 BindingIndex bindingIndex = 351 descriptor->layout->GetBindingIndex(BindingNumber(entry.binding)); 352 ASSERT(bindingIndex < mLayout->GetBindingCount()); 353 354 // Only a single binding type should be set, so once we found it we can skip to the 355 // next loop iteration. 356 357 if (entry.buffer != nullptr) { 358 ASSERT(mBindingData.bindings[bindingIndex] == nullptr); 359 mBindingData.bindings[bindingIndex] = entry.buffer; 360 mBindingData.bufferData[bindingIndex].offset = entry.offset; 361 uint64_t bufferSize = (entry.size == wgpu::kWholeSize) 362 ? entry.buffer->GetSize() - entry.offset 363 : entry.size; 364 mBindingData.bufferData[bindingIndex].size = bufferSize; 365 continue; 366 } 367 368 if (entry.textureView != nullptr) { 369 ASSERT(mBindingData.bindings[bindingIndex] == nullptr); 370 mBindingData.bindings[bindingIndex] = entry.textureView; 371 continue; 372 } 373 374 if (entry.sampler != nullptr) { 375 ASSERT(mBindingData.bindings[bindingIndex] == nullptr); 376 mBindingData.bindings[bindingIndex] = entry.sampler; 377 continue; 378 } 379 380 const ExternalTextureBindingEntry* externalTextureBindingEntry = nullptr; 381 FindInChain(entry.nextInChain, &externalTextureBindingEntry); 382 if (externalTextureBindingEntry != nullptr) { 383 ASSERT(mBindingData.bindings[bindingIndex] == nullptr); 384 mBindingData.bindings[bindingIndex] = externalTextureBindingEntry->externalTexture; 385 continue; 386 } 387 } 388 389 uint32_t packedIdx = 0; 390 for (BindingIndex bindingIndex{0}; bindingIndex < descriptor->layout->GetBufferCount(); 391 ++bindingIndex) { 392 if (descriptor->layout->GetBindingInfo(bindingIndex).buffer.minBindingSize == 0) { 393 mBindingData.unverifiedBufferSizes[packedIdx] = 394 mBindingData.bufferData[bindingIndex].size; 395 ++packedIdx; 396 } 397 } 398 399 TrackInDevice(); 400 } 401 BindGroupBase(DeviceBase * device)402 BindGroupBase::BindGroupBase(DeviceBase* device) : ApiObjectBase(device, kLabelNotImplemented) { 403 TrackInDevice(); 404 } 405 406 BindGroupBase::~BindGroupBase() = default; 407 DestroyImpl()408 void BindGroupBase::DestroyImpl() { 409 if (mLayout != nullptr) { 410 ASSERT(!IsError()); 411 for (BindingIndex i{0}; i < mLayout->GetBindingCount(); ++i) { 412 mBindingData.bindings[i].~Ref<ObjectBase>(); 413 } 414 } 415 } 416 DeleteThis()417 void BindGroupBase::DeleteThis() { 418 // Add another ref to the layout so that if this is the last ref, the layout 419 // is destroyed after the bind group. The bind group is slab-allocated inside 420 // memory owned by the layout (except for the null backend). 421 Ref<BindGroupLayoutBase> layout = mLayout; 422 ApiObjectBase::DeleteThis(); 423 } 424 BindGroupBase(DeviceBase * device,ObjectBase::ErrorTag tag)425 BindGroupBase::BindGroupBase(DeviceBase* device, ObjectBase::ErrorTag tag) 426 : ApiObjectBase(device, tag), mBindingData() { 427 } 428 429 // static MakeError(DeviceBase * device)430 BindGroupBase* BindGroupBase::MakeError(DeviceBase* device) { 431 return new BindGroupBase(device, ObjectBase::kError); 432 } 433 GetType() const434 ObjectType BindGroupBase::GetType() const { 435 return ObjectType::BindGroup; 436 } 437 GetLayout()438 BindGroupLayoutBase* BindGroupBase::GetLayout() { 439 ASSERT(!IsError()); 440 return mLayout.Get(); 441 } 442 GetLayout() const443 const BindGroupLayoutBase* BindGroupBase::GetLayout() const { 444 ASSERT(!IsError()); 445 return mLayout.Get(); 446 } 447 GetUnverifiedBufferSizes() const448 const ityp::span<uint32_t, uint64_t>& BindGroupBase::GetUnverifiedBufferSizes() const { 449 ASSERT(!IsError()); 450 return mBindingData.unverifiedBufferSizes; 451 } 452 GetBindingAsBufferBinding(BindingIndex bindingIndex)453 BufferBinding BindGroupBase::GetBindingAsBufferBinding(BindingIndex bindingIndex) { 454 ASSERT(!IsError()); 455 ASSERT(bindingIndex < mLayout->GetBindingCount()); 456 ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Buffer); 457 BufferBase* buffer = static_cast<BufferBase*>(mBindingData.bindings[bindingIndex].Get()); 458 return {buffer, mBindingData.bufferData[bindingIndex].offset, 459 mBindingData.bufferData[bindingIndex].size}; 460 } 461 GetBindingAsSampler(BindingIndex bindingIndex) const462 SamplerBase* BindGroupBase::GetBindingAsSampler(BindingIndex bindingIndex) const { 463 ASSERT(!IsError()); 464 ASSERT(bindingIndex < mLayout->GetBindingCount()); 465 ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Sampler); 466 return static_cast<SamplerBase*>(mBindingData.bindings[bindingIndex].Get()); 467 } 468 GetBindingAsTextureView(BindingIndex bindingIndex)469 TextureViewBase* BindGroupBase::GetBindingAsTextureView(BindingIndex bindingIndex) { 470 ASSERT(!IsError()); 471 ASSERT(bindingIndex < mLayout->GetBindingCount()); 472 ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Texture || 473 mLayout->GetBindingInfo(bindingIndex).bindingType == 474 BindingInfoType::StorageTexture); 475 return static_cast<TextureViewBase*>(mBindingData.bindings[bindingIndex].Get()); 476 } 477 GetBindingAsExternalTexture(BindingIndex bindingIndex)478 ExternalTextureBase* BindGroupBase::GetBindingAsExternalTexture(BindingIndex bindingIndex) { 479 ASSERT(!IsError()); 480 ASSERT(bindingIndex < mLayout->GetBindingCount()); 481 ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == 482 BindingInfoType::ExternalTexture); 483 return static_cast<ExternalTextureBase*>(mBindingData.bindings[bindingIndex].Get()); 484 } 485 486 } // namespace dawn_native 487