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/BindGroupLayout.h" 16 17 #include "common/BitSetIterator.h" 18 19 #include "dawn_native/ChainUtils_autogen.h" 20 #include "dawn_native/Device.h" 21 #include "dawn_native/ObjectBase.h" 22 #include "dawn_native/ObjectContentHasher.h" 23 #include "dawn_native/ObjectType_autogen.h" 24 #include "dawn_native/PerStage.h" 25 #include "dawn_native/ValidationUtils_autogen.h" 26 27 #include <algorithm> 28 #include <functional> 29 #include <set> 30 31 namespace dawn_native { 32 33 namespace { ValidateStorageTextureFormat(DeviceBase * device,wgpu::TextureFormat storageTextureFormat)34 MaybeError ValidateStorageTextureFormat(DeviceBase* device, 35 wgpu::TextureFormat storageTextureFormat) { 36 const Format* format = nullptr; 37 DAWN_TRY_ASSIGN(format, device->GetInternalFormat(storageTextureFormat)); 38 39 ASSERT(format != nullptr); 40 DAWN_INVALID_IF(!format->supportsStorageUsage, 41 "Texture format (%s) does not support storage textures.", 42 storageTextureFormat); 43 44 return {}; 45 } 46 ValidateStorageTextureViewDimension(wgpu::TextureViewDimension dimension)47 MaybeError ValidateStorageTextureViewDimension(wgpu::TextureViewDimension dimension) { 48 switch (dimension) { 49 case wgpu::TextureViewDimension::Cube: 50 case wgpu::TextureViewDimension::CubeArray: 51 return DAWN_FORMAT_VALIDATION_ERROR( 52 "%s texture views cannot be used as storage textures.", dimension); 53 54 case wgpu::TextureViewDimension::e1D: 55 case wgpu::TextureViewDimension::e2D: 56 case wgpu::TextureViewDimension::e2DArray: 57 case wgpu::TextureViewDimension::e3D: 58 return {}; 59 60 case wgpu::TextureViewDimension::Undefined: 61 break; 62 } 63 UNREACHABLE(); 64 } 65 ValidateBindGroupLayoutEntry(DeviceBase * device,const BindGroupLayoutEntry & entry,bool allowInternalBinding)66 MaybeError ValidateBindGroupLayoutEntry(DeviceBase* device, 67 const BindGroupLayoutEntry& entry, 68 bool allowInternalBinding) { 69 DAWN_TRY(ValidateShaderStage(entry.visibility)); 70 71 int bindingMemberCount = 0; 72 BindingInfoType bindingType; 73 wgpu::ShaderStage allowedStages = kAllStages; 74 75 if (entry.buffer.type != wgpu::BufferBindingType::Undefined) { 76 bindingMemberCount++; 77 bindingType = BindingInfoType::Buffer; 78 const BufferBindingLayout& buffer = entry.buffer; 79 80 // The kInternalStorageBufferBinding is used internally and not a value 81 // in wgpu::BufferBindingType. 82 if (buffer.type == kInternalStorageBufferBinding) { 83 DAWN_INVALID_IF(!allowInternalBinding, "Internal binding types are disallowed"); 84 } else { 85 DAWN_TRY(ValidateBufferBindingType(buffer.type)); 86 } 87 88 if (buffer.type == wgpu::BufferBindingType::Storage || 89 buffer.type == kInternalStorageBufferBinding) { 90 allowedStages &= ~wgpu::ShaderStage::Vertex; 91 } 92 } 93 if (entry.sampler.type != wgpu::SamplerBindingType::Undefined) { 94 bindingMemberCount++; 95 bindingType = BindingInfoType::Sampler; 96 DAWN_TRY(ValidateSamplerBindingType(entry.sampler.type)); 97 } 98 if (entry.texture.sampleType != wgpu::TextureSampleType::Undefined) { 99 bindingMemberCount++; 100 bindingType = BindingInfoType::Texture; 101 const TextureBindingLayout& texture = entry.texture; 102 DAWN_TRY(ValidateTextureSampleType(texture.sampleType)); 103 104 // viewDimension defaults to 2D if left undefined, needs validation otherwise. 105 wgpu::TextureViewDimension viewDimension = wgpu::TextureViewDimension::e2D; 106 if (texture.viewDimension != wgpu::TextureViewDimension::Undefined) { 107 DAWN_TRY(ValidateTextureViewDimension(texture.viewDimension)); 108 viewDimension = texture.viewDimension; 109 } 110 111 DAWN_INVALID_IF( 112 texture.multisampled && viewDimension != wgpu::TextureViewDimension::e2D, 113 "View dimension (%s) for a multisampled texture bindings was not %s.", 114 viewDimension, wgpu::TextureViewDimension::e2D); 115 } 116 if (entry.storageTexture.access != wgpu::StorageTextureAccess::Undefined) { 117 bindingMemberCount++; 118 bindingType = BindingInfoType::StorageTexture; 119 const StorageTextureBindingLayout& storageTexture = entry.storageTexture; 120 DAWN_TRY(ValidateStorageTextureAccess(storageTexture.access)); 121 DAWN_TRY(ValidateStorageTextureFormat(device, storageTexture.format)); 122 123 // viewDimension defaults to 2D if left undefined, needs validation otherwise. 124 if (storageTexture.viewDimension != wgpu::TextureViewDimension::Undefined) { 125 DAWN_TRY(ValidateTextureViewDimension(storageTexture.viewDimension)); 126 DAWN_TRY(ValidateStorageTextureViewDimension(storageTexture.viewDimension)); 127 } 128 129 if (storageTexture.access == wgpu::StorageTextureAccess::WriteOnly) { 130 allowedStages &= ~wgpu::ShaderStage::Vertex; 131 } 132 } 133 134 const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr; 135 FindInChain(entry.nextInChain, &externalTextureBindingLayout); 136 if (externalTextureBindingLayout != nullptr) { 137 bindingMemberCount++; 138 bindingType = BindingInfoType::ExternalTexture; 139 } 140 141 DAWN_INVALID_IF(bindingMemberCount != 1, 142 "BindGroupLayoutEntry had more than one of buffer, sampler, texture, " 143 "storageTexture, or externalTexture set"); 144 145 DAWN_INVALID_IF( 146 !IsSubset(entry.visibility, allowedStages), 147 "%s bindings cannot be used with a visibility of %s. Only %s are allowed.", 148 bindingType, entry.visibility, allowedStages); 149 150 return {}; 151 } 152 153 } // anonymous namespace 154 ValidateBindGroupLayoutDescriptor(DeviceBase * device,const BindGroupLayoutDescriptor * descriptor,bool allowInternalBinding)155 MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device, 156 const BindGroupLayoutDescriptor* descriptor, 157 bool allowInternalBinding) { 158 DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr"); 159 160 std::set<BindingNumber> bindingsSet; 161 BindingCounts bindingCounts = {}; 162 163 for (uint32_t i = 0; i < descriptor->entryCount; ++i) { 164 const BindGroupLayoutEntry& entry = descriptor->entries[i]; 165 BindingNumber bindingNumber = BindingNumber(entry.binding); 166 167 DAWN_INVALID_IF(bindingsSet.count(bindingNumber) != 0, 168 "On entries[%u]: binding index (%u) was specified by a previous entry.", 169 i, entry.binding); 170 171 DAWN_TRY_CONTEXT(ValidateBindGroupLayoutEntry(device, entry, allowInternalBinding), 172 "validating entries[%u]", i); 173 174 IncrementBindingCounts(&bindingCounts, entry); 175 176 bindingsSet.insert(bindingNumber); 177 } 178 179 DAWN_TRY_CONTEXT(ValidateBindingCounts(bindingCounts), "validating binding counts"); 180 181 return {}; 182 } 183 184 namespace { 185 operator !=(const BindingInfo & a,const BindingInfo & b)186 bool operator!=(const BindingInfo& a, const BindingInfo& b) { 187 if (a.visibility != b.visibility || a.bindingType != b.bindingType) { 188 return true; 189 } 190 191 switch (a.bindingType) { 192 case BindingInfoType::Buffer: 193 return a.buffer.type != b.buffer.type || 194 a.buffer.hasDynamicOffset != b.buffer.hasDynamicOffset || 195 a.buffer.minBindingSize != b.buffer.minBindingSize; 196 case BindingInfoType::Sampler: 197 return a.sampler.type != b.sampler.type; 198 case BindingInfoType::Texture: 199 return a.texture.sampleType != b.texture.sampleType || 200 a.texture.viewDimension != b.texture.viewDimension || 201 a.texture.multisampled != b.texture.multisampled; 202 case BindingInfoType::StorageTexture: 203 return a.storageTexture.access != b.storageTexture.access || 204 a.storageTexture.viewDimension != b.storageTexture.viewDimension || 205 a.storageTexture.format != b.storageTexture.format; 206 case BindingInfoType::ExternalTexture: 207 return false; 208 } 209 UNREACHABLE(); 210 } 211 IsBufferBinding(const BindGroupLayoutEntry & binding)212 bool IsBufferBinding(const BindGroupLayoutEntry& binding) { 213 return binding.buffer.type != wgpu::BufferBindingType::Undefined; 214 } 215 BindingHasDynamicOffset(const BindGroupLayoutEntry & binding)216 bool BindingHasDynamicOffset(const BindGroupLayoutEntry& binding) { 217 if (binding.buffer.type != wgpu::BufferBindingType::Undefined) { 218 return binding.buffer.hasDynamicOffset; 219 } 220 return false; 221 } 222 CreateBindGroupLayoutInfo(const BindGroupLayoutEntry & binding)223 BindingInfo CreateBindGroupLayoutInfo(const BindGroupLayoutEntry& binding) { 224 BindingInfo bindingInfo; 225 bindingInfo.binding = BindingNumber(binding.binding); 226 bindingInfo.visibility = binding.visibility; 227 228 if (binding.buffer.type != wgpu::BufferBindingType::Undefined) { 229 bindingInfo.bindingType = BindingInfoType::Buffer; 230 bindingInfo.buffer = binding.buffer; 231 } else if (binding.sampler.type != wgpu::SamplerBindingType::Undefined) { 232 bindingInfo.bindingType = BindingInfoType::Sampler; 233 bindingInfo.sampler = binding.sampler; 234 } else if (binding.texture.sampleType != wgpu::TextureSampleType::Undefined) { 235 bindingInfo.bindingType = BindingInfoType::Texture; 236 bindingInfo.texture = binding.texture; 237 238 if (binding.texture.viewDimension == wgpu::TextureViewDimension::Undefined) { 239 bindingInfo.texture.viewDimension = wgpu::TextureViewDimension::e2D; 240 } 241 } else if (binding.storageTexture.access != wgpu::StorageTextureAccess::Undefined) { 242 bindingInfo.bindingType = BindingInfoType::StorageTexture; 243 bindingInfo.storageTexture = binding.storageTexture; 244 245 if (binding.storageTexture.viewDimension == wgpu::TextureViewDimension::Undefined) { 246 bindingInfo.storageTexture.viewDimension = wgpu::TextureViewDimension::e2D; 247 } 248 } else { 249 const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr; 250 FindInChain(binding.nextInChain, &externalTextureBindingLayout); 251 if (externalTextureBindingLayout != nullptr) { 252 bindingInfo.bindingType = BindingInfoType::ExternalTexture; 253 } 254 } 255 256 return bindingInfo; 257 } 258 SortBindingsCompare(const BindGroupLayoutEntry & a,const BindGroupLayoutEntry & b)259 bool SortBindingsCompare(const BindGroupLayoutEntry& a, const BindGroupLayoutEntry& b) { 260 const bool aIsBuffer = IsBufferBinding(a); 261 const bool bIsBuffer = IsBufferBinding(b); 262 if (aIsBuffer != bIsBuffer) { 263 // Always place buffers first. 264 return aIsBuffer; 265 } 266 267 if (aIsBuffer) { 268 bool aHasDynamicOffset = BindingHasDynamicOffset(a); 269 bool bHasDynamicOffset = BindingHasDynamicOffset(b); 270 ASSERT(bIsBuffer); 271 if (aHasDynamicOffset != bHasDynamicOffset) { 272 // Buffers with dynamic offsets should come before those without. 273 // This makes it easy to iterate over the dynamic buffer bindings 274 // [0, dynamicBufferCount) during validation. 275 return aHasDynamicOffset; 276 } 277 if (aHasDynamicOffset) { 278 ASSERT(bHasDynamicOffset); 279 ASSERT(a.binding != b.binding); 280 // Above, we ensured that dynamic buffers are first. Now, ensure that 281 // dynamic buffer bindings are in increasing order. This is because dynamic 282 // buffer offsets are applied in increasing order of binding number. 283 return a.binding < b.binding; 284 } 285 } 286 287 // This applies some defaults and gives us a single value to check for the binding type. 288 BindingInfo aInfo = CreateBindGroupLayoutInfo(a); 289 BindingInfo bInfo = CreateBindGroupLayoutInfo(b); 290 291 // Sort by type. 292 if (aInfo.bindingType != bInfo.bindingType) { 293 return aInfo.bindingType < bInfo.bindingType; 294 } 295 296 if (a.visibility != b.visibility) { 297 return a.visibility < b.visibility; 298 } 299 300 switch (aInfo.bindingType) { 301 case BindingInfoType::Buffer: 302 if (aInfo.buffer.minBindingSize != bInfo.buffer.minBindingSize) { 303 return aInfo.buffer.minBindingSize < bInfo.buffer.minBindingSize; 304 } 305 break; 306 case BindingInfoType::Sampler: 307 if (aInfo.sampler.type != bInfo.sampler.type) { 308 return aInfo.sampler.type < bInfo.sampler.type; 309 } 310 break; 311 case BindingInfoType::Texture: 312 if (aInfo.texture.multisampled != bInfo.texture.multisampled) { 313 return aInfo.texture.multisampled < bInfo.texture.multisampled; 314 } 315 if (aInfo.texture.viewDimension != bInfo.texture.viewDimension) { 316 return aInfo.texture.viewDimension < bInfo.texture.viewDimension; 317 } 318 if (aInfo.texture.sampleType != bInfo.texture.sampleType) { 319 return aInfo.texture.sampleType < bInfo.texture.sampleType; 320 } 321 break; 322 case BindingInfoType::StorageTexture: 323 if (aInfo.storageTexture.access != bInfo.storageTexture.access) { 324 return aInfo.storageTexture.access < bInfo.storageTexture.access; 325 } 326 if (aInfo.storageTexture.viewDimension != bInfo.storageTexture.viewDimension) { 327 return aInfo.storageTexture.viewDimension < 328 bInfo.storageTexture.viewDimension; 329 } 330 if (aInfo.storageTexture.format != bInfo.storageTexture.format) { 331 return aInfo.storageTexture.format < bInfo.storageTexture.format; 332 } 333 break; 334 case BindingInfoType::ExternalTexture: 335 break; 336 } 337 return a.binding < b.binding; 338 } 339 340 // This is a utility function to help ASSERT that the BGL-binding comparator places buffers 341 // first. CheckBufferBindingsFirst(ityp::span<BindingIndex,const BindingInfo> bindings)342 bool CheckBufferBindingsFirst(ityp::span<BindingIndex, const BindingInfo> bindings) { 343 BindingIndex lastBufferIndex{0}; 344 BindingIndex firstNonBufferIndex = std::numeric_limits<BindingIndex>::max(); 345 for (BindingIndex i{0}; i < bindings.size(); ++i) { 346 if (bindings[i].bindingType == BindingInfoType::Buffer) { 347 lastBufferIndex = std::max(i, lastBufferIndex); 348 } else { 349 firstNonBufferIndex = std::min(i, firstNonBufferIndex); 350 } 351 } 352 353 // If there are no buffers, then |lastBufferIndex| is initialized to 0 and 354 // |firstNonBufferIndex| gets set to 0. 355 return firstNonBufferIndex >= lastBufferIndex; 356 } 357 358 } // namespace 359 360 // BindGroupLayoutBase 361 BindGroupLayoutBase(DeviceBase * device,const BindGroupLayoutDescriptor * descriptor,PipelineCompatibilityToken pipelineCompatibilityToken,ApiObjectBase::UntrackedByDeviceTag tag)362 BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, 363 const BindGroupLayoutDescriptor* descriptor, 364 PipelineCompatibilityToken pipelineCompatibilityToken, 365 ApiObjectBase::UntrackedByDeviceTag tag) 366 : ApiObjectBase(device, descriptor->label), 367 mBindingInfo(BindingIndex(descriptor->entryCount)), 368 mPipelineCompatibilityToken(pipelineCompatibilityToken) { 369 std::vector<BindGroupLayoutEntry> sortedBindings( 370 descriptor->entries, descriptor->entries + descriptor->entryCount); 371 372 std::sort(sortedBindings.begin(), sortedBindings.end(), SortBindingsCompare); 373 374 for (BindingIndex i{0}; i < mBindingInfo.size(); ++i) { 375 const BindGroupLayoutEntry& binding = sortedBindings[static_cast<uint32_t>(i)]; 376 377 mBindingInfo[i] = CreateBindGroupLayoutInfo(binding); 378 379 if (IsBufferBinding(binding)) { 380 // Buffers must be contiguously packed at the start of the binding info. 381 ASSERT(GetBufferCount() == i); 382 } 383 IncrementBindingCounts(&mBindingCounts, binding); 384 385 const auto& it = mBindingMap.emplace(BindingNumber(binding.binding), i); 386 ASSERT(it.second); 387 } 388 ASSERT(CheckBufferBindingsFirst({mBindingInfo.data(), GetBindingCount()})); 389 ASSERT(mBindingInfo.size() <= kMaxBindingsPerPipelineLayoutTyped); 390 } 391 BindGroupLayoutBase(DeviceBase * device,const BindGroupLayoutDescriptor * descriptor,PipelineCompatibilityToken pipelineCompatibilityToken)392 BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, 393 const BindGroupLayoutDescriptor* descriptor, 394 PipelineCompatibilityToken pipelineCompatibilityToken) 395 : BindGroupLayoutBase(device, descriptor, pipelineCompatibilityToken, kUntrackedByDevice) { 396 TrackInDevice(); 397 } 398 BindGroupLayoutBase(DeviceBase * device,ObjectBase::ErrorTag tag)399 BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag) 400 : ApiObjectBase(device, tag) { 401 } 402 BindGroupLayoutBase(DeviceBase * device)403 BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device) 404 : ApiObjectBase(device, kLabelNotImplemented) { 405 TrackInDevice(); 406 } 407 408 BindGroupLayoutBase::~BindGroupLayoutBase() = default; 409 DestroyImpl()410 void BindGroupLayoutBase::DestroyImpl() { 411 if (IsCachedReference()) { 412 // Do not uncache the actual cached object if we are a blueprint. 413 GetDevice()->UncacheBindGroupLayout(this); 414 } 415 } 416 417 // static MakeError(DeviceBase * device)418 BindGroupLayoutBase* BindGroupLayoutBase::MakeError(DeviceBase* device) { 419 return new BindGroupLayoutBase(device, ObjectBase::kError); 420 } 421 GetType() const422 ObjectType BindGroupLayoutBase::GetType() const { 423 return ObjectType::BindGroupLayout; 424 } 425 GetBindingMap() const426 const BindGroupLayoutBase::BindingMap& BindGroupLayoutBase::GetBindingMap() const { 427 ASSERT(!IsError()); 428 return mBindingMap; 429 } 430 HasBinding(BindingNumber bindingNumber) const431 bool BindGroupLayoutBase::HasBinding(BindingNumber bindingNumber) const { 432 return mBindingMap.count(bindingNumber) != 0; 433 } 434 GetBindingIndex(BindingNumber bindingNumber) const435 BindingIndex BindGroupLayoutBase::GetBindingIndex(BindingNumber bindingNumber) const { 436 ASSERT(!IsError()); 437 const auto& it = mBindingMap.find(bindingNumber); 438 ASSERT(it != mBindingMap.end()); 439 return it->second; 440 } 441 ComputeContentHash()442 size_t BindGroupLayoutBase::ComputeContentHash() { 443 ObjectContentHasher recorder; 444 recorder.Record(mPipelineCompatibilityToken); 445 446 // std::map is sorted by key, so two BGLs constructed in different orders 447 // will still record the same. 448 for (const auto& it : mBindingMap) { 449 recorder.Record(it.first, it.second); 450 451 const BindingInfo& info = mBindingInfo[it.second]; 452 453 recorder.Record(info.buffer.hasDynamicOffset, info.visibility, info.bindingType, 454 info.buffer.type, info.buffer.minBindingSize, info.sampler.type, 455 info.texture.sampleType, info.texture.viewDimension, 456 info.texture.multisampled, info.storageTexture.access, 457 info.storageTexture.format, info.storageTexture.viewDimension); 458 } 459 460 return recorder.GetContentHash(); 461 } 462 operator ()(const BindGroupLayoutBase * a,const BindGroupLayoutBase * b) const463 bool BindGroupLayoutBase::EqualityFunc::operator()(const BindGroupLayoutBase* a, 464 const BindGroupLayoutBase* b) const { 465 return a->IsLayoutEqual(b); 466 } 467 GetBindingCount() const468 BindingIndex BindGroupLayoutBase::GetBindingCount() const { 469 return mBindingInfo.size(); 470 } 471 GetBufferCount() const472 BindingIndex BindGroupLayoutBase::GetBufferCount() const { 473 return BindingIndex(mBindingCounts.bufferCount); 474 } 475 GetDynamicBufferCount() const476 BindingIndex BindGroupLayoutBase::GetDynamicBufferCount() const { 477 // This is a binding index because dynamic buffers are packed at the front of the binding 478 // info. 479 return static_cast<BindingIndex>(mBindingCounts.dynamicStorageBufferCount + 480 mBindingCounts.dynamicUniformBufferCount); 481 } 482 GetUnverifiedBufferCount() const483 uint32_t BindGroupLayoutBase::GetUnverifiedBufferCount() const { 484 return mBindingCounts.unverifiedBufferCount; 485 } 486 GetBindingCountInfo() const487 const BindingCounts& BindGroupLayoutBase::GetBindingCountInfo() const { 488 return mBindingCounts; 489 } 490 IsLayoutEqual(const BindGroupLayoutBase * other,bool excludePipelineCompatibiltyToken) const491 bool BindGroupLayoutBase::IsLayoutEqual(const BindGroupLayoutBase* other, 492 bool excludePipelineCompatibiltyToken) const { 493 if (!excludePipelineCompatibiltyToken && 494 GetPipelineCompatibilityToken() != other->GetPipelineCompatibilityToken()) { 495 return false; 496 } 497 if (GetBindingCount() != other->GetBindingCount()) { 498 return false; 499 } 500 for (BindingIndex i{0}; i < GetBindingCount(); ++i) { 501 if (mBindingInfo[i] != other->mBindingInfo[i]) { 502 return false; 503 } 504 } 505 return mBindingMap == other->mBindingMap; 506 } 507 GetPipelineCompatibilityToken() const508 PipelineCompatibilityToken BindGroupLayoutBase::GetPipelineCompatibilityToken() const { 509 return mPipelineCompatibilityToken; 510 } 511 GetBindingDataSize() const512 size_t BindGroupLayoutBase::GetBindingDataSize() const { 513 // | ------ buffer-specific ----------| ------------ object pointers -------------| 514 // | --- offsets + sizes -------------| --------------- Ref<ObjectBase> ----------| 515 // Followed by: 516 // |---------buffer size array--------| 517 // |-uint64_t[mUnverifiedBufferCount]-| 518 size_t objectPointerStart = mBindingCounts.bufferCount * sizeof(BufferBindingData); 519 ASSERT(IsAligned(objectPointerStart, alignof(Ref<ObjectBase>))); 520 size_t bufferSizeArrayStart = 521 Align(objectPointerStart + mBindingCounts.totalCount * sizeof(Ref<ObjectBase>), 522 sizeof(uint64_t)); 523 ASSERT(IsAligned(bufferSizeArrayStart, alignof(uint64_t))); 524 return bufferSizeArrayStart + mBindingCounts.unverifiedBufferCount * sizeof(uint64_t); 525 } 526 ComputeBindingDataPointers(void * dataStart) const527 BindGroupLayoutBase::BindingDataPointers BindGroupLayoutBase::ComputeBindingDataPointers( 528 void* dataStart) const { 529 BufferBindingData* bufferData = reinterpret_cast<BufferBindingData*>(dataStart); 530 auto bindings = reinterpret_cast<Ref<ObjectBase>*>(bufferData + mBindingCounts.bufferCount); 531 uint64_t* unverifiedBufferSizes = AlignPtr( 532 reinterpret_cast<uint64_t*>(bindings + mBindingCounts.totalCount), sizeof(uint64_t)); 533 534 ASSERT(IsPtrAligned(bufferData, alignof(BufferBindingData))); 535 ASSERT(IsPtrAligned(bindings, alignof(Ref<ObjectBase>))); 536 ASSERT(IsPtrAligned(unverifiedBufferSizes, alignof(uint64_t))); 537 538 return {{bufferData, GetBufferCount()}, 539 {bindings, GetBindingCount()}, 540 {unverifiedBufferSizes, mBindingCounts.unverifiedBufferCount}}; 541 } 542 IsStorageBufferBinding(BindingIndex bindingIndex) const543 bool BindGroupLayoutBase::IsStorageBufferBinding(BindingIndex bindingIndex) const { 544 ASSERT(bindingIndex < GetBufferCount()); 545 switch (GetBindingInfo(bindingIndex).buffer.type) { 546 case wgpu::BufferBindingType::Uniform: 547 return false; 548 case kInternalStorageBufferBinding: 549 case wgpu::BufferBindingType::Storage: 550 case wgpu::BufferBindingType::ReadOnlyStorage: 551 return true; 552 case wgpu::BufferBindingType::Undefined: 553 UNREACHABLE(); 554 } 555 } 556 557 } // namespace dawn_native 558