WebGPU error handling 1: Return error objects on errors.

Dawn used to return "nullptr" when an error happened while creating an
object. To match WebGPU we want to return valid pointers but to "error"
objects.

This commit implements WebGPU error handling for all "descriptorized"
objects and changes the nullptr error checks into "ValidateObject"
checks. This method is used both to check that the object isn't an
error, but also that all objects in a function call are from the same
device.

New validation is added to objects with methods (apart from Device) so
they check they aren't error objects.

A large number of ASSERTs were added to check that frontend objects
aren't accessed when they are errors, so that missing validation would
hit the asserts instead of crashing randomly.

The bind group validation tests were modified to test the behavior with
both nullptrs and error objects.

Future commits will change the CommandBufferBuilder to not be a builder
anymore but an "Encoder" instead with special-cased error handling. Then
once all objects are descriptorized, the notion of builders will be
removed from the code.

BUG=dawn:8

Change-Id: I8647712d5de3deb0e99e3bc58f34496f67710edd
Reviewed-on: https://dawn-review.googlesource.com/c/4360
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez 2019-02-13 13:09:18 +00:00 committed by Commit Bot service account
parent f872e6924c
commit a594f8fdb4
35 changed files with 471 additions and 112 deletions

View File

@ -28,12 +28,14 @@ namespace dawn_native {
// Helper functions to perform binding-type specific validation // Helper functions to perform binding-type specific validation
MaybeError ValidateBufferBinding(const BindGroupBinding& binding, MaybeError ValidateBufferBinding(const DeviceBase* device,
const BindGroupBinding& binding,
dawn::BufferUsageBit requiredUsage) { dawn::BufferUsageBit requiredUsage) {
if (binding.buffer == nullptr || binding.sampler != nullptr || if (binding.buffer == nullptr || binding.sampler != nullptr ||
binding.textureView != nullptr) { binding.textureView != nullptr) {
return DAWN_VALIDATION_ERROR("expected buffer binding"); return DAWN_VALIDATION_ERROR("expected buffer binding");
} }
DAWN_TRY(device->ValidateObject(binding.buffer));
uint32_t bufferSize = binding.buffer->GetSize(); uint32_t bufferSize = binding.buffer->GetSize();
if (binding.size > bufferSize) { if (binding.size > bufferSize) {
@ -58,12 +60,14 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateTextureBinding(const BindGroupBinding& binding, MaybeError ValidateTextureBinding(const DeviceBase* device,
const BindGroupBinding& binding,
dawn::TextureUsageBit requiredUsage) { dawn::TextureUsageBit requiredUsage) {
if (binding.textureView == nullptr || binding.sampler != nullptr || if (binding.textureView == nullptr || binding.sampler != nullptr ||
binding.buffer != nullptr) { binding.buffer != nullptr) {
return DAWN_VALIDATION_ERROR("expected texture binding"); return DAWN_VALIDATION_ERROR("expected texture binding");
} }
DAWN_TRY(device->ValidateObject(binding.textureView));
if (!(binding.textureView->GetTexture()->GetUsage() & requiredUsage)) { if (!(binding.textureView->GetTexture()->GetUsage() & requiredUsage)) {
return DAWN_VALIDATION_ERROR("texture binding usage mismatch"); return DAWN_VALIDATION_ERROR("texture binding usage mismatch");
@ -72,24 +76,26 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateSamplerBinding(const BindGroupBinding& binding) { MaybeError ValidateSamplerBinding(const DeviceBase* device,
const BindGroupBinding& binding) {
if (binding.sampler == nullptr || binding.textureView != nullptr || if (binding.sampler == nullptr || binding.textureView != nullptr ||
binding.buffer != nullptr) { binding.buffer != nullptr) {
return DAWN_VALIDATION_ERROR("expected sampler binding"); return DAWN_VALIDATION_ERROR("expected sampler binding");
} }
DAWN_TRY(device->ValidateObject(binding.sampler));
return {}; return {};
} }
} // anonymous namespace } // anonymous namespace
MaybeError ValidateBindGroupDescriptor(DeviceBase*, const BindGroupDescriptor* descriptor) { MaybeError ValidateBindGroupDescriptor(DeviceBase* device,
const BindGroupDescriptor* descriptor) {
if (descriptor->nextInChain != nullptr) { if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
} }
if (descriptor->layout == nullptr) { DAWN_TRY(device->ValidateObject(descriptor->layout));
return DAWN_VALIDATION_ERROR("layout cannot be null");
}
const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo = const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo =
descriptor->layout->GetBindingInfo(); descriptor->layout->GetBindingInfo();
@ -120,16 +126,17 @@ namespace dawn_native {
// Perform binding-type specific validation. // Perform binding-type specific validation.
switch (layoutInfo.types[bindingIndex]) { switch (layoutInfo.types[bindingIndex]) {
case dawn::BindingType::UniformBuffer: case dawn::BindingType::UniformBuffer:
DAWN_TRY(ValidateBufferBinding(binding, dawn::BufferUsageBit::Uniform)); DAWN_TRY(ValidateBufferBinding(device, binding, dawn::BufferUsageBit::Uniform));
break; break;
case dawn::BindingType::StorageBuffer: case dawn::BindingType::StorageBuffer:
DAWN_TRY(ValidateBufferBinding(binding, dawn::BufferUsageBit::Storage)); DAWN_TRY(ValidateBufferBinding(device, binding, dawn::BufferUsageBit::Storage));
break; break;
case dawn::BindingType::SampledTexture: case dawn::BindingType::SampledTexture:
DAWN_TRY(ValidateTextureBinding(binding, dawn::TextureUsageBit::Sampled)); DAWN_TRY(
ValidateTextureBinding(device, binding, dawn::TextureUsageBit::Sampled));
break; break;
case dawn::BindingType::Sampler: case dawn::BindingType::Sampler:
DAWN_TRY(ValidateSamplerBinding(binding)); DAWN_TRY(ValidateSamplerBinding(device, binding));
break; break;
} }
} }
@ -179,11 +186,22 @@ namespace dawn_native {
} }
} }
BindGroupBase::BindGroupBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
}
// static
BindGroupBase* BindGroupBase::MakeError(DeviceBase* device) {
return new BindGroupBase(device, ObjectBase::kError);
}
const BindGroupLayoutBase* BindGroupBase::GetLayout() const { const BindGroupLayoutBase* BindGroupBase::GetLayout() const {
ASSERT(!IsError());
return mLayout.Get(); return mLayout.Get();
} }
BufferBinding BindGroupBase::GetBindingAsBufferBinding(size_t binding) { BufferBinding BindGroupBase::GetBindingAsBufferBinding(size_t binding) {
ASSERT(!IsError());
ASSERT(binding < kMaxBindingsPerGroup); ASSERT(binding < kMaxBindingsPerGroup);
ASSERT(mLayout->GetBindingInfo().mask[binding]); ASSERT(mLayout->GetBindingInfo().mask[binding]);
ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::UniformBuffer || ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::UniformBuffer ||
@ -193,6 +211,7 @@ namespace dawn_native {
} }
SamplerBase* BindGroupBase::GetBindingAsSampler(size_t binding) { SamplerBase* BindGroupBase::GetBindingAsSampler(size_t binding) {
ASSERT(!IsError());
ASSERT(binding < kMaxBindingsPerGroup); ASSERT(binding < kMaxBindingsPerGroup);
ASSERT(mLayout->GetBindingInfo().mask[binding]); ASSERT(mLayout->GetBindingInfo().mask[binding]);
ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::Sampler); ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::Sampler);
@ -200,9 +219,11 @@ namespace dawn_native {
} }
TextureViewBase* BindGroupBase::GetBindingAsTextureView(size_t binding) { TextureViewBase* BindGroupBase::GetBindingAsTextureView(size_t binding) {
ASSERT(!IsError());
ASSERT(binding < kMaxBindingsPerGroup); ASSERT(binding < kMaxBindingsPerGroup);
ASSERT(mLayout->GetBindingInfo().mask[binding]); ASSERT(mLayout->GetBindingInfo().mask[binding]);
ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::SampledTexture); ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::SampledTexture);
return reinterpret_cast<TextureViewBase*>(mBindings[binding].Get()); return reinterpret_cast<TextureViewBase*>(mBindings[binding].Get());
} }
} // namespace dawn_native } // namespace dawn_native

View File

@ -42,12 +42,16 @@ namespace dawn_native {
public: public:
BindGroupBase(DeviceBase* device, const BindGroupDescriptor* descriptor); BindGroupBase(DeviceBase* device, const BindGroupDescriptor* descriptor);
static BindGroupBase* MakeError(DeviceBase* device);
const BindGroupLayoutBase* GetLayout() const; const BindGroupLayoutBase* GetLayout() const;
BufferBinding GetBindingAsBufferBinding(size_t binding); BufferBinding GetBindingAsBufferBinding(size_t binding);
SamplerBase* GetBindingAsSampler(size_t binding); SamplerBase* GetBindingAsSampler(size_t binding);
TextureViewBase* GetBindingAsTextureView(size_t binding); TextureViewBase* GetBindingAsTextureView(size_t binding);
private: private:
BindGroupBase(DeviceBase* device, ObjectBase::ErrorTag tag);
Ref<BindGroupLayoutBase> mLayout; Ref<BindGroupLayoutBase> mLayout;
std::array<Ref<ObjectBase>, kMaxBindingsPerGroup> mBindings; std::array<Ref<ObjectBase>, kMaxBindingsPerGroup> mBindings;
std::array<uint32_t, kMaxBindingsPerGroup> mOffsets; std::array<uint32_t, kMaxBindingsPerGroup> mOffsets;

View File

@ -92,14 +92,25 @@ namespace dawn_native {
} }
} }
BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag), mIsBlueprint(true) {
}
BindGroupLayoutBase::~BindGroupLayoutBase() { BindGroupLayoutBase::~BindGroupLayoutBase() {
// Do not uncache the actual cached object if we are a blueprint // Do not uncache the actual cached object if we are a blueprint
if (!mIsBlueprint) { if (!mIsBlueprint) {
ASSERT(!IsError());
GetDevice()->UncacheBindGroupLayout(this); GetDevice()->UncacheBindGroupLayout(this);
} }
} }
// static
BindGroupLayoutBase* BindGroupLayoutBase::MakeError(DeviceBase* device) {
return new BindGroupLayoutBase(device, ObjectBase::kError);
}
const BindGroupLayoutBase::LayoutBindingInfo& BindGroupLayoutBase::GetBindingInfo() const { const BindGroupLayoutBase::LayoutBindingInfo& BindGroupLayoutBase::GetBindingInfo() const {
ASSERT(!IsError());
return mBindingInfo; return mBindingInfo;
} }

View File

@ -37,6 +37,8 @@ namespace dawn_native {
bool blueprint = false); bool blueprint = false);
~BindGroupLayoutBase() override; ~BindGroupLayoutBase() override;
static BindGroupLayoutBase* MakeError(DeviceBase* device);
struct LayoutBindingInfo { struct LayoutBindingInfo {
std::array<dawn::ShaderStageBit, kMaxBindingsPerGroup> visibilities; std::array<dawn::ShaderStageBit, kMaxBindingsPerGroup> visibilities;
std::array<dawn::BindingType, kMaxBindingsPerGroup> types; std::array<dawn::BindingType, kMaxBindingsPerGroup> types;
@ -45,6 +47,8 @@ namespace dawn_native {
const LayoutBindingInfo& GetBindingInfo() const; const LayoutBindingInfo& GetBindingInfo() const;
private: private:
BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag);
LayoutBindingInfo mBindingInfo; LayoutBindingInfo mBindingInfo;
bool mIsBlueprint = false; bool mIsBlueprint = false;
}; };

View File

@ -23,6 +23,33 @@
namespace dawn_native { namespace dawn_native {
namespace {
class ErrorBuffer : public BufferBase {
public:
ErrorBuffer(DeviceBase* device) : BufferBase(device, ObjectBase::kError) {
}
private:
MaybeError SetSubDataImpl(uint32_t start,
uint32_t count,
const uint8_t* data) override {
UNREACHABLE();
return {};
}
void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t size) override {
UNREACHABLE();
}
void MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t size) override {
UNREACHABLE();
}
void UnmapImpl() override {
UNREACHABLE();
}
};
} // anonymous namespace
MaybeError ValidateBufferDescriptor(DeviceBase*, const BufferDescriptor* descriptor) { MaybeError ValidateBufferDescriptor(DeviceBase*, const BufferDescriptor* descriptor) {
if (descriptor->nextInChain != nullptr) { if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
@ -53,22 +80,35 @@ namespace dawn_native {
: ObjectBase(device), mSize(descriptor->size), mUsage(descriptor->usage) { : ObjectBase(device), mSize(descriptor->size), mUsage(descriptor->usage) {
} }
BufferBase::BufferBase(DeviceBase* device, ObjectBase::ErrorTag tag) : ObjectBase(device, tag) {
}
BufferBase::~BufferBase() { BufferBase::~BufferBase() {
if (mIsMapped) { if (mIsMapped) {
ASSERT(!IsError());
CallMapReadCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr); CallMapReadCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
CallMapWriteCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr); CallMapWriteCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
} }
} }
// static
BufferBase* BufferBase::MakeError(DeviceBase* device) {
return new ErrorBuffer(device);
}
uint32_t BufferBase::GetSize() const { uint32_t BufferBase::GetSize() const {
ASSERT(!IsError());
return mSize; return mSize;
} }
dawn::BufferUsageBit BufferBase::GetUsage() const { dawn::BufferUsageBit BufferBase::GetUsage() const {
ASSERT(!IsError());
return mUsage; return mUsage;
} }
MaybeError BufferBase::ValidateCanUseInSubmitNow() const { MaybeError BufferBase::ValidateCanUseInSubmitNow() const {
ASSERT(!IsError());
if (mIsMapped) { if (mIsMapped) {
return DAWN_VALIDATION_ERROR("Buffer used in a submit while mapped"); return DAWN_VALIDATION_ERROR("Buffer used in a submit while mapped");
} }
@ -78,6 +118,8 @@ namespace dawn_native {
void BufferBase::CallMapReadCallback(uint32_t serial, void BufferBase::CallMapReadCallback(uint32_t serial,
dawnBufferMapAsyncStatus status, dawnBufferMapAsyncStatus status,
const void* pointer) { const void* pointer) {
ASSERT(!IsError());
if (mMapReadCallback != nullptr && serial == mMapSerial) { if (mMapReadCallback != nullptr && serial == mMapSerial) {
ASSERT(mMapWriteCallback == nullptr); ASSERT(mMapWriteCallback == nullptr);
// Tag the callback as fired before firing it, otherwise it could fire a second time if // Tag the callback as fired before firing it, otherwise it could fire a second time if
@ -91,6 +133,8 @@ namespace dawn_native {
void BufferBase::CallMapWriteCallback(uint32_t serial, void BufferBase::CallMapWriteCallback(uint32_t serial,
dawnBufferMapAsyncStatus status, dawnBufferMapAsyncStatus status,
void* pointer) { void* pointer) {
ASSERT(!IsError());
if (mMapWriteCallback != nullptr && serial == mMapSerial) { if (mMapWriteCallback != nullptr && serial == mMapSerial) {
ASSERT(mMapReadCallback == nullptr); ASSERT(mMapReadCallback == nullptr);
// Tag the callback as fired before firing it, otherwise it could fire a second time if // Tag the callback as fired before firing it, otherwise it could fire a second time if
@ -105,6 +149,7 @@ namespace dawn_native {
if (GetDevice()->ConsumedError(ValidateSetSubData(start, count))) { if (GetDevice()->ConsumedError(ValidateSetSubData(start, count))) {
return; return;
} }
ASSERT(!IsError());
if (GetDevice()->ConsumedError(SetSubDataImpl(start, count, data))) { if (GetDevice()->ConsumedError(SetSubDataImpl(start, count, data))) {
return; return;
@ -119,6 +164,7 @@ namespace dawn_native {
callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata); callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata);
return; return;
} }
ASSERT(!IsError());
ASSERT(mMapWriteCallback == nullptr); ASSERT(mMapWriteCallback == nullptr);
@ -139,6 +185,7 @@ namespace dawn_native {
callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata); callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata);
return; return;
} }
ASSERT(!IsError());
ASSERT(mMapReadCallback == nullptr); ASSERT(mMapReadCallback == nullptr);
@ -155,6 +202,7 @@ namespace dawn_native {
if (GetDevice()->ConsumedError(ValidateUnmap())) { if (GetDevice()->ConsumedError(ValidateUnmap())) {
return; return;
} }
ASSERT(!IsError());
// A map request can only be called once, so this will fire only if the request wasn't // A map request can only be called once, so this will fire only if the request wasn't
// completed before the Unmap // completed before the Unmap
@ -168,6 +216,8 @@ namespace dawn_native {
} }
MaybeError BufferBase::ValidateSetSubData(uint32_t start, uint32_t count) const { MaybeError BufferBase::ValidateSetSubData(uint32_t start, uint32_t count) const {
DAWN_TRY(GetDevice()->ValidateObject(this));
if (count > GetSize()) { if (count > GetSize()) {
return DAWN_VALIDATION_ERROR("Buffer subdata with too much data"); return DAWN_VALIDATION_ERROR("Buffer subdata with too much data");
} }
@ -187,6 +237,8 @@ namespace dawn_native {
MaybeError BufferBase::ValidateMap(uint32_t start, MaybeError BufferBase::ValidateMap(uint32_t start,
uint32_t size, uint32_t size,
dawn::BufferUsageBit requiredUsage) const { dawn::BufferUsageBit requiredUsage) const {
DAWN_TRY(GetDevice()->ValidateObject(this));
if (size > GetSize()) { if (size > GetSize()) {
return DAWN_VALIDATION_ERROR("Buffer mapping with too big a region"); return DAWN_VALIDATION_ERROR("Buffer mapping with too big a region");
} }
@ -208,6 +260,8 @@ namespace dawn_native {
} }
MaybeError BufferBase::ValidateUnmap() const { MaybeError BufferBase::ValidateUnmap() const {
DAWN_TRY(GetDevice()->ValidateObject(this));
if (!mIsMapped) { if (!mIsMapped) {
return DAWN_VALIDATION_ERROR("Buffer wasn't mapped"); return DAWN_VALIDATION_ERROR("Buffer wasn't mapped");
} }

View File

@ -39,6 +39,8 @@ namespace dawn_native {
BufferBase(DeviceBase* device, const BufferDescriptor* descriptor); BufferBase(DeviceBase* device, const BufferDescriptor* descriptor);
~BufferBase(); ~BufferBase();
static BufferBase* MakeError(DeviceBase* device);
uint32_t GetSize() const; uint32_t GetSize() const;
dawn::BufferUsageBit GetUsage() const; dawn::BufferUsageBit GetUsage() const;
@ -57,6 +59,8 @@ namespace dawn_native {
void Unmap(); void Unmap();
protected: protected:
BufferBase(DeviceBase* device, ObjectBase::ErrorTag tag);
void CallMapReadCallback(uint32_t serial, void CallMapReadCallback(uint32_t serial,
dawnBufferMapAsyncStatus status, dawnBufferMapAsyncStatus status,
const void* pointer); const void* pointer);
@ -74,7 +78,7 @@ namespace dawn_native {
dawn::BufferUsageBit requiredUsage) const; dawn::BufferUsageBit requiredUsage) const;
MaybeError ValidateUnmap() const; MaybeError ValidateUnmap() const;
uint32_t mSize; uint32_t mSize = 0;
dawn::BufferUsageBit mUsage = dawn::BufferUsageBit::None; dawn::BufferUsageBit mUsage = dawn::BufferUsageBit::None;
dawnBufferMapReadCallback mMapReadCallback = nullptr; dawnBufferMapReadCallback mMapReadCallback = nullptr;

View File

@ -387,6 +387,8 @@ namespace dawn_native {
case Command::CopyBufferToBuffer: { case Command::CopyBufferToBuffer: {
CopyBufferToBufferCmd* copy = mIterator.NextCommand<CopyBufferToBufferCmd>(); CopyBufferToBufferCmd* copy = mIterator.NextCommand<CopyBufferToBufferCmd>();
DAWN_TRY(GetDevice()->ValidateObject(copy->source.buffer.Get()));
DAWN_TRY(GetDevice()->ValidateObject(copy->destination.buffer.Get()));
DAWN_TRY(ValidateCopySizeFitsInBuffer(copy->source, copy->size)); DAWN_TRY(ValidateCopySizeFitsInBuffer(copy->source, copy->size));
DAWN_TRY(ValidateCopySizeFitsInBuffer(copy->destination, copy->size)); DAWN_TRY(ValidateCopySizeFitsInBuffer(copy->destination, copy->size));
@ -402,6 +404,9 @@ namespace dawn_native {
case Command::CopyBufferToTexture: { case Command::CopyBufferToTexture: {
CopyBufferToTextureCmd* copy = mIterator.NextCommand<CopyBufferToTextureCmd>(); CopyBufferToTextureCmd* copy = mIterator.NextCommand<CopyBufferToTextureCmd>();
DAWN_TRY(GetDevice()->ValidateObject(copy->source.buffer.Get()));
DAWN_TRY(GetDevice()->ValidateObject(copy->destination.texture.Get()));
uint32_t bufferCopySize = 0; uint32_t bufferCopySize = 0;
DAWN_TRY(ValidateRowPitch(copy->destination.texture->GetFormat(), DAWN_TRY(ValidateRowPitch(copy->destination.texture->GetFormat(),
copy->copySize, copy->source.rowPitch)); copy->copySize, copy->source.rowPitch));
@ -427,6 +432,9 @@ namespace dawn_native {
case Command::CopyTextureToBuffer: { case Command::CopyTextureToBuffer: {
CopyTextureToBufferCmd* copy = mIterator.NextCommand<CopyTextureToBufferCmd>(); CopyTextureToBufferCmd* copy = mIterator.NextCommand<CopyTextureToBufferCmd>();
DAWN_TRY(GetDevice()->ValidateObject(copy->source.texture.Get()));
DAWN_TRY(GetDevice()->ValidateObject(copy->destination.buffer.Get()));
uint32_t bufferCopySize = 0; uint32_t bufferCopySize = 0;
DAWN_TRY(ValidateRowPitch(copy->source.texture->GetFormat(), copy->copySize, DAWN_TRY(ValidateRowPitch(copy->source.texture->GetFormat(), copy->copySize,
copy->destination.rowPitch)); copy->destination.rowPitch));

View File

@ -17,6 +17,7 @@
#include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandBuffer.h"
#include "dawn_native/Commands.h" #include "dawn_native/Commands.h"
#include "dawn_native/ComputePipeline.h" #include "dawn_native/ComputePipeline.h"
#include "dawn_native/Device.h"
namespace dawn_native { namespace dawn_native {
@ -39,12 +40,8 @@ namespace dawn_native {
} }
void ComputePassEncoderBase::SetPipeline(ComputePipelineBase* pipeline) { void ComputePassEncoderBase::SetPipeline(ComputePipelineBase* pipeline) {
if (mTopLevelBuilder->ConsumedError(ValidateCanRecordCommands())) { if (mTopLevelBuilder->ConsumedError(ValidateCanRecordCommands()) ||
return; mTopLevelBuilder->ConsumedError(GetDevice()->ValidateObject(pipeline))) {
}
if (pipeline == nullptr) {
mTopLevelBuilder->HandleError("Pipeline cannot be null");
return; return;
} }

View File

@ -18,19 +18,14 @@
namespace dawn_native { namespace dawn_native {
MaybeError ValidateComputePipelineDescriptor(DeviceBase*, MaybeError ValidateComputePipelineDescriptor(DeviceBase* device,
const ComputePipelineDescriptor* descriptor) { const ComputePipelineDescriptor* descriptor) {
if (descriptor->nextInChain != nullptr) { if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
} }
if (descriptor->module == nullptr) { DAWN_TRY(device->ValidateObject(descriptor->module));
return DAWN_VALIDATION_ERROR("module cannot be null"); DAWN_TRY(device->ValidateObject(descriptor->layout));
}
if (descriptor->layout == nullptr) {
return DAWN_VALIDATION_ERROR("layout cannot be null");
}
if (descriptor->entryPoint != std::string("main")) { if (descriptor->entryPoint != std::string("main")) {
return DAWN_VALIDATION_ERROR("Currently the entry point has to be main()"); return DAWN_VALIDATION_ERROR("Currently the entry point has to be main()");
@ -55,4 +50,13 @@ namespace dawn_native {
ExtractModuleData(dawn::ShaderStage::Compute, descriptor->module); ExtractModuleData(dawn::ShaderStage::Compute, descriptor->module);
} }
ComputePipelineBase::ComputePipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: PipelineBase(device, tag) {
}
// static
ComputePipelineBase* ComputePipelineBase::MakeError(DeviceBase* device) {
return new ComputePipelineBase(device, ObjectBase::kError);
}
} // namespace dawn_native } // namespace dawn_native

View File

@ -27,6 +27,11 @@ namespace dawn_native {
class ComputePipelineBase : public PipelineBase { class ComputePipelineBase : public PipelineBase {
public: public:
ComputePipelineBase(DeviceBase* device, const ComputePipelineDescriptor* descriptor); ComputePipelineBase(DeviceBase* device, const ComputePipelineDescriptor* descriptor);
static ComputePipelineBase* MakeError(DeviceBase* device);
private:
ComputePipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag);
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -70,6 +70,16 @@ namespace dawn_native {
mErrorUserdata = userdata; mErrorUserdata = userdata;
} }
MaybeError DeviceBase::ValidateObject(const ObjectBase* object) const {
if (DAWN_UNLIKELY(object->GetDevice() != this)) {
return DAWN_VALIDATION_ERROR("Object from a different device.");
}
if (DAWN_UNLIKELY(object->IsError())) {
return DAWN_VALIDATION_ERROR("Object is an error.");
}
return {};
}
AdapterBase* DeviceBase::GetAdapter() const { AdapterBase* DeviceBase::GetAdapter() const {
return mAdapter; return mAdapter;
} }
@ -108,7 +118,7 @@ namespace dawn_native {
BindGroupBase* result = nullptr; BindGroupBase* result = nullptr;
if (ConsumedError(CreateBindGroupInternal(&result, descriptor))) { if (ConsumedError(CreateBindGroupInternal(&result, descriptor))) {
return nullptr; return BindGroupBase::MakeError(this);
} }
return result; return result;
@ -118,7 +128,7 @@ namespace dawn_native {
BindGroupLayoutBase* result = nullptr; BindGroupLayoutBase* result = nullptr;
if (ConsumedError(CreateBindGroupLayoutInternal(&result, descriptor))) { if (ConsumedError(CreateBindGroupLayoutInternal(&result, descriptor))) {
return nullptr; return BindGroupLayoutBase::MakeError(this);
} }
return result; return result;
@ -127,7 +137,7 @@ namespace dawn_native {
BufferBase* result = nullptr; BufferBase* result = nullptr;
if (ConsumedError(CreateBufferInternal(&result, descriptor))) { if (ConsumedError(CreateBufferInternal(&result, descriptor))) {
return nullptr; return BufferBase::MakeError(this);
} }
return result; return result;
@ -140,7 +150,7 @@ namespace dawn_native {
ComputePipelineBase* result = nullptr; ComputePipelineBase* result = nullptr;
if (ConsumedError(CreateComputePipelineInternal(&result, descriptor))) { if (ConsumedError(CreateComputePipelineInternal(&result, descriptor))) {
return nullptr; return ComputePipelineBase::MakeError(this);
} }
return result; return result;
@ -149,7 +159,7 @@ namespace dawn_native {
FenceBase* result = nullptr; FenceBase* result = nullptr;
if (ConsumedError(CreateFenceInternal(&result, descriptor))) { if (ConsumedError(CreateFenceInternal(&result, descriptor))) {
return nullptr; return FenceBase::MakeError(this);
} }
return result; return result;
@ -162,7 +172,7 @@ namespace dawn_native {
PipelineLayoutBase* result = nullptr; PipelineLayoutBase* result = nullptr;
if (ConsumedError(CreatePipelineLayoutInternal(&result, descriptor))) { if (ConsumedError(CreatePipelineLayoutInternal(&result, descriptor))) {
return nullptr; return PipelineLayoutBase::MakeError(this);
} }
return result; return result;
@ -171,6 +181,9 @@ namespace dawn_native {
QueueBase* result = nullptr; QueueBase* result = nullptr;
if (ConsumedError(CreateQueueInternal(&result))) { if (ConsumedError(CreateQueueInternal(&result))) {
// If queue creation failure ever becomes possible, we should implement MakeError and
// friends for them.
UNREACHABLE();
return nullptr; return nullptr;
} }
@ -183,7 +196,7 @@ namespace dawn_native {
SamplerBase* result = nullptr; SamplerBase* result = nullptr;
if (ConsumedError(CreateSamplerInternal(&result, descriptor))) { if (ConsumedError(CreateSamplerInternal(&result, descriptor))) {
return nullptr; return SamplerBase::MakeError(this);
} }
return result; return result;
@ -193,7 +206,7 @@ namespace dawn_native {
RenderPipelineBase* result = nullptr; RenderPipelineBase* result = nullptr;
if (ConsumedError(CreateRenderPipelineInternal(&result, descriptor))) { if (ConsumedError(CreateRenderPipelineInternal(&result, descriptor))) {
return nullptr; return RenderPipelineBase::MakeError(this);
} }
return result; return result;
@ -202,7 +215,7 @@ namespace dawn_native {
ShaderModuleBase* result = nullptr; ShaderModuleBase* result = nullptr;
if (ConsumedError(CreateShaderModuleInternal(&result, descriptor))) { if (ConsumedError(CreateShaderModuleInternal(&result, descriptor))) {
return nullptr; return ShaderModuleBase::MakeError(this);
} }
return result; return result;
@ -214,8 +227,9 @@ namespace dawn_native {
TextureBase* result = nullptr; TextureBase* result = nullptr;
if (ConsumedError(CreateTextureInternal(&result, descriptor))) { if (ConsumedError(CreateTextureInternal(&result, descriptor))) {
return nullptr; return TextureBase::MakeError(this);
} }
return result; return result;
} }
TextureViewBase* DeviceBase::CreateTextureView(TextureBase* texture, TextureViewBase* DeviceBase::CreateTextureView(TextureBase* texture,
@ -223,8 +237,9 @@ namespace dawn_native {
TextureViewBase* result = nullptr; TextureViewBase* result = nullptr;
if (ConsumedError(CreateTextureViewInternal(&result, texture, descriptor))) { if (ConsumedError(CreateTextureViewInternal(&result, texture, descriptor))) {
return nullptr; return TextureViewBase::MakeError(this);
} }
return result; return result;
} }

View File

@ -49,6 +49,8 @@ namespace dawn_native {
return false; return false;
} }
MaybeError ValidateObject(const ObjectBase* object) const;
AdapterBase* GetAdapter() const; AdapterBase* GetAdapter() const;
// Used by autogenerated code, returns itself // Used by autogenerated code, returns itself

View File

@ -43,7 +43,8 @@ namespace dawn_native {
// //
// but shorthand version for specific error types are preferred: // but shorthand version for specific error types are preferred:
// return DAWN_VALIDATION_ERROR("My error message"); // return DAWN_VALIDATION_ERROR("My error message");
#define DAWN_MAKE_ERROR(TYPE, MESSAGE) MakeError(TYPE, MESSAGE, __FILE__, __func__, __LINE__) #define DAWN_MAKE_ERROR(TYPE, MESSAGE) \
::dawn_native::MakeError(TYPE, MESSAGE, __FILE__, __func__, __LINE__)
#define DAWN_VALIDATION_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::Validation, MESSAGE) #define DAWN_VALIDATION_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::Validation, MESSAGE)
#define DAWN_CONTEXT_LOST_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::ContextLost, MESSAGE) #define DAWN_CONTEXT_LOST_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::ContextLost, MESSAGE)
#define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::Unimplemented, MESSAGE) #define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::Unimplemented, MESSAGE)
@ -55,31 +56,31 @@ namespace dawn_native {
// When Errors aren't handled explicitly, calls to functions returning errors should be // When Errors aren't handled explicitly, calls to functions returning errors should be
// wrapped in an DAWN_TRY. It will return the error if any, otherwise keep executing // wrapped in an DAWN_TRY. It will return the error if any, otherwise keep executing
// the current function. // the current function.
#define DAWN_TRY(EXPR) \ #define DAWN_TRY(EXPR) \
{ \ { \
auto DAWN_LOCAL_VAR = EXPR; \ auto DAWN_LOCAL_VAR = EXPR; \
if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \ if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \
ErrorData* error = DAWN_LOCAL_VAR.AcquireError(); \ ErrorData* error = DAWN_LOCAL_VAR.AcquireError(); \
AppendBacktrace(error, __FILE__, __func__, __LINE__); \ ::dawn_native::AppendBacktrace(error, __FILE__, __func__, __LINE__); \
return {std::move(error)}; \ return {std::move(error)}; \
} \ } \
} \ } \
for (;;) \ for (;;) \
break break
// DAWN_TRY_ASSIGN is the same as DAWN_TRY for ResultOrError and assigns the success value, if // DAWN_TRY_ASSIGN is the same as DAWN_TRY for ResultOrError and assigns the success value, if
// any, to VAR. // any, to VAR.
#define DAWN_TRY_ASSIGN(VAR, EXPR) \ #define DAWN_TRY_ASSIGN(VAR, EXPR) \
{ \ { \
auto DAWN_LOCAL_VAR = EXPR; \ auto DAWN_LOCAL_VAR = EXPR; \
if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \ if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \
ErrorData* error = DAWN_LOCAL_VAR.AcquireError(); \ ErrorData* error = DAWN_LOCAL_VAR.AcquireError(); \
AppendBacktrace(error, __FILE__, __func__, __LINE__); \ ::dawn_native::AppendBacktrace(error, __FILE__, __func__, __LINE__); \
return {std::move(error)}; \ return {std::move(error)}; \
} \ } \
VAR = DAWN_LOCAL_VAR.AcquireSuccess(); \ VAR = DAWN_LOCAL_VAR.AcquireSuccess(); \
} \ } \
for (;;) \ for (;;) \
break break
// Implementation detail of DAWN_TRY and DAWN_TRY_ASSIGN's adding to the Error's backtrace. // Implementation detail of DAWN_TRY and DAWN_TRY_ASSIGN's adding to the Error's backtrace.

View File

@ -38,14 +38,26 @@ namespace dawn_native {
mCompletedValue(descriptor->initialValue) { mCompletedValue(descriptor->initialValue) {
} }
FenceBase::FenceBase(DeviceBase* device, ObjectBase::ErrorTag tag) : ObjectBase(device, tag) {
}
FenceBase::~FenceBase() { FenceBase::~FenceBase() {
for (auto& request : mRequests.IterateAll()) { for (auto& request : mRequests.IterateAll()) {
ASSERT(!IsError());
request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, request.userdata); request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, request.userdata);
} }
mRequests.Clear(); mRequests.Clear();
} }
// static
FenceBase* FenceBase::MakeError(DeviceBase* device) {
return new FenceBase(device, ObjectBase::kError);
}
uint64_t FenceBase::GetCompletedValue() const { uint64_t FenceBase::GetCompletedValue() const {
if (IsError()) {
return 0;
}
return mCompletedValue; return mCompletedValue;
} }
@ -56,6 +68,7 @@ namespace dawn_native {
callback(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata); callback(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata);
return; return;
} }
ASSERT(!IsError());
if (value <= mCompletedValue) { if (value <= mCompletedValue) {
callback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata); callback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata);
@ -69,15 +82,18 @@ namespace dawn_native {
} }
uint64_t FenceBase::GetSignaledValue() const { uint64_t FenceBase::GetSignaledValue() const {
ASSERT(!IsError());
return mSignalValue; return mSignalValue;
} }
void FenceBase::SetSignaledValue(uint64_t signalValue) { void FenceBase::SetSignaledValue(uint64_t signalValue) {
ASSERT(!IsError());
ASSERT(signalValue > mSignalValue); ASSERT(signalValue > mSignalValue);
mSignalValue = signalValue; mSignalValue = signalValue;
} }
void FenceBase::SetCompletedValue(uint64_t completedValue) { void FenceBase::SetCompletedValue(uint64_t completedValue) {
ASSERT(!IsError());
ASSERT(completedValue <= mSignalValue); ASSERT(completedValue <= mSignalValue);
ASSERT(completedValue > mCompletedValue); ASSERT(completedValue > mCompletedValue);
mCompletedValue = completedValue; mCompletedValue = completedValue;
@ -89,6 +105,7 @@ namespace dawn_native {
} }
MaybeError FenceBase::ValidateOnCompletion(uint64_t value) const { MaybeError FenceBase::ValidateOnCompletion(uint64_t value) const {
DAWN_TRY(GetDevice()->ValidateObject(this));
if (value > mSignalValue) { if (value > mSignalValue) {
return DAWN_VALIDATION_ERROR("Value greater than fence signaled value"); return DAWN_VALIDATION_ERROR("Value greater than fence signaled value");
} }

View File

@ -33,12 +33,15 @@ namespace dawn_native {
FenceBase(DeviceBase* device, const FenceDescriptor* descriptor); FenceBase(DeviceBase* device, const FenceDescriptor* descriptor);
~FenceBase(); ~FenceBase();
static FenceBase* MakeError(DeviceBase* device);
uint64_t GetSignaledValue() const;
// Dawn API // Dawn API
uint64_t GetCompletedValue() const; uint64_t GetCompletedValue() const;
void OnCompletion(uint64_t value, void OnCompletion(uint64_t value,
dawn::FenceOnCompletionCallback callback, dawn::FenceOnCompletionCallback callback,
dawn::CallbackUserdata userdata); dawn::CallbackUserdata userdata);
uint64_t GetSignaledValue() const;
protected: protected:
friend class QueueBase; friend class QueueBase;
@ -47,6 +50,8 @@ namespace dawn_native {
void SetCompletedValue(uint64_t completedValue); void SetCompletedValue(uint64_t completedValue);
private: private:
FenceBase(DeviceBase* device, ObjectBase::ErrorTag tag);
MaybeError ValidateOnCompletion(uint64_t value) const; MaybeError ValidateOnCompletion(uint64_t value) const;
struct OnCompletionData { struct OnCompletionData {

View File

@ -16,7 +16,10 @@
namespace dawn_native { namespace dawn_native {
ObjectBase::ObjectBase(DeviceBase* device) : mDevice(device) { ObjectBase::ObjectBase(DeviceBase* device) : mDevice(device), mIsError(false) {
}
ObjectBase::ObjectBase(DeviceBase* device, ErrorTag) : mDevice(device), mIsError(true) {
} }
ObjectBase::~ObjectBase() { ObjectBase::~ObjectBase() {
@ -26,4 +29,8 @@ namespace dawn_native {
return mDevice; return mDevice;
} }
bool ObjectBase::IsError() const {
return mIsError;
}
} // namespace dawn_native } // namespace dawn_native

View File

@ -23,13 +23,22 @@ namespace dawn_native {
class ObjectBase : public RefCounted { class ObjectBase : public RefCounted {
public: public:
struct ErrorTag {};
static constexpr ErrorTag kError = {};
ObjectBase(DeviceBase* device); ObjectBase(DeviceBase* device);
ObjectBase(DeviceBase* device, ErrorTag tag);
virtual ~ObjectBase(); virtual ~ObjectBase();
DeviceBase* GetDevice() const; DeviceBase* GetDevice() const;
bool IsError() const;
private: private:
DeviceBase* mDevice; DeviceBase* mDevice;
// TODO(cwallez@chromium.org): This most likely adds 4 bytes to most Dawn objects, see if
// that bit can be hidden in the refcount once it is a single 64bit refcount.
// See https://bugs.chromium.org/p/dawn/issues/detail?id=105
bool mIsError;
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -26,10 +26,16 @@ namespace dawn_native {
PipelineBase::PipelineBase(DeviceBase* device, PipelineBase::PipelineBase(DeviceBase* device,
PipelineLayoutBase* layout, PipelineLayoutBase* layout,
dawn::ShaderStageBit stages) dawn::ShaderStageBit stages)
: ObjectBase(device), mStageMask(stages), mLayout(layout), mDevice(device) { : ObjectBase(device), mStageMask(stages), mLayout(layout) {
}
PipelineBase::PipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
} }
void PipelineBase::ExtractModuleData(dawn::ShaderStage stage, ShaderModuleBase* module) { void PipelineBase::ExtractModuleData(dawn::ShaderStage stage, ShaderModuleBase* module) {
ASSERT(!IsError());
PushConstantInfo* info = &mPushConstants[stage]; PushConstantInfo* info = &mPushConstants[stage];
const auto& moduleInfo = module->GetPushConstants(); const auto& moduleInfo = module->GetPushConstants();
@ -50,19 +56,18 @@ namespace dawn_native {
const PipelineBase::PushConstantInfo& PipelineBase::GetPushConstants( const PipelineBase::PushConstantInfo& PipelineBase::GetPushConstants(
dawn::ShaderStage stage) const { dawn::ShaderStage stage) const {
ASSERT(!IsError());
return mPushConstants[stage]; return mPushConstants[stage];
} }
dawn::ShaderStageBit PipelineBase::GetStageMask() const { dawn::ShaderStageBit PipelineBase::GetStageMask() const {
ASSERT(!IsError());
return mStageMask; return mStageMask;
} }
PipelineLayoutBase* PipelineBase::GetLayout() { PipelineLayoutBase* PipelineBase::GetLayout() {
ASSERT(!IsError());
return mLayout.Get(); return mLayout.Get();
} }
DeviceBase* PipelineBase::GetDevice() const {
return mDevice;
}
} // namespace dawn_native } // namespace dawn_native

View File

@ -37,26 +37,24 @@ namespace dawn_native {
class PipelineBase : public ObjectBase { class PipelineBase : public ObjectBase {
public: public:
PipelineBase(DeviceBase* device, PipelineLayoutBase* layout, dawn::ShaderStageBit stages);
struct PushConstantInfo { struct PushConstantInfo {
std::bitset<kMaxPushConstants> mask; std::bitset<kMaxPushConstants> mask;
std::array<PushConstantType, kMaxPushConstants> types; std::array<PushConstantType, kMaxPushConstants> types;
}; };
const PushConstantInfo& GetPushConstants(dawn::ShaderStage stage) const; const PushConstantInfo& GetPushConstants(dawn::ShaderStage stage) const;
dawn::ShaderStageBit GetStageMask() const; dawn::ShaderStageBit GetStageMask() const;
PipelineLayoutBase* GetLayout(); PipelineLayoutBase* GetLayout();
DeviceBase* GetDevice() const;
protected: protected:
PipelineBase(DeviceBase* device, PipelineLayoutBase* layout, dawn::ShaderStageBit stages);
PipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag);
void ExtractModuleData(dawn::ShaderStage stage, ShaderModuleBase* module); void ExtractModuleData(dawn::ShaderStage stage, ShaderModuleBase* module);
private: private:
dawn::ShaderStageBit mStageMask; dawn::ShaderStageBit mStageMask;
Ref<PipelineLayoutBase> mLayout; Ref<PipelineLayoutBase> mLayout;
PerStage<PushConstantInfo> mPushConstants; PerStage<PushConstantInfo> mPushConstants;
DeviceBase* mDevice;
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -20,7 +20,7 @@
namespace dawn_native { namespace dawn_native {
MaybeError ValidatePipelineLayoutDescriptor(DeviceBase*, MaybeError ValidatePipelineLayoutDescriptor(DeviceBase* device,
const PipelineLayoutDescriptor* descriptor) { const PipelineLayoutDescriptor* descriptor) {
if (descriptor->nextInChain != nullptr) { if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
@ -31,9 +31,7 @@ namespace dawn_native {
} }
for (uint32_t i = 0; i < descriptor->numBindGroupLayouts; ++i) { for (uint32_t i = 0; i < descriptor->numBindGroupLayouts; ++i) {
if (descriptor->bindGroupLayouts[i] == nullptr) { DAWN_TRY(device->ValidateObject(descriptor->bindGroupLayouts[i]));
return DAWN_VALIDATION_ERROR("bind group layouts may not be null");
}
} }
return {}; return {};
} }
@ -50,22 +48,36 @@ namespace dawn_native {
} }
} }
PipelineLayoutBase::PipelineLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
}
// static
PipelineLayoutBase* PipelineLayoutBase::MakeError(DeviceBase* device) {
return new PipelineLayoutBase(device, ObjectBase::kError);
}
const BindGroupLayoutBase* PipelineLayoutBase::GetBindGroupLayout(size_t group) const { const BindGroupLayoutBase* PipelineLayoutBase::GetBindGroupLayout(size_t group) const {
ASSERT(!IsError());
ASSERT(group < kMaxBindGroups); ASSERT(group < kMaxBindGroups);
ASSERT(mMask[group]); ASSERT(mMask[group]);
return mBindGroupLayouts[group].Get(); return mBindGroupLayouts[group].Get();
} }
const std::bitset<kMaxBindGroups> PipelineLayoutBase::GetBindGroupLayoutsMask() const { const std::bitset<kMaxBindGroups> PipelineLayoutBase::GetBindGroupLayoutsMask() const {
ASSERT(!IsError());
return mMask; return mMask;
} }
std::bitset<kMaxBindGroups> PipelineLayoutBase::InheritedGroupsMask( std::bitset<kMaxBindGroups> PipelineLayoutBase::InheritedGroupsMask(
const PipelineLayoutBase* other) const { const PipelineLayoutBase* other) const {
ASSERT(!IsError());
return {(1 << GroupsInheritUpTo(other)) - 1u}; return {(1 << GroupsInheritUpTo(other)) - 1u};
} }
uint32_t PipelineLayoutBase::GroupsInheritUpTo(const PipelineLayoutBase* other) const { uint32_t PipelineLayoutBase::GroupsInheritUpTo(const PipelineLayoutBase* other) const {
ASSERT(!IsError());
for (uint32_t i = 0; i < kMaxBindGroups; ++i) { for (uint32_t i = 0; i < kMaxBindGroups; ++i) {
if (!mMask[i] || mBindGroupLayouts[i].Get() != other->mBindGroupLayouts[i].Get()) { if (!mMask[i] || mBindGroupLayouts[i].Get() != other->mBindGroupLayouts[i].Get()) {
return i; return i;

View File

@ -36,6 +36,8 @@ namespace dawn_native {
public: public:
PipelineLayoutBase(DeviceBase* device, const PipelineLayoutDescriptor* descriptor); PipelineLayoutBase(DeviceBase* device, const PipelineLayoutDescriptor* descriptor);
static PipelineLayoutBase* MakeError(DeviceBase* device);
const BindGroupLayoutBase* GetBindGroupLayout(size_t group) const; const BindGroupLayoutBase* GetBindGroupLayout(size_t group) const;
const std::bitset<kMaxBindGroups> GetBindGroupLayoutsMask() const; const std::bitset<kMaxBindGroups> GetBindGroupLayoutsMask() const;
@ -48,6 +50,8 @@ namespace dawn_native {
uint32_t GroupsInheritUpTo(const PipelineLayoutBase* other) const; uint32_t GroupsInheritUpTo(const PipelineLayoutBase* other) const;
protected: protected:
PipelineLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag);
BindGroupLayoutArray mBindGroupLayouts; BindGroupLayoutArray mBindGroupLayouts;
std::bitset<kMaxBindGroups> mMask; std::bitset<kMaxBindGroups> mMask;
}; };

View File

@ -17,6 +17,7 @@
#include "dawn_native/BindGroup.h" #include "dawn_native/BindGroup.h"
#include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandBuffer.h"
#include "dawn_native/Commands.h" #include "dawn_native/Commands.h"
#include "dawn_native/Device.h"
#include <string.h> #include <string.h>
@ -38,12 +39,8 @@ namespace dawn_native {
} }
void ProgrammablePassEncoder::SetBindGroup(uint32_t groupIndex, BindGroupBase* group) { void ProgrammablePassEncoder::SetBindGroup(uint32_t groupIndex, BindGroupBase* group) {
if (mTopLevelBuilder->ConsumedError(ValidateCanRecordCommands())) { if (mTopLevelBuilder->ConsumedError(ValidateCanRecordCommands()) ||
return; mTopLevelBuilder->ConsumedError(GetDevice()->ValidateObject(group))) {
}
if (group == nullptr) {
mTopLevelBuilder->HandleError("BindGroup cannot be null");
return; return;
} }

View File

@ -32,6 +32,7 @@ namespace dawn_native {
if (GetDevice()->ConsumedError(ValidateSubmit(numCommands, commands))) { if (GetDevice()->ConsumedError(ValidateSubmit(numCommands, commands))) {
return; return;
} }
ASSERT(!IsError());
SubmitImpl(numCommands, commands); SubmitImpl(numCommands, commands);
} }
@ -40,13 +41,20 @@ namespace dawn_native {
if (GetDevice()->ConsumedError(ValidateSignal(fence, signalValue))) { if (GetDevice()->ConsumedError(ValidateSignal(fence, signalValue))) {
return; return;
} }
ASSERT(!IsError());
fence->SetSignaledValue(signalValue); fence->SetSignaledValue(signalValue);
GetDevice()->GetFenceSignalTracker()->UpdateFenceOnComplete(fence, signalValue); GetDevice()->GetFenceSignalTracker()->UpdateFenceOnComplete(fence, signalValue);
} }
MaybeError QueueBase::ValidateSubmit(uint32_t numCommands, CommandBufferBase* const* commands) { MaybeError QueueBase::ValidateSubmit(uint32_t numCommands, CommandBufferBase* const* commands) {
DAWN_TRY(GetDevice()->ValidateObject(this));
for (uint32_t i = 0; i < numCommands; ++i) { for (uint32_t i = 0; i < numCommands; ++i) {
DAWN_TRY(GetDevice()->ValidateObject(commands[i]));
// TODO(cwallez@chromium.org): Remove this once CommandBufferBuilder doesn't use the
// builder mechanism anymore.
if (commands[i] == nullptr) { if (commands[i] == nullptr) {
return DAWN_VALIDATION_ERROR("Command buffers cannot be null"); return DAWN_VALIDATION_ERROR("Command buffers cannot be null");
} }
@ -74,9 +82,8 @@ namespace dawn_native {
} }
MaybeError QueueBase::ValidateSignal(const FenceBase* fence, uint64_t signalValue) { MaybeError QueueBase::ValidateSignal(const FenceBase* fence, uint64_t signalValue) {
if (fence == nullptr) { DAWN_TRY(GetDevice()->ValidateObject(this));
return DAWN_VALIDATION_ERROR("Fence cannot be null"); DAWN_TRY(GetDevice()->ValidateObject(fence));
}
if (signalValue <= fence->GetSignaledValue()) { if (signalValue <= fence->GetSignaledValue()) {
return DAWN_VALIDATION_ERROR("Signal value less than or equal to fence signaled value"); return DAWN_VALIDATION_ERROR("Signal value less than or equal to fence signaled value");

View File

@ -164,6 +164,10 @@ namespace dawn_native {
continue; continue;
} }
// TODO(cwallez@chromium.org): Once RenderPassDescriptor doesn't use a builder, check
// that the textureView is a valid object.
// See https://bugs.chromium.org/p/dawn/issues/detail?id=6
if (!IsColorRenderableTextureFormat(textureView->GetFormat())) { if (!IsColorRenderableTextureFormat(textureView->GetFormat())) {
HandleError( HandleError(
"The format of the texture view used as color attachment is not color " "The format of the texture view used as color attachment is not color "
@ -190,6 +194,10 @@ namespace dawn_native {
void RenderPassDescriptorBuilder::SetDepthStencilAttachment( void RenderPassDescriptorBuilder::SetDepthStencilAttachment(
const RenderPassDepthStencilAttachmentDescriptor* attachment) { const RenderPassDepthStencilAttachmentDescriptor* attachment) {
TextureViewBase* textureView = attachment->attachment; TextureViewBase* textureView = attachment->attachment;
// TODO(cwallez@chromium.org): Once RenderPassDescriptor doesn't use a builder, check that
// the textureView is a valid object.
// See https://bugs.chromium.org/p/dawn/issues/detail?id=6
if (textureView == nullptr) { if (textureView == nullptr) {
HandleError("Texture view cannot be nullptr"); HandleError("Texture view cannot be nullptr");
return; return;

View File

@ -17,6 +17,7 @@
#include "dawn_native/Buffer.h" #include "dawn_native/Buffer.h"
#include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandBuffer.h"
#include "dawn_native/Commands.h" #include "dawn_native/Commands.h"
#include "dawn_native/Device.h"
#include "dawn_native/RenderPipeline.h" #include "dawn_native/RenderPipeline.h"
#include <string.h> #include <string.h>
@ -64,12 +65,8 @@ namespace dawn_native {
} }
void RenderPassEncoderBase::SetPipeline(RenderPipelineBase* pipeline) { void RenderPassEncoderBase::SetPipeline(RenderPipelineBase* pipeline) {
if (mTopLevelBuilder->ConsumedError(ValidateCanRecordCommands())) { if (mTopLevelBuilder->ConsumedError(ValidateCanRecordCommands()) ||
return; mTopLevelBuilder->ConsumedError(GetDevice()->ValidateObject(pipeline))) {
}
if (pipeline == nullptr) {
mTopLevelBuilder->HandleError("Pipeline cannot be null");
return; return;
} }
@ -117,12 +114,8 @@ namespace dawn_native {
} }
void RenderPassEncoderBase::SetIndexBuffer(BufferBase* buffer, uint32_t offset) { void RenderPassEncoderBase::SetIndexBuffer(BufferBase* buffer, uint32_t offset) {
if (mTopLevelBuilder->ConsumedError(ValidateCanRecordCommands())) { if (mTopLevelBuilder->ConsumedError(ValidateCanRecordCommands()) ||
return; mTopLevelBuilder->ConsumedError(GetDevice()->ValidateObject(buffer))) {
}
if (buffer == nullptr) {
mTopLevelBuilder->HandleError("Buffer cannot be null");
return; return;
} }
@ -141,8 +134,7 @@ namespace dawn_native {
} }
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
if (buffers[i] == nullptr) { if (mTopLevelBuilder->ConsumedError(GetDevice()->ValidateObject(buffers[i]))) {
mTopLevelBuilder->HandleError("Buffers cannot be null");
return; return;
} }
} }

View File

@ -25,9 +25,12 @@ namespace dawn_native {
// Helper functions // Helper functions
namespace { namespace {
MaybeError ValidatePipelineStageDescriptor(const PipelineStageDescriptor* descriptor, MaybeError ValidatePipelineStageDescriptor(DeviceBase* device,
const PipelineStageDescriptor* descriptor,
const PipelineLayoutBase* layout, const PipelineLayoutBase* layout,
dawn::ShaderStage stage) { dawn::ShaderStage stage) {
DAWN_TRY(device->ValidateObject(descriptor->module));
if (descriptor->entryPoint != std::string("main")) { if (descriptor->entryPoint != std::string("main")) {
return DAWN_VALIDATION_ERROR("Entry point must be \"main\""); return DAWN_VALIDATION_ERROR("Entry point must be \"main\"");
} }
@ -110,28 +113,22 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
} }
if (descriptor->layout == nullptr) { DAWN_TRY(device->ValidateObject(descriptor->layout));
return DAWN_VALIDATION_ERROR("Layout must not be null");
}
if (descriptor->inputState == nullptr) { if (descriptor->inputState == nullptr) {
return DAWN_VALIDATION_ERROR("Input state must not be null"); return DAWN_VALIDATION_ERROR("Input state must not be null");
} }
if (descriptor->depthStencilState == nullptr) {
return DAWN_VALIDATION_ERROR("Depth stencil state must not be null");
}
for (uint32_t i = 0; i < descriptor->numBlendStates; ++i) { for (uint32_t i = 0; i < descriptor->numBlendStates; ++i) {
DAWN_TRY(ValidateBlendStateDescriptor(&descriptor->blendStates[i])); DAWN_TRY(ValidateBlendStateDescriptor(&descriptor->blendStates[i]));
} }
DAWN_TRY(ValidateIndexFormat(descriptor->indexFormat)); DAWN_TRY(ValidateIndexFormat(descriptor->indexFormat));
DAWN_TRY(ValidatePrimitiveTopology(descriptor->primitiveTopology)); DAWN_TRY(ValidatePrimitiveTopology(descriptor->primitiveTopology));
DAWN_TRY(ValidatePipelineStageDescriptor(descriptor->vertexStage, descriptor->layout, DAWN_TRY(ValidatePipelineStageDescriptor(device, descriptor->vertexStage,
dawn::ShaderStage::Vertex)); descriptor->layout, dawn::ShaderStage::Vertex));
DAWN_TRY(ValidatePipelineStageDescriptor(descriptor->fragmentStage, descriptor->layout, DAWN_TRY(ValidatePipelineStageDescriptor(device, descriptor->fragmentStage,
dawn::ShaderStage::Fragment)); descriptor->layout, dawn::ShaderStage::Fragment));
DAWN_TRY(ValidateAttachmentsStateDescriptor(descriptor->attachmentsState)); DAWN_TRY(ValidateAttachmentsStateDescriptor(descriptor->attachmentsState));
if ((descriptor->vertexStage->module->GetUsedVertexAttributes() & if ((descriptor->vertexStage->module->GetUsedVertexAttributes() &
@ -206,46 +203,65 @@ namespace dawn_native {
// attachment are set? // attachment are set?
} }
RenderPipelineBase::RenderPipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: PipelineBase(device, tag) {
}
// static
RenderPipelineBase* RenderPipelineBase::MakeError(DeviceBase* device) {
return new RenderPipelineBase(device, ObjectBase::kError);
}
const BlendStateDescriptor* RenderPipelineBase::GetBlendStateDescriptor( const BlendStateDescriptor* RenderPipelineBase::GetBlendStateDescriptor(
uint32_t attachmentSlot) { uint32_t attachmentSlot) {
ASSERT(!IsError());
ASSERT(attachmentSlot < mBlendStates.size()); ASSERT(attachmentSlot < mBlendStates.size());
return &mBlendStates[attachmentSlot]; return &mBlendStates[attachmentSlot];
} }
const DepthStencilStateDescriptor* RenderPipelineBase::GetDepthStencilStateDescriptor() { const DepthStencilStateDescriptor* RenderPipelineBase::GetDepthStencilStateDescriptor() {
ASSERT(!IsError());
return &mDepthStencilState; return &mDepthStencilState;
} }
dawn::IndexFormat RenderPipelineBase::GetIndexFormat() const { dawn::IndexFormat RenderPipelineBase::GetIndexFormat() const {
ASSERT(!IsError());
return mIndexFormat; return mIndexFormat;
} }
InputStateBase* RenderPipelineBase::GetInputState() { InputStateBase* RenderPipelineBase::GetInputState() {
ASSERT(!IsError());
return mInputState.Get(); return mInputState.Get();
} }
dawn::PrimitiveTopology RenderPipelineBase::GetPrimitiveTopology() const { dawn::PrimitiveTopology RenderPipelineBase::GetPrimitiveTopology() const {
ASSERT(!IsError());
return mPrimitiveTopology; return mPrimitiveTopology;
} }
std::bitset<kMaxColorAttachments> RenderPipelineBase::GetColorAttachmentsMask() const { std::bitset<kMaxColorAttachments> RenderPipelineBase::GetColorAttachmentsMask() const {
ASSERT(!IsError());
return mColorAttachmentsSet; return mColorAttachmentsSet;
} }
bool RenderPipelineBase::HasDepthStencilAttachment() const { bool RenderPipelineBase::HasDepthStencilAttachment() const {
ASSERT(!IsError());
return mHasDepthStencilAttachment; return mHasDepthStencilAttachment;
} }
dawn::TextureFormat RenderPipelineBase::GetColorAttachmentFormat(uint32_t attachment) const { dawn::TextureFormat RenderPipelineBase::GetColorAttachmentFormat(uint32_t attachment) const {
ASSERT(!IsError());
return mColorAttachmentFormats[attachment]; return mColorAttachmentFormats[attachment];
} }
dawn::TextureFormat RenderPipelineBase::GetDepthStencilFormat() const { dawn::TextureFormat RenderPipelineBase::GetDepthStencilFormat() const {
ASSERT(!IsError());
ASSERT(mHasDepthStencilAttachment); ASSERT(mHasDepthStencilAttachment);
return mDepthStencilFormat; return mDepthStencilFormat;
} }
bool RenderPipelineBase::IsCompatibleWith(const RenderPassDescriptorBase* renderPass) const { bool RenderPipelineBase::IsCompatibleWith(const RenderPassDescriptorBase* renderPass) const {
ASSERT(!IsError());
// TODO(cwallez@chromium.org): This is called on every SetPipeline command. Optimize it for // TODO(cwallez@chromium.org): This is called on every SetPipeline command. Optimize it for
// example by caching some "attachment compatibility" object that would make the // example by caching some "attachment compatibility" object that would make the
// compatibility check a single pointer comparison. // compatibility check a single pointer comparison.

View File

@ -36,6 +36,8 @@ namespace dawn_native {
public: public:
RenderPipelineBase(DeviceBase* device, const RenderPipelineDescriptor* descriptor); RenderPipelineBase(DeviceBase* device, const RenderPipelineDescriptor* descriptor);
static RenderPipelineBase* MakeError(DeviceBase* device);
const BlendStateDescriptor* GetBlendStateDescriptor(uint32_t attachmentSlot); const BlendStateDescriptor* GetBlendStateDescriptor(uint32_t attachmentSlot);
const DepthStencilStateDescriptor* GetDepthStencilStateDescriptor(); const DepthStencilStateDescriptor* GetDepthStencilStateDescriptor();
dawn::IndexFormat GetIndexFormat() const; dawn::IndexFormat GetIndexFormat() const;
@ -52,6 +54,8 @@ namespace dawn_native {
bool IsCompatibleWith(const RenderPassDescriptorBase* renderPass) const; bool IsCompatibleWith(const RenderPassDescriptorBase* renderPass) const;
private: private:
RenderPipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag);
DepthStencilStateDescriptor mDepthStencilState; DepthStencilStateDescriptor mDepthStencilState;
dawn::IndexFormat mIndexFormat; dawn::IndexFormat mIndexFormat;
Ref<InputStateBase> mInputState; Ref<InputStateBase> mInputState;

View File

@ -49,4 +49,13 @@ namespace dawn_native {
SamplerBase::SamplerBase(DeviceBase* device, const SamplerDescriptor*) : ObjectBase(device) { SamplerBase::SamplerBase(DeviceBase* device, const SamplerDescriptor*) : ObjectBase(device) {
} }
SamplerBase::SamplerBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
}
// static
SamplerBase* SamplerBase::MakeError(DeviceBase* device) {
return new SamplerBase(device, ObjectBase::kError);
}
} // namespace dawn_native } // namespace dawn_native

View File

@ -29,6 +29,11 @@ namespace dawn_native {
class SamplerBase : public ObjectBase { class SamplerBase : public ObjectBase {
public: public:
SamplerBase(DeviceBase* device, const SamplerDescriptor* descriptor); SamplerBase(DeviceBase* device, const SamplerDescriptor* descriptor);
static SamplerBase* MakeError(DeviceBase* device);
private:
SamplerBase(DeviceBase* device, ObjectBase::ErrorTag tag);
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -69,7 +69,18 @@ namespace dawn_native {
: ObjectBase(device) { : ObjectBase(device) {
} }
ShaderModuleBase::ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
}
// static
ShaderModuleBase* ShaderModuleBase::MakeError(DeviceBase* device) {
return new ShaderModuleBase(device, ObjectBase::kError);
}
void ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) { void ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) {
ASSERT(!IsError());
DeviceBase* device = GetDevice(); DeviceBase* device = GetDevice();
// TODO(cwallez@chromium.org): make errors here builder-level // TODO(cwallez@chromium.org): make errors here builder-level
// currently errors here do not prevent the shadermodule from being used // currently errors here do not prevent the shadermodule from being used
@ -211,22 +222,28 @@ namespace dawn_native {
} }
const ShaderModuleBase::PushConstantInfo& ShaderModuleBase::GetPushConstants() const { const ShaderModuleBase::PushConstantInfo& ShaderModuleBase::GetPushConstants() const {
ASSERT(!IsError());
return mPushConstants; return mPushConstants;
} }
const ShaderModuleBase::ModuleBindingInfo& ShaderModuleBase::GetBindingInfo() const { const ShaderModuleBase::ModuleBindingInfo& ShaderModuleBase::GetBindingInfo() const {
ASSERT(!IsError());
return mBindingInfo; return mBindingInfo;
} }
const std::bitset<kMaxVertexAttributes>& ShaderModuleBase::GetUsedVertexAttributes() const { const std::bitset<kMaxVertexAttributes>& ShaderModuleBase::GetUsedVertexAttributes() const {
ASSERT(!IsError());
return mUsedVertexAttributes; return mUsedVertexAttributes;
} }
dawn::ShaderStage ShaderModuleBase::GetExecutionModel() const { dawn::ShaderStage ShaderModuleBase::GetExecutionModel() const {
ASSERT(!IsError());
return mExecutionModel; return mExecutionModel;
} }
bool ShaderModuleBase::IsCompatibleWithPipelineLayout(const PipelineLayoutBase* layout) { bool ShaderModuleBase::IsCompatibleWithPipelineLayout(const PipelineLayoutBase* layout) {
ASSERT(!IsError());
for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
if (!IsCompatibleWithBindGroupLayout(group, layout->GetBindGroupLayout(group))) { if (!IsCompatibleWithBindGroupLayout(group, layout->GetBindGroupLayout(group))) {
return false; return false;
@ -246,6 +263,8 @@ namespace dawn_native {
bool ShaderModuleBase::IsCompatibleWithBindGroupLayout(size_t group, bool ShaderModuleBase::IsCompatibleWithBindGroupLayout(size_t group,
const BindGroupLayoutBase* layout) { const BindGroupLayoutBase* layout) {
ASSERT(!IsError());
const auto& layoutInfo = layout->GetBindingInfo(); const auto& layoutInfo = layout->GetBindingInfo();
for (size_t i = 0; i < kMaxBindingsPerGroup; ++i) { for (size_t i = 0; i < kMaxBindingsPerGroup; ++i) {
const auto& moduleInfo = mBindingInfo[group][i]; const auto& moduleInfo = mBindingInfo[group][i];

View File

@ -40,6 +40,8 @@ namespace dawn_native {
public: public:
ShaderModuleBase(DeviceBase* device, const ShaderModuleDescriptor* descriptor); ShaderModuleBase(DeviceBase* device, const ShaderModuleDescriptor* descriptor);
static ShaderModuleBase* MakeError(DeviceBase* device);
void ExtractSpirvInfo(const spirv_cross::Compiler& compiler); void ExtractSpirvInfo(const spirv_cross::Compiler& compiler);
struct PushConstantInfo { struct PushConstantInfo {
@ -68,6 +70,8 @@ namespace dawn_native {
bool IsCompatibleWithPipelineLayout(const PipelineLayoutBase* layout); bool IsCompatibleWithPipelineLayout(const PipelineLayoutBase* layout);
private: private:
ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag);
bool IsCompatibleWithBindGroupLayout(size_t group, const BindGroupLayoutBase* layout); bool IsCompatibleWithBindGroupLayout(size_t group, const BindGroupLayoutBase* layout);
PushConstantInfo mPushConstants = {}; PushConstantInfo mPushConstants = {};

View File

@ -72,6 +72,7 @@ namespace dawn_native {
} }
void SwapChainBase::Present(TextureBase* texture) { void SwapChainBase::Present(TextureBase* texture) {
// This also checks that the texture is valid since mLastNextTexture is always valid.
if (texture != mLastNextTexture) { if (texture != mLastNextTexture) {
GetDevice()->HandleError("Tried to present something other than the last NextTexture"); GetDevice()->HandleError("Tried to present something other than the last NextTexture");
return; return;

View File

@ -163,13 +163,14 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateTextureViewDescriptor(const DeviceBase*, MaybeError ValidateTextureViewDescriptor(const DeviceBase* device,
const TextureBase* texture, const TextureBase* texture,
const TextureViewDescriptor* descriptor) { const TextureViewDescriptor* descriptor) {
if (descriptor->nextInChain != nullptr) { if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
} }
DAWN_TRY(device->ValidateObject(texture));
DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension)); DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension));
DAWN_TRY(ValidateTextureFormat(descriptor->format)); DAWN_TRY(ValidateTextureFormat(descriptor->format));
@ -292,31 +293,52 @@ namespace dawn_native {
mUsage(descriptor->usage) { mUsage(descriptor->usage) {
} }
TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
}
// static
TextureBase* TextureBase::MakeError(DeviceBase* device) {
return new TextureBase(device, ObjectBase::kError);
}
dawn::TextureDimension TextureBase::GetDimension() const { dawn::TextureDimension TextureBase::GetDimension() const {
ASSERT(!IsError());
return mDimension; return mDimension;
} }
dawn::TextureFormat TextureBase::GetFormat() const { dawn::TextureFormat TextureBase::GetFormat() const {
ASSERT(!IsError());
return mFormat; return mFormat;
} }
const Extent3D& TextureBase::GetSize() const { const Extent3D& TextureBase::GetSize() const {
ASSERT(!IsError());
return mSize; return mSize;
} }
uint32_t TextureBase::GetArrayLayers() const { uint32_t TextureBase::GetArrayLayers() const {
ASSERT(!IsError());
return mArrayLayers; return mArrayLayers;
} }
uint32_t TextureBase::GetNumMipLevels() const { uint32_t TextureBase::GetNumMipLevels() const {
ASSERT(!IsError());
return mNumMipLevels; return mNumMipLevels;
} }
dawn::TextureUsageBit TextureBase::GetUsage() const { dawn::TextureUsageBit TextureBase::GetUsage() const {
ASSERT(!IsError());
return mUsage; return mUsage;
} }
MaybeError TextureBase::ValidateCanUseInSubmitNow() const { MaybeError TextureBase::ValidateCanUseInSubmitNow() const {
ASSERT(!IsError());
return {}; return {};
} }
TextureViewBase* TextureBase::CreateDefaultTextureView() { TextureViewBase* TextureBase::CreateDefaultTextureView() {
TextureViewDescriptor descriptor = MakeDefaultTextureViewDescriptor(this); TextureViewDescriptor descriptor = {};
if (!IsError()) {
descriptor = MakeDefaultTextureViewDescriptor(this);
}
return GetDevice()->CreateTextureView(this, &descriptor); return GetDevice()->CreateTextureView(this, &descriptor);
} }
@ -336,31 +358,47 @@ namespace dawn_native {
mLayerCount(descriptor->layerCount) { mLayerCount(descriptor->layerCount) {
} }
TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ObjectBase(device, tag) {
}
// static
TextureViewBase* TextureViewBase::MakeError(DeviceBase* device) {
return new TextureViewBase(device, ObjectBase::kError);
}
const TextureBase* TextureViewBase::GetTexture() const { const TextureBase* TextureViewBase::GetTexture() const {
ASSERT(!IsError());
return mTexture.Get(); return mTexture.Get();
} }
TextureBase* TextureViewBase::GetTexture() { TextureBase* TextureViewBase::GetTexture() {
ASSERT(!IsError());
return mTexture.Get(); return mTexture.Get();
} }
dawn::TextureFormat TextureViewBase::GetFormat() const { dawn::TextureFormat TextureViewBase::GetFormat() const {
ASSERT(!IsError());
return mFormat; return mFormat;
} }
uint32_t TextureViewBase::GetBaseMipLevel() const { uint32_t TextureViewBase::GetBaseMipLevel() const {
ASSERT(!IsError());
return mBaseMipLevel; return mBaseMipLevel;
} }
uint32_t TextureViewBase::GetLevelCount() const { uint32_t TextureViewBase::GetLevelCount() const {
ASSERT(!IsError());
return mLevelCount; return mLevelCount;
} }
uint32_t TextureViewBase::GetBaseArrayLayer() const { uint32_t TextureViewBase::GetBaseArrayLayer() const {
ASSERT(!IsError());
return mBaseArrayLayer; return mBaseArrayLayer;
} }
uint32_t TextureViewBase::GetLayerCount() const { uint32_t TextureViewBase::GetLayerCount() const {
ASSERT(!IsError());
return mLayerCount; return mLayerCount;
} }
} // namespace dawn_native } // namespace dawn_native

View File

@ -47,6 +47,8 @@ namespace dawn_native {
public: public:
TextureBase(DeviceBase* device, const TextureDescriptor* descriptor); TextureBase(DeviceBase* device, const TextureDescriptor* descriptor);
static TextureBase* MakeError(DeviceBase* device);
dawn::TextureDimension GetDimension() const; dawn::TextureDimension GetDimension() const;
dawn::TextureFormat GetFormat() const; dawn::TextureFormat GetFormat() const;
const Extent3D& GetSize() const; const Extent3D& GetSize() const;
@ -61,6 +63,8 @@ namespace dawn_native {
TextureViewBase* CreateTextureView(const TextureViewDescriptor* descriptor); TextureViewBase* CreateTextureView(const TextureViewDescriptor* descriptor);
private: private:
TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag);
dawn::TextureDimension mDimension; dawn::TextureDimension mDimension;
dawn::TextureFormat mFormat; dawn::TextureFormat mFormat;
Extent3D mSize; Extent3D mSize;
@ -73,6 +77,8 @@ namespace dawn_native {
public: public:
TextureViewBase(TextureBase* texture, const TextureViewDescriptor* descriptor); TextureViewBase(TextureBase* texture, const TextureViewDescriptor* descriptor);
static TextureViewBase* MakeError(DeviceBase* device);
const TextureBase* GetTexture() const; const TextureBase* GetTexture() const;
TextureBase* GetTexture(); TextureBase* GetTexture();
@ -83,6 +89,8 @@ namespace dawn_native {
uint32_t GetLayerCount() const; uint32_t GetLayerCount() const;
private: private:
TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag);
Ref<TextureBase> mTexture; Ref<TextureBase> mTexture;
dawn::TextureFormat mFormat; dawn::TextureFormat mFormat;

View File

@ -161,6 +161,19 @@ TEST_F(BindGroupValidationTest, SamplerBindingType) {
binding.buffer = mUBO; binding.buffer = mUBO;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.buffer = nullptr; binding.buffer = nullptr;
// Setting the sampler to an error sampler is an error.
{
dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
samplerDesc.minFilter = static_cast<dawn::FilterMode>(0xFFFFFFFF);
dawn::Sampler errorSampler;
ASSERT_DEVICE_ERROR(errorSampler = device.CreateSampler(&samplerDesc));
binding.sampler = errorSampler;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.sampler = nullptr;
}
} }
// Check that a texture binding must contain exactly a texture view // Check that a texture binding must contain exactly a texture view
@ -198,6 +211,24 @@ TEST_F(BindGroupValidationTest, TextureBindingType) {
binding.buffer = mUBO; binding.buffer = mUBO;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.buffer = nullptr; binding.buffer = nullptr;
// Setting the texture view to an error texture view is an error.
{
dawn::TextureViewDescriptor viewDesc;
viewDesc.format = dawn::TextureFormat::R8G8B8A8Unorm;
viewDesc.dimension = dawn::TextureViewDimension::e2D;
viewDesc.baseMipLevel = 0;
viewDesc.levelCount = 0;
viewDesc.baseArrayLayer = 0;
viewDesc.layerCount = 0;
dawn::TextureView errorView;
ASSERT_DEVICE_ERROR(errorView = mSampledTexture.CreateTextureView(&viewDesc));
binding.textureView = errorView;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.textureView = nullptr;
}
} }
// Check that a buffer binding must contain exactly a buffer // Check that a buffer binding must contain exactly a buffer
@ -235,6 +266,20 @@ TEST_F(BindGroupValidationTest, BufferBindingType) {
binding.sampler = mSampler; binding.sampler = mSampler;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.sampler = nullptr; binding.sampler = nullptr;
// Setting the buffer to an error buffer is an error.
{
dawn::BufferDescriptor bufferDesc;
bufferDesc.size = 1024;
bufferDesc.usage = static_cast<dawn::BufferUsageBit>(0xFFFFFFFF);
dawn::Buffer errorBuffer;
ASSERT_DEVICE_ERROR(errorBuffer = device.CreateBuffer(&bufferDesc));
binding.buffer = errorBuffer;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.buffer = nullptr;
}
} }
// Check that a texture must have the correct usage // Check that a texture must have the correct usage
@ -338,6 +383,25 @@ TEST_F(BindGroupValidationTest, BufferBindingOOB) {
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 256, uint32_t(0) - uint32_t(256)}})); ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 256, uint32_t(0) - uint32_t(256)}}));
} }
// Test what happens when the layout is an error.
TEST_F(BindGroupValidationTest, ErrorLayout) {
dawn::BindGroupLayout goodLayout = utils::MakeBindGroupLayout(device, {
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
});
dawn::BindGroupLayout errorLayout;
ASSERT_DEVICE_ERROR(errorLayout = utils::MakeBindGroupLayout(device, {
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
}));
// Control case, creating with the good layout works
utils::MakeBindGroup(device, goodLayout, {{0, mUBO, 0, 256}});
// Control case, creating with the good layout works
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, errorLayout, {{0, mUBO, 0, 256}}));
}
class BindGroupLayoutValidationTest : public ValidationTest { class BindGroupLayoutValidationTest : public ValidationTest {
}; };