blob: ff9d69523cf50194b127ca38e89841486d64cc1d [file] [log] [blame]
// 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 "dawn_native/BindGroupLayout.h"
#include "dawn_native/Device.h"
namespace dawn_native {
namespace {
// Helper functions to perform binding-type specific validation
MaybeError ValidateBufferBinding(const DeviceBase* device,
const BindGroupBinding& binding,
wgpu::BufferUsage requiredUsage) {
if (binding.buffer == nullptr || binding.sampler != nullptr ||
binding.textureView != nullptr) {
return DAWN_VALIDATION_ERROR("expected buffer binding");
}
DAWN_TRY(device->ValidateObject(binding.buffer));
uint64_t bufferSize = binding.buffer->GetSize();
uint64_t bindingSize = (binding.size == wgpu::kWholeSize) ? bufferSize : binding.size;
if (bindingSize > bufferSize) {
return DAWN_VALIDATION_ERROR("Buffer binding size larger than the buffer");
}
// Note that no overflow can happen because we already checked that
// bufferSize >= bindingSize
if (binding.offset > bufferSize - bindingSize) {
return DAWN_VALIDATION_ERROR("Buffer binding doesn't fit in the buffer");
}
if (!IsAligned(binding.offset, 256)) {
return DAWN_VALIDATION_ERROR(
"Buffer offset for bind group needs to be 256-byte aligned");
}
if (!(binding.buffer->GetUsage() & requiredUsage)) {
return DAWN_VALIDATION_ERROR("buffer binding usage mismatch");
}
return {};
}
MaybeError ValidateTextureBinding(const DeviceBase* device,
const BindGroupBinding& binding,
wgpu::TextureUsage requiredUsage,
bool multisampledBinding,
wgpu::TextureComponentType requiredComponentType,
wgpu::TextureViewDimension requiredDimension) {
if (binding.textureView == nullptr || binding.sampler != nullptr ||
binding.buffer != nullptr) {
return DAWN_VALIDATION_ERROR("expected texture binding");
}
DAWN_TRY(device->ValidateObject(binding.textureView));
TextureBase* texture = binding.textureView->GetTexture();
if (!(texture->GetUsage() & requiredUsage)) {
return DAWN_VALIDATION_ERROR("texture binding usage mismatch");
}
if (texture->IsMultisampledTexture() != multisampledBinding) {
return DAWN_VALIDATION_ERROR("texture multisampling mismatch");
}
if (!texture->GetFormat().HasComponentType(requiredComponentType)) {
return DAWN_VALIDATION_ERROR("texture component type usage mismatch");
}
if (binding.textureView->GetDimension() != requiredDimension) {
return DAWN_VALIDATION_ERROR("texture view dimension mismatch");
}
return {};
}
MaybeError ValidateSamplerBinding(const DeviceBase* device,
const BindGroupBinding& binding) {
if (binding.sampler == nullptr || binding.textureView != nullptr ||
binding.buffer != nullptr) {
return DAWN_VALIDATION_ERROR("expected sampler binding");
}
DAWN_TRY(device->ValidateObject(binding.sampler));
return {};
}
} // anonymous namespace
MaybeError ValidateBindGroupDescriptor(DeviceBase* device,
const BindGroupDescriptor* descriptor) {
if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
}
DAWN_TRY(device->ValidateObject(descriptor->layout));
const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo =
descriptor->layout->GetBindingInfo();
if (descriptor->bindingCount != layoutInfo.mask.count()) {
return DAWN_VALIDATION_ERROR("numBindings mismatch");
}
std::bitset<kMaxBindingsPerGroup> bindingsSet;
for (uint32_t i = 0; i < descriptor->bindingCount; ++i) {
const BindGroupBinding& binding = descriptor->bindings[i];
uint32_t bindingIndex = binding.binding;
// Check that we can set this binding.
if (bindingIndex >= kMaxBindingsPerGroup) {
return DAWN_VALIDATION_ERROR("binding index too high");
}
if (!layoutInfo.mask[bindingIndex]) {
return DAWN_VALIDATION_ERROR("setting non-existent binding");
}
if (bindingsSet[bindingIndex]) {
return DAWN_VALIDATION_ERROR("binding set twice");
}
bindingsSet.set(bindingIndex);
// Perform binding-type specific validation.
switch (layoutInfo.types[bindingIndex]) {
case wgpu::BindingType::UniformBuffer:
DAWN_TRY(ValidateBufferBinding(device, binding, wgpu::BufferUsage::Uniform));
break;
case wgpu::BindingType::StorageBuffer:
case wgpu::BindingType::ReadonlyStorageBuffer:
DAWN_TRY(ValidateBufferBinding(device, binding, wgpu::BufferUsage::Storage));
break;
case wgpu::BindingType::SampledTexture:
DAWN_TRY(ValidateTextureBinding(device, binding, wgpu::TextureUsage::Sampled,
layoutInfo.multisampled[bindingIndex],
layoutInfo.textureComponentTypes[bindingIndex],
layoutInfo.textureDimensions[bindingIndex]));
break;
case wgpu::BindingType::Sampler:
DAWN_TRY(ValidateSamplerBinding(device, binding));
break;
case wgpu::BindingType::StorageTexture:
UNREACHABLE();
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 == layoutInfo.mask);
return {};
}
// BindGroup
BindGroupBase::BindGroupBase(DeviceBase* device, const BindGroupDescriptor* descriptor)
: ObjectBase(device), mLayout(descriptor->layout) {
for (uint32_t i = 0; i < descriptor->bindingCount; ++i) {
const BindGroupBinding& binding = descriptor->bindings[i];
uint32_t bindingIndex = binding.binding;
ASSERT(bindingIndex < kMaxBindingsPerGroup);
// Only a single binding type should be set, so once we found it we can skip to the
// next loop iteration.
if (binding.buffer != nullptr) {
ASSERT(mBindings[bindingIndex].Get() == nullptr);
mBindings[bindingIndex] = binding.buffer;
mOffsets[bindingIndex] = binding.offset;
uint64_t bufferSize =
(binding.size == wgpu::kWholeSize) ? binding.buffer->GetSize() : binding.size;
mSizes[bindingIndex] = bufferSize;
continue;
}
if (binding.textureView != nullptr) {
ASSERT(mBindings[bindingIndex].Get() == nullptr);
mBindings[bindingIndex] = binding.textureView;
continue;
}
if (binding.sampler != nullptr) {
ASSERT(mBindings[bindingIndex].Get() == nullptr);
mBindings[bindingIndex] = binding.sampler;
continue;
}
}
}
BindGroupBase::BindGroupBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
}
// static
BindGroupBase* BindGroupBase::MakeError(DeviceBase* device) {
return new BindGroupBase(device, ObjectBase::kError);
}
} // namespace dawn_native