• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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