| // Copyright 2017 The Dawn Authors | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "dawn_native/BindGroup.h" | 
 |  | 
 | #include "common/Assert.h" | 
 | #include "common/Math.h" | 
 | #include "common/ityp_bitset.h" | 
 | #include "dawn_native/BindGroupLayout.h" | 
 | #include "dawn_native/Buffer.h" | 
 | #include "dawn_native/ChainUtils_autogen.h" | 
 | #include "dawn_native/Device.h" | 
 | #include "dawn_native/ExternalTexture.h" | 
 | #include "dawn_native/ObjectBase.h" | 
 | #include "dawn_native/ObjectType_autogen.h" | 
 | #include "dawn_native/Sampler.h" | 
 | #include "dawn_native/Texture.h" | 
 |  | 
 | namespace dawn_native { | 
 |  | 
 |     namespace { | 
 |  | 
 |         // Helper functions to perform binding-type specific validation | 
 |  | 
 |         MaybeError ValidateBufferBinding(const DeviceBase* device, | 
 |                                          const BindGroupEntry& entry, | 
 |                                          const BindingInfo& bindingInfo) { | 
 |             DAWN_INVALID_IF(entry.buffer == nullptr, "Binding entry buffer not set."); | 
 |  | 
 |             DAWN_INVALID_IF(entry.sampler != nullptr || entry.textureView != nullptr, | 
 |                             "Expected only buffer to be set for binding entry."); | 
 |  | 
 |             DAWN_INVALID_IF(entry.nextInChain != nullptr, "nextInChain must be nullptr."); | 
 |  | 
 |             DAWN_TRY(device->ValidateObject(entry.buffer)); | 
 |  | 
 |             ASSERT(bindingInfo.bindingType == BindingInfoType::Buffer); | 
 |  | 
 |             wgpu::BufferUsage requiredUsage; | 
 |             uint64_t maxBindingSize; | 
 |             uint64_t requiredBindingAlignment; | 
 |             switch (bindingInfo.buffer.type) { | 
 |                 case wgpu::BufferBindingType::Uniform: | 
 |                     requiredUsage = wgpu::BufferUsage::Uniform; | 
 |                     maxBindingSize = device->GetLimits().v1.maxUniformBufferBindingSize; | 
 |                     requiredBindingAlignment = | 
 |                         device->GetLimits().v1.minUniformBufferOffsetAlignment; | 
 |                     break; | 
 |                 case wgpu::BufferBindingType::Storage: | 
 |                 case wgpu::BufferBindingType::ReadOnlyStorage: | 
 |                     requiredUsage = wgpu::BufferUsage::Storage; | 
 |                     maxBindingSize = device->GetLimits().v1.maxStorageBufferBindingSize; | 
 |                     requiredBindingAlignment = | 
 |                         device->GetLimits().v1.minStorageBufferOffsetAlignment; | 
 |                     break; | 
 |                 case kInternalStorageBufferBinding: | 
 |                     requiredUsage = kInternalStorageBuffer; | 
 |                     maxBindingSize = device->GetLimits().v1.maxStorageBufferBindingSize; | 
 |                     requiredBindingAlignment = | 
 |                         device->GetLimits().v1.minStorageBufferOffsetAlignment; | 
 |                     break; | 
 |                 case wgpu::BufferBindingType::Undefined: | 
 |                     UNREACHABLE(); | 
 |             } | 
 |  | 
 |             uint64_t bufferSize = entry.buffer->GetSize(); | 
 |  | 
 |             // Handle wgpu::WholeSize, avoiding overflows. | 
 |             DAWN_INVALID_IF(entry.offset > bufferSize, | 
 |                             "Binding offset (%u) is larger than the size (%u) of %s.", entry.offset, | 
 |                             bufferSize, entry.buffer); | 
 |  | 
 |             uint64_t bindingSize = | 
 |                 (entry.size == wgpu::kWholeSize) ? bufferSize - entry.offset : entry.size; | 
 |  | 
 |             DAWN_INVALID_IF(bindingSize > bufferSize, | 
 |                             "Binding size (%u) is larger than the size (%u) of %s.", bindingSize, | 
 |                             bufferSize, entry.buffer); | 
 |  | 
 |             DAWN_INVALID_IF(bindingSize == 0, "Binding size is zero"); | 
 |  | 
 |             // Note that no overflow can happen because we already checked that | 
 |             // bufferSize >= bindingSize | 
 |             DAWN_INVALID_IF( | 
 |                 entry.offset > bufferSize - bindingSize, | 
 |                 "Binding range (offset: %u, size: %u) doesn't fit in the size (%u) of %s.", | 
 |                 entry.offset, bufferSize, bindingSize, entry.buffer); | 
 |  | 
 |             DAWN_INVALID_IF(!IsAligned(entry.offset, requiredBindingAlignment), | 
 |                             "Offset (%u) does not satisfy the minimum %s alignment (%u).", | 
 |                             entry.offset, bindingInfo.buffer.type, requiredBindingAlignment); | 
 |  | 
 |             DAWN_INVALID_IF(!(entry.buffer->GetUsage() & requiredUsage), | 
 |                             "Binding usage (%s) of %s doesn't match expected usage (%s).", | 
 |                             entry.buffer->GetUsage(), entry.buffer, requiredUsage); | 
 |  | 
 |             DAWN_INVALID_IF(bindingSize < bindingInfo.buffer.minBindingSize, | 
 |                             "Binding size (%u) is smaller than the minimum binding size (%u).", | 
 |                             bindingSize, bindingInfo.buffer.minBindingSize); | 
 |  | 
 |             DAWN_INVALID_IF(bindingSize > maxBindingSize, | 
 |                             "Binding size (%u) is larger than the maximum binding size (%u).", | 
 |                             bindingSize, maxBindingSize); | 
 |  | 
 |             return {}; | 
 |         } | 
 |  | 
 |         MaybeError ValidateTextureBinding(DeviceBase* device, | 
 |                                           const BindGroupEntry& entry, | 
 |                                           const BindingInfo& bindingInfo) { | 
 |             DAWN_INVALID_IF(entry.textureView == nullptr, "Binding entry textureView not set."); | 
 |  | 
 |             DAWN_INVALID_IF(entry.sampler != nullptr || entry.buffer != nullptr, | 
 |                             "Expected only textureView to be set for binding entry."); | 
 |  | 
 |             DAWN_INVALID_IF(entry.nextInChain != nullptr, "nextInChain must be nullptr."); | 
 |  | 
 |             DAWN_TRY(device->ValidateObject(entry.textureView)); | 
 |  | 
 |             TextureViewBase* view = entry.textureView; | 
 |  | 
 |             Aspect aspect = view->GetAspects(); | 
 |             // TODO(dawn:563): Format Aspects | 
 |             DAWN_INVALID_IF(!HasOneBit(aspect), "Multiple aspects selected in %s.", view); | 
 |  | 
 |             TextureBase* texture = view->GetTexture(); | 
 |             switch (bindingInfo.bindingType) { | 
 |                 case BindingInfoType::Texture: { | 
 |                     SampleTypeBit supportedTypes = | 
 |                         texture->GetFormat().GetAspectInfo(aspect).supportedSampleTypes; | 
 |                     SampleTypeBit requiredType = | 
 |                         SampleTypeToSampleTypeBit(bindingInfo.texture.sampleType); | 
 |  | 
 |                     DAWN_INVALID_IF( | 
 |                         !(texture->GetUsage() & wgpu::TextureUsage::TextureBinding), | 
 |                         "Usage (%s) of %s doesn't include TextureUsage::TextureBinding.", | 
 |                         texture->GetUsage(), texture); | 
 |  | 
 |                     DAWN_INVALID_IF( | 
 |                         texture->IsMultisampledTexture() != bindingInfo.texture.multisampled, | 
 |                         "Sample count (%u) of %s doesn't match expectation (multisampled: %d).", | 
 |                         texture->GetSampleCount(), texture, bindingInfo.texture.multisampled); | 
 |  | 
 |                     // TODO(dawn:563): Improve error message. | 
 |                     DAWN_INVALID_IF((supportedTypes & requiredType) == 0, | 
 |                                     "Texture component type usage mismatch."); | 
 |  | 
 |                     DAWN_INVALID_IF( | 
 |                         entry.textureView->GetDimension() != bindingInfo.texture.viewDimension, | 
 |                         "Dimension (%s) of %s doesn't match the expected dimension (%s).", | 
 |                         entry.textureView->GetDimension(), entry.textureView, | 
 |                         bindingInfo.texture.viewDimension); | 
 |                     break; | 
 |                 } | 
 |                 case BindingInfoType::StorageTexture: { | 
 |                     DAWN_INVALID_IF( | 
 |                         !(texture->GetUsage() & wgpu::TextureUsage::StorageBinding), | 
 |                         "Usage (%s) of %s doesn't include TextureUsage::StorageBinding.", | 
 |                         texture->GetUsage(), texture); | 
 |  | 
 |                     ASSERT(!texture->IsMultisampledTexture()); | 
 |  | 
 |                     DAWN_INVALID_IF( | 
 |                         texture->GetFormat().format != bindingInfo.storageTexture.format, | 
 |                         "Format (%s) of %s expected to be (%s).", texture->GetFormat().format, | 
 |                         texture, bindingInfo.storageTexture.format); | 
 |  | 
 |                     DAWN_INVALID_IF( | 
 |                         entry.textureView->GetDimension() != | 
 |                             bindingInfo.storageTexture.viewDimension, | 
 |                         "Dimension (%s) of %s doesn't match the expected dimension (%s).", | 
 |                         entry.textureView->GetDimension(), entry.textureView, | 
 |                         bindingInfo.storageTexture.viewDimension); | 
 |                     break; | 
 |                 } | 
 |                 default: | 
 |                     UNREACHABLE(); | 
 |                     break; | 
 |             } | 
 |  | 
 |             return {}; | 
 |         } | 
 |  | 
 |         MaybeError ValidateSamplerBinding(const DeviceBase* device, | 
 |                                           const BindGroupEntry& entry, | 
 |                                           const BindingInfo& bindingInfo) { | 
 |             DAWN_INVALID_IF(entry.sampler == nullptr, "Binding entry sampler not set."); | 
 |  | 
 |             DAWN_INVALID_IF(entry.textureView != nullptr || entry.buffer != nullptr, | 
 |                             "Expected only sampler to be set for binding entry."); | 
 |  | 
 |             DAWN_INVALID_IF(entry.nextInChain != nullptr, "nextInChain must be nullptr."); | 
 |  | 
 |             DAWN_TRY(device->ValidateObject(entry.sampler)); | 
 |  | 
 |             ASSERT(bindingInfo.bindingType == BindingInfoType::Sampler); | 
 |  | 
 |             switch (bindingInfo.sampler.type) { | 
 |                 case wgpu::SamplerBindingType::NonFiltering: | 
 |                     DAWN_INVALID_IF( | 
 |                         entry.sampler->IsFiltering(), | 
 |                         "Filtering sampler %s is incompatible with non-filtering sampler " | 
 |                         "binding.", | 
 |                         entry.sampler); | 
 |                     DAWN_FALLTHROUGH; | 
 |                 case wgpu::SamplerBindingType::Filtering: | 
 |                     DAWN_INVALID_IF( | 
 |                         entry.sampler->IsComparison(), | 
 |                         "Comparison sampler %s is incompatible with non-comparison sampler " | 
 |                         "binding.", | 
 |                         entry.sampler); | 
 |                     break; | 
 |                 case wgpu::SamplerBindingType::Comparison: | 
 |                     DAWN_INVALID_IF( | 
 |                         !entry.sampler->IsComparison(), | 
 |                         "Non-comparison sampler %s is imcompatible with comparison sampler " | 
 |                         "binding.", | 
 |                         entry.sampler); | 
 |                     break; | 
 |                 default: | 
 |                     UNREACHABLE(); | 
 |                     break; | 
 |             } | 
 |  | 
 |             return {}; | 
 |         } | 
 |  | 
 |         MaybeError ValidateExternalTextureBinding(const DeviceBase* device, | 
 |                                                   const BindGroupEntry& entry, | 
 |                                                   const BindingInfo& bindingInfo) { | 
 |             const ExternalTextureBindingEntry* externalTextureBindingEntry = nullptr; | 
 |             FindInChain(entry.nextInChain, &externalTextureBindingEntry); | 
 |  | 
 |             DAWN_INVALID_IF(externalTextureBindingEntry == nullptr, | 
 |                             "Binding entry external texture not set."); | 
 |  | 
 |             DAWN_INVALID_IF( | 
 |                 entry.sampler != nullptr || entry.textureView != nullptr || entry.buffer != nullptr, | 
 |                 "Expected only external texture to be set for binding entry."); | 
 |  | 
 |             DAWN_TRY(ValidateSingleSType(externalTextureBindingEntry->nextInChain, | 
 |                                          wgpu::SType::ExternalTextureBindingEntry)); | 
 |  | 
 |             DAWN_TRY(device->ValidateObject(externalTextureBindingEntry->externalTexture)); | 
 |  | 
 |             return {}; | 
 |         } | 
 |  | 
 |     }  // anonymous namespace | 
 |  | 
 |     MaybeError ValidateBindGroupDescriptor(DeviceBase* device, | 
 |                                            const BindGroupDescriptor* descriptor) { | 
 |         DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr."); | 
 |  | 
 |         DAWN_TRY(device->ValidateObject(descriptor->layout)); | 
 |  | 
 |         DAWN_INVALID_IF( | 
 |             BindingIndex(descriptor->entryCount) != descriptor->layout->GetBindingCount(), | 
 |             "Number of entries (%u) did not match the number of entries (%u) specified in %s", | 
 |             descriptor->entryCount, static_cast<uint32_t>(descriptor->layout->GetBindingCount()), | 
 |             descriptor->layout); | 
 |  | 
 |         const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap(); | 
 |         ASSERT(bindingMap.size() <= kMaxBindingsPerPipelineLayout); | 
 |  | 
 |         ityp::bitset<BindingIndex, kMaxBindingsPerPipelineLayout> bindingsSet; | 
 |         for (uint32_t i = 0; i < descriptor->entryCount; ++i) { | 
 |             const BindGroupEntry& entry = descriptor->entries[i]; | 
 |  | 
 |             const auto& it = bindingMap.find(BindingNumber(entry.binding)); | 
 |             DAWN_INVALID_IF(it == bindingMap.end(), | 
 |                             "In entries[%u], binding index %u not present in the bind group layout", | 
 |                             i, entry.binding); | 
 |  | 
 |             BindingIndex bindingIndex = it->second; | 
 |             ASSERT(bindingIndex < descriptor->layout->GetBindingCount()); | 
 |  | 
 |             DAWN_INVALID_IF(bindingsSet[bindingIndex], | 
 |                             "In entries[%u], binding index %u already used by a previous entry", i, | 
 |                             entry.binding); | 
 |  | 
 |             bindingsSet.set(bindingIndex); | 
 |  | 
 |             const BindingInfo& bindingInfo = descriptor->layout->GetBindingInfo(bindingIndex); | 
 |  | 
 |             // Perform binding-type specific validation. | 
 |             switch (bindingInfo.bindingType) { | 
 |                 case BindingInfoType::Buffer: | 
 |                     DAWN_TRY_CONTEXT(ValidateBufferBinding(device, entry, bindingInfo), | 
 |                                      "validating entries[%u] as a Buffer", i); | 
 |                     break; | 
 |                 case BindingInfoType::Texture: | 
 |                 case BindingInfoType::StorageTexture: | 
 |                     DAWN_TRY_CONTEXT(ValidateTextureBinding(device, entry, bindingInfo), | 
 |                                      "validating entries[%u] as a Texture", i); | 
 |                     break; | 
 |                 case BindingInfoType::Sampler: | 
 |                     DAWN_TRY_CONTEXT(ValidateSamplerBinding(device, entry, bindingInfo), | 
 |                                      "validating entries[%u] as a Sampler", i); | 
 |                     break; | 
 |                 case BindingInfoType::ExternalTexture: | 
 |                     DAWN_TRY_CONTEXT(ValidateExternalTextureBinding(device, entry, bindingInfo), | 
 |                                      "validating entries[%u] as an ExternalTexture", i); | 
 |                     break; | 
 |             } | 
 |         } | 
 |  | 
 |         // This should always be true because | 
 |         //  - numBindings has to match between the bind group and its layout. | 
 |         //  - Each binding must be set at most once | 
 |         // | 
 |         // We don't validate the equality because it wouldn't be possible to cover it with a test. | 
 |         ASSERT(bindingsSet.count() == bindingMap.size()); | 
 |  | 
 |         return {}; | 
 |     }  // anonymous namespace | 
 |  | 
 |     // BindGroup | 
 |  | 
 |     BindGroupBase::BindGroupBase(DeviceBase* device, | 
 |                                  const BindGroupDescriptor* descriptor, | 
 |                                  void* bindingDataStart) | 
 |         : ApiObjectBase(device, kLabelNotImplemented), | 
 |           mLayout(descriptor->layout), | 
 |           mBindingData(mLayout->ComputeBindingDataPointers(bindingDataStart)) { | 
 |         for (BindingIndex i{0}; i < mLayout->GetBindingCount(); ++i) { | 
 |             // TODO(enga): Shouldn't be needed when bindings are tightly packed. | 
 |             // This is to fill Ref<ObjectBase> holes with nullptrs. | 
 |             new (&mBindingData.bindings[i]) Ref<ObjectBase>(); | 
 |         } | 
 |  | 
 |         for (uint32_t i = 0; i < descriptor->entryCount; ++i) { | 
 |             const BindGroupEntry& entry = descriptor->entries[i]; | 
 |  | 
 |             BindingIndex bindingIndex = | 
 |                 descriptor->layout->GetBindingIndex(BindingNumber(entry.binding)); | 
 |             ASSERT(bindingIndex < mLayout->GetBindingCount()); | 
 |  | 
 |             // Only a single binding type should be set, so once we found it we can skip to the | 
 |             // next loop iteration. | 
 |  | 
 |             if (entry.buffer != nullptr) { | 
 |                 ASSERT(mBindingData.bindings[bindingIndex] == nullptr); | 
 |                 mBindingData.bindings[bindingIndex] = entry.buffer; | 
 |                 mBindingData.bufferData[bindingIndex].offset = entry.offset; | 
 |                 uint64_t bufferSize = (entry.size == wgpu::kWholeSize) | 
 |                                           ? entry.buffer->GetSize() - entry.offset | 
 |                                           : entry.size; | 
 |                 mBindingData.bufferData[bindingIndex].size = bufferSize; | 
 |                 continue; | 
 |             } | 
 |  | 
 |             if (entry.textureView != nullptr) { | 
 |                 ASSERT(mBindingData.bindings[bindingIndex] == nullptr); | 
 |                 mBindingData.bindings[bindingIndex] = entry.textureView; | 
 |                 continue; | 
 |             } | 
 |  | 
 |             if (entry.sampler != nullptr) { | 
 |                 ASSERT(mBindingData.bindings[bindingIndex] == nullptr); | 
 |                 mBindingData.bindings[bindingIndex] = entry.sampler; | 
 |                 continue; | 
 |             } | 
 |  | 
 |             const ExternalTextureBindingEntry* externalTextureBindingEntry = nullptr; | 
 |             FindInChain(entry.nextInChain, &externalTextureBindingEntry); | 
 |             if (externalTextureBindingEntry != nullptr) { | 
 |                 ASSERT(mBindingData.bindings[bindingIndex] == nullptr); | 
 |                 mBindingData.bindings[bindingIndex] = externalTextureBindingEntry->externalTexture; | 
 |                 continue; | 
 |             } | 
 |         } | 
 |  | 
 |         uint32_t packedIdx = 0; | 
 |         for (BindingIndex bindingIndex{0}; bindingIndex < descriptor->layout->GetBufferCount(); | 
 |              ++bindingIndex) { | 
 |             if (descriptor->layout->GetBindingInfo(bindingIndex).buffer.minBindingSize == 0) { | 
 |                 mBindingData.unverifiedBufferSizes[packedIdx] = | 
 |                     mBindingData.bufferData[bindingIndex].size; | 
 |                 ++packedIdx; | 
 |             } | 
 |         } | 
 |  | 
 |         TrackInDevice(); | 
 |     } | 
 |  | 
 |     BindGroupBase::BindGroupBase(DeviceBase* device) : ApiObjectBase(device, kLabelNotImplemented) { | 
 |         TrackInDevice(); | 
 |     } | 
 |  | 
 |     BindGroupBase::~BindGroupBase() { | 
 |         if (mLayout != nullptr) { | 
 |             ASSERT(!IsError()); | 
 |             for (BindingIndex i{0}; i < mLayout->GetBindingCount(); ++i) { | 
 |                 mBindingData.bindings[i].~Ref<ObjectBase>(); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     void BindGroupBase::DeleteThis() { | 
 |         // Add another ref to the layout so that if this is the last ref, the layout | 
 |         // is destroyed after the bind group. The bind group is slab-allocated inside | 
 |         // memory owned by the layout (except for the null backend). | 
 |         Ref<BindGroupLayoutBase> layout = mLayout; | 
 |         ApiObjectBase::DeleteThis(); | 
 |     } | 
 |  | 
 |     BindGroupBase::BindGroupBase(DeviceBase* device, ObjectBase::ErrorTag tag) | 
 |         : ApiObjectBase(device, tag), mBindingData() { | 
 |     } | 
 |  | 
 |     // static | 
 |     BindGroupBase* BindGroupBase::MakeError(DeviceBase* device) { | 
 |         return new BindGroupBase(device, ObjectBase::kError); | 
 |     } | 
 |  | 
 |     ObjectType BindGroupBase::GetType() const { | 
 |         return ObjectType::BindGroup; | 
 |     } | 
 |  | 
 |     BindGroupLayoutBase* BindGroupBase::GetLayout() { | 
 |         ASSERT(!IsError()); | 
 |         return mLayout.Get(); | 
 |     } | 
 |  | 
 |     const BindGroupLayoutBase* BindGroupBase::GetLayout() const { | 
 |         ASSERT(!IsError()); | 
 |         return mLayout.Get(); | 
 |     } | 
 |  | 
 |     const ityp::span<uint32_t, uint64_t>& BindGroupBase::GetUnverifiedBufferSizes() const { | 
 |         ASSERT(!IsError()); | 
 |         return mBindingData.unverifiedBufferSizes; | 
 |     } | 
 |  | 
 |     BufferBinding BindGroupBase::GetBindingAsBufferBinding(BindingIndex bindingIndex) { | 
 |         ASSERT(!IsError()); | 
 |         ASSERT(bindingIndex < mLayout->GetBindingCount()); | 
 |         ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Buffer); | 
 |         BufferBase* buffer = static_cast<BufferBase*>(mBindingData.bindings[bindingIndex].Get()); | 
 |         return {buffer, mBindingData.bufferData[bindingIndex].offset, | 
 |                 mBindingData.bufferData[bindingIndex].size}; | 
 |     } | 
 |  | 
 |     SamplerBase* BindGroupBase::GetBindingAsSampler(BindingIndex bindingIndex) const { | 
 |         ASSERT(!IsError()); | 
 |         ASSERT(bindingIndex < mLayout->GetBindingCount()); | 
 |         ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Sampler); | 
 |         return static_cast<SamplerBase*>(mBindingData.bindings[bindingIndex].Get()); | 
 |     } | 
 |  | 
 |     TextureViewBase* BindGroupBase::GetBindingAsTextureView(BindingIndex bindingIndex) { | 
 |         ASSERT(!IsError()); | 
 |         ASSERT(bindingIndex < mLayout->GetBindingCount()); | 
 |         ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Texture || | 
 |                mLayout->GetBindingInfo(bindingIndex).bindingType == | 
 |                    BindingInfoType::StorageTexture); | 
 |         return static_cast<TextureViewBase*>(mBindingData.bindings[bindingIndex].Get()); | 
 |     } | 
 |  | 
 |     ExternalTextureBase* BindGroupBase::GetBindingAsExternalTexture(BindingIndex bindingIndex) { | 
 |         ASSERT(!IsError()); | 
 |         ASSERT(bindingIndex < mLayout->GetBindingCount()); | 
 |         ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == | 
 |                BindingInfoType::ExternalTexture); | 
 |         return static_cast<ExternalTextureBase*>(mBindingData.bindings[bindingIndex].Get()); | 
 |     } | 
 |  | 
 | }  // namespace dawn_native |