Implement RenderBundle in the frontend
This CL implements RenderBundle and RenderBundleEncoder in the frontend
and adds unittests for validation.
Bug: dawn:154
Change-Id: Ice5ecd384cd627ad270b73052408f8139d1ea5f4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9221
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/AttachmentState.cpp b/src/dawn_native/AttachmentState.cpp
index 39ddb4b..00bd10b 100644
--- a/src/dawn_native/AttachmentState.cpp
+++ b/src/dawn_native/AttachmentState.cpp
@@ -21,6 +21,19 @@
namespace dawn_native {
+ AttachmentStateBlueprint::AttachmentStateBlueprint(
+ const RenderBundleEncoderDescriptor* descriptor)
+ : mHasDepthStencilAttachment(descriptor->depthStencilFormat != nullptr),
+ mSampleCount(descriptor->sampleCount) {
+ for (uint32_t i = 0; i < descriptor->colorFormatsCount; ++i) {
+ mColorAttachmentsSet.set(i);
+ mColorFormats[i] = descriptor->colorFormats[i];
+ }
+ if (mHasDepthStencilAttachment) {
+ mDepthStencilFormat = *descriptor->depthStencilFormat;
+ }
+ }
+
AttachmentStateBlueprint::AttachmentStateBlueprint(const RenderPipelineDescriptor* descriptor)
: mHasDepthStencilAttachment(descriptor->depthStencilState != nullptr),
mSampleCount(descriptor->sampleCount) {
diff --git a/src/dawn_native/AttachmentState.h b/src/dawn_native/AttachmentState.h
index 34f2c1a..a7202fd 100644
--- a/src/dawn_native/AttachmentState.h
+++ b/src/dawn_native/AttachmentState.h
@@ -33,8 +33,9 @@
class AttachmentStateBlueprint {
public:
// Note: Descriptors must be validated before the AttachmentState is constructed.
- AttachmentStateBlueprint(const RenderPipelineDescriptor* descriptor);
- AttachmentStateBlueprint(const RenderPassDescriptor* descriptor);
+ explicit AttachmentStateBlueprint(const RenderBundleEncoderDescriptor* descriptor);
+ explicit AttachmentStateBlueprint(const RenderPipelineDescriptor* descriptor);
+ explicit AttachmentStateBlueprint(const RenderPassDescriptor* descriptor);
AttachmentStateBlueprint(const AttachmentStateBlueprint& rhs);
diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp
index 9099a9e..78f8949 100644
--- a/src/dawn_native/CommandValidation.cpp
+++ b/src/dawn_native/CommandValidation.cpp
@@ -19,6 +19,7 @@
#include "dawn_native/CommandBufferStateTracker.h"
#include "dawn_native/Commands.h"
#include "dawn_native/PassResourceUsageTracker.h"
+#include "dawn_native/RenderBundle.h"
#include "dawn_native/RenderPipeline.h"
namespace dawn_native {
@@ -177,6 +178,27 @@
} // namespace
+ MaybeError ValidateRenderBundle(CommandIterator* commands,
+ const AttachmentState* attachmentState,
+ PassResourceUsage* resourceUsage) {
+ PassResourceUsageTracker usageTracker;
+ CommandBufferStateTracker commandBufferState;
+ unsigned int debugGroupStackSize = 0;
+
+ Command type;
+ while (commands->NextCommandId(&type)) {
+ DAWN_TRY(ValidateRenderBundleCommand(commands, type, &usageTracker, &commandBufferState,
+ attachmentState, &debugGroupStackSize,
+ "Command disallowed inside a render bundle"));
+ }
+
+ DAWN_TRY(usageTracker.ValidateRenderPassUsages());
+ ASSERT(resourceUsage != nullptr);
+ *resourceUsage = usageTracker.AcquireResourceUsage();
+
+ return {};
+ }
+
MaybeError ValidateRenderPass(CommandIterator* commands,
BeginRenderPassCmd* renderPass,
std::vector<PassResourceUsage>* perPassResourceUsages) {
@@ -217,6 +239,33 @@
return {};
} break;
+ case Command::ExecuteBundles: {
+ ExecuteBundlesCmd* cmd = commands->NextCommand<ExecuteBundlesCmd>();
+ auto bundles = commands->NextData<Ref<RenderBundleBase>>(cmd->count);
+ for (uint32_t i = 0; i < cmd->count; ++i) {
+ if (DAWN_UNLIKELY(renderPass->attachmentState.Get() !=
+ bundles[i]->GetAttachmentState())) {
+ return DAWN_VALIDATION_ERROR(
+ "Render bundle is not compatible with render pass");
+ }
+
+ const PassResourceUsage& usages = bundles[i]->GetResourceUsage();
+ for (uint32_t i = 0; i < usages.buffers.size(); ++i) {
+ usageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]);
+ }
+
+ for (uint32_t i = 0; i < usages.textures.size(); ++i) {
+ usageTracker.TextureUsedAs(usages.textures[i], usages.textureUsages[i]);
+ }
+ }
+
+ if (cmd->count > 0) {
+ // Reset state. It is invalidated after render bundle execution.
+ commandBufferState = CommandBufferStateTracker{};
+ }
+
+ } break;
+
case Command::SetStencilReference: {
commands->NextCommand<SetStencilReferenceCmd>();
} break;
diff --git a/src/dawn_native/CommandValidation.h b/src/dawn_native/CommandValidation.h
index 2d29cb3..c90343c 100644
--- a/src/dawn_native/CommandValidation.h
+++ b/src/dawn_native/CommandValidation.h
@@ -22,9 +22,13 @@
namespace dawn_native {
+ class AttachmentState;
struct BeginRenderPassCmd;
struct PassResourceUsage;
+ MaybeError ValidateRenderBundle(CommandIterator* commands,
+ const AttachmentState* attachmentState,
+ PassResourceUsage* resourceUsage);
MaybeError ValidateRenderPass(CommandIterator* commands,
BeginRenderPassCmd* renderPass,
std::vector<PassResourceUsage>* perPassResourceUsages);
diff --git a/src/dawn_native/Commands.cpp b/src/dawn_native/Commands.cpp
index fbc9172..eb11791 100644
--- a/src/dawn_native/Commands.cpp
+++ b/src/dawn_native/Commands.cpp
@@ -18,6 +18,7 @@
#include "dawn_native/Buffer.h"
#include "dawn_native/CommandAllocator.h"
#include "dawn_native/ComputePipeline.h"
+#include "dawn_native/RenderBundle.h"
#include "dawn_native/RenderPipeline.h"
#include "dawn_native/Texture.h"
@@ -86,6 +87,14 @@
EndRenderPassCmd* cmd = commands->NextCommand<EndRenderPassCmd>();
cmd->~EndRenderPassCmd();
} break;
+ case Command::ExecuteBundles: {
+ ExecuteBundlesCmd* cmd = commands->NextCommand<ExecuteBundlesCmd>();
+ auto bundles = commands->NextData<Ref<RenderBundleBase>>(cmd->count);
+ for (size_t i = 0; i < cmd->count; ++i) {
+ (&bundles[i])->~Ref<RenderBundleBase>();
+ }
+ cmd->~ExecuteBundlesCmd();
+ } break;
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
commands->NextData<char>(cmd->length + 1);
@@ -207,6 +216,11 @@
commands->NextCommand<EndRenderPassCmd>();
break;
+ case Command::ExecuteBundles: {
+ auto* cmd = commands->NextCommand<ExecuteBundlesCmd>();
+ commands->NextData<Ref<RenderBundleBase>>(cmd->count);
+ } break;
+
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
commands->NextData<char>(cmd->length + 1);
diff --git a/src/dawn_native/Commands.h b/src/dawn_native/Commands.h
index 442cc95..18d834d 100644
--- a/src/dawn_native/Commands.h
+++ b/src/dawn_native/Commands.h
@@ -46,6 +46,7 @@
DrawIndexedIndirect,
EndComputePass,
EndRenderPass,
+ ExecuteBundles,
InsertDebugMarker,
PopDebugGroup,
PushDebugGroup,
@@ -170,6 +171,10 @@
struct EndRenderPassCmd {};
+ struct ExecuteBundlesCmd {
+ uint32_t count;
+ };
+
struct InsertDebugMarkerCmd {
uint32_t length;
};
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 3eef7e3..b02bda1e 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -29,6 +29,7 @@
#include "dawn_native/Instance.h"
#include "dawn_native/PipelineLayout.h"
#include "dawn_native/Queue.h"
+#include "dawn_native/RenderBundleEncoder.h"
#include "dawn_native/RenderPipeline.h"
#include "dawn_native/Sampler.h"
#include "dawn_native/ShaderModule.h"
@@ -268,33 +269,34 @@
}
Ref<AttachmentState> DeviceBase::GetOrCreateAttachmentState(
- const RenderPipelineDescriptor* descriptor) {
- AttachmentStateBlueprint blueprint(descriptor);
-
- auto iter = mCaches->attachmentStates.find(&blueprint);
+ AttachmentStateBlueprint* blueprint) {
+ auto iter = mCaches->attachmentStates.find(blueprint);
if (iter != mCaches->attachmentStates.end()) {
return static_cast<AttachmentState*>(*iter);
}
- Ref<AttachmentState> attachmentState = new AttachmentState(this, blueprint);
+ Ref<AttachmentState> attachmentState = new AttachmentState(this, *blueprint);
attachmentState->Release();
mCaches->attachmentStates.insert(attachmentState.Get());
return attachmentState;
}
Ref<AttachmentState> DeviceBase::GetOrCreateAttachmentState(
+ const RenderBundleEncoderDescriptor* descriptor) {
+ AttachmentStateBlueprint blueprint(descriptor);
+ return GetOrCreateAttachmentState(&blueprint);
+ }
+
+ Ref<AttachmentState> DeviceBase::GetOrCreateAttachmentState(
+ const RenderPipelineDescriptor* descriptor) {
+ AttachmentStateBlueprint blueprint(descriptor);
+ return GetOrCreateAttachmentState(&blueprint);
+ }
+
+ Ref<AttachmentState> DeviceBase::GetOrCreateAttachmentState(
const RenderPassDescriptor* descriptor) {
AttachmentStateBlueprint blueprint(descriptor);
-
- auto iter = mCaches->attachmentStates.find(&blueprint);
- if (iter != mCaches->attachmentStates.end()) {
- return static_cast<AttachmentState*>(*iter);
- }
-
- Ref<AttachmentState> attachmentState = new AttachmentState(this, blueprint);
- attachmentState->Release();
- mCaches->attachmentStates.insert(attachmentState.Get());
- return attachmentState;
+ return GetOrCreateAttachmentState(&blueprint);
}
void DeviceBase::UncacheAttachmentState(AttachmentState* obj) {
@@ -428,6 +430,16 @@
return result;
}
+ RenderBundleEncoderBase* DeviceBase::CreateRenderBundleEncoder(
+ const RenderBundleEncoderDescriptor* descriptor) {
+ RenderBundleEncoderBase* result = nullptr;
+
+ if (ConsumedError(CreateRenderBundleEncoderInternal(&result, descriptor))) {
+ return RenderBundleEncoderBase::MakeError(this);
+ }
+
+ return result;
+ }
RenderPipelineBase* DeviceBase::CreateRenderPipeline(
const RenderPipelineDescriptor* descriptor) {
RenderPipelineBase* result = nullptr;
@@ -601,6 +613,14 @@
return {};
}
+ MaybeError DeviceBase::CreateRenderBundleEncoderInternal(
+ RenderBundleEncoderBase** result,
+ const RenderBundleEncoderDescriptor* descriptor) {
+ DAWN_TRY(ValidateRenderBundleEncoderDescriptor(this, descriptor));
+ *result = new RenderBundleEncoderBase(this, descriptor);
+ return {};
+ }
+
MaybeError DeviceBase::CreateRenderPipelineInternal(
RenderPipelineBase** result,
const RenderPipelineDescriptor* descriptor) {
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 8a2b1c8..3ccde37 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -34,6 +34,7 @@
class AdapterBase;
class AttachmentState;
+ class AttachmentStateBlueprint;
class FenceSignalTracker;
class DynamicUploader;
class StagingBufferBase;
@@ -116,6 +117,9 @@
const ShaderModuleDescriptor* descriptor);
void UncacheShaderModule(ShaderModuleBase* obj);
+ Ref<AttachmentState> GetOrCreateAttachmentState(AttachmentStateBlueprint* blueprint);
+ Ref<AttachmentState> GetOrCreateAttachmentState(
+ const RenderBundleEncoderDescriptor* descriptor);
Ref<AttachmentState> GetOrCreateAttachmentState(const RenderPipelineDescriptor* descriptor);
Ref<AttachmentState> GetOrCreateAttachmentState(const RenderPassDescriptor* descriptor);
void UncacheAttachmentState(AttachmentState* obj);
@@ -132,6 +136,8 @@
ComputePipelineBase* CreateComputePipeline(const ComputePipelineDescriptor* descriptor);
PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor);
QueueBase* CreateQueue();
+ RenderBundleEncoderBase* CreateRenderBundleEncoder(
+ const RenderBundleEncoderDescriptor* descriptor);
RenderPipelineBase* CreateRenderPipeline(const RenderPipelineDescriptor* descriptor);
SamplerBase* CreateSampler(const SamplerDescriptor* descriptor);
ShaderModuleBase* CreateShaderModule(const ShaderModuleDescriptor* descriptor);
@@ -204,6 +210,9 @@
MaybeError CreatePipelineLayoutInternal(PipelineLayoutBase** result,
const PipelineLayoutDescriptor* descriptor);
MaybeError CreateQueueInternal(QueueBase** result);
+ MaybeError CreateRenderBundleEncoderInternal(
+ RenderBundleEncoderBase** result,
+ const RenderBundleEncoderDescriptor* descriptor);
MaybeError CreateRenderPipelineInternal(RenderPipelineBase** result,
const RenderPipelineDescriptor* descriptor);
MaybeError CreateSamplerInternal(SamplerBase** result, const SamplerDescriptor* descriptor);
diff --git a/src/dawn_native/EncodingContext.cpp b/src/dawn_native/EncodingContext.cpp
index d2c8a75..d36ccef 100644
--- a/src/dawn_native/EncodingContext.cpp
+++ b/src/dawn_native/EncodingContext.cpp
@@ -76,6 +76,10 @@
}
MaybeError EncodingContext::Finish() {
+ if (IsFinished()) {
+ return DAWN_VALIDATION_ERROR("Command encoding already finished");
+ }
+
const void* currentEncoder = mCurrentEncoder;
const void* topLevelEncoder = mTopLevelEncoder;
diff --git a/src/dawn_native/Forward.h b/src/dawn_native/Forward.h
index b4c3e83..1ba29da 100644
--- a/src/dawn_native/Forward.h
+++ b/src/dawn_native/Forward.h
@@ -32,6 +32,8 @@
class PipelineBase;
class PipelineLayoutBase;
class QueueBase;
+ class RenderBundleBase;
+ class RenderBundleEncoderBase;
class RenderPassEncoderBase;
class RenderPipelineBase;
class SamplerBase;
diff --git a/src/dawn_native/RenderBundle.cpp b/src/dawn_native/RenderBundle.cpp
new file mode 100644
index 0000000..9cd08ea
--- /dev/null
+++ b/src/dawn_native/RenderBundle.cpp
@@ -0,0 +1,61 @@
+// Copyright 2019 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/RenderBundle.h"
+
+#include "common/BitSetIterator.h"
+#include "dawn_native/Commands.h"
+#include "dawn_native/Device.h"
+#include "dawn_native/RenderBundleEncoder.h"
+
+namespace dawn_native {
+
+ RenderBundleBase::RenderBundleBase(RenderBundleEncoderBase* encoder,
+ const RenderBundleDescriptor* descriptor,
+ AttachmentState* attachmentState,
+ PassResourceUsage resourceUsage)
+ : ObjectBase(encoder->GetDevice()),
+ mCommands(encoder->AcquireCommands()),
+ mAttachmentState(attachmentState),
+ mResourceUsage(std::move(resourceUsage)) {
+ }
+
+ RenderBundleBase::~RenderBundleBase() {
+ FreeCommands(&mCommands);
+ }
+
+ // static
+ RenderBundleBase* RenderBundleBase::MakeError(DeviceBase* device) {
+ return new RenderBundleBase(device, ObjectBase::kError);
+ }
+
+ RenderBundleBase::RenderBundleBase(DeviceBase* device, ErrorTag errorTag)
+ : ObjectBase(device, errorTag) {
+ }
+
+ CommandIterator* RenderBundleBase::GetCommands() {
+ return &mCommands;
+ }
+
+ const AttachmentState* RenderBundleBase::GetAttachmentState() const {
+ ASSERT(!IsError());
+ return mAttachmentState.Get();
+ }
+
+ const PassResourceUsage& RenderBundleBase::GetResourceUsage() const {
+ ASSERT(!IsError());
+ return mResourceUsage;
+ }
+
+} // namespace dawn_native
diff --git a/src/dawn_native/RenderBundle.h b/src/dawn_native/RenderBundle.h
new file mode 100644
index 0000000..26db850
--- /dev/null
+++ b/src/dawn_native/RenderBundle.h
@@ -0,0 +1,60 @@
+// Copyright 2019 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.
+
+#ifndef DAWNNATIVE_RENDERBUNDLE_H_
+#define DAWNNATIVE_RENDERBUNDLE_H_
+
+#include "common/Constants.h"
+#include "dawn_native/AttachmentState.h"
+#include "dawn_native/CommandAllocator.h"
+#include "dawn_native/Error.h"
+#include "dawn_native/ObjectBase.h"
+#include "dawn_native/PassResourceUsage.h"
+
+#include "dawn_native/dawn_platform.h"
+
+#include <bitset>
+
+namespace dawn_native {
+
+ struct BeginRenderPassCmd;
+ struct RenderBundleDescriptor;
+ class RenderBundleEncoderBase;
+
+ class RenderBundleBase : public ObjectBase {
+ public:
+ RenderBundleBase(RenderBundleEncoderBase* encoder,
+ const RenderBundleDescriptor* descriptor,
+ AttachmentState* attachmentState,
+ PassResourceUsage resourceUsage);
+ ~RenderBundleBase() override;
+
+ static RenderBundleBase* MakeError(DeviceBase* device);
+
+ CommandIterator* GetCommands();
+
+ const AttachmentState* GetAttachmentState() const;
+ const PassResourceUsage& GetResourceUsage() const;
+
+ private:
+ RenderBundleBase(DeviceBase* device, ErrorTag errorTag);
+
+ CommandIterator mCommands;
+ Ref<AttachmentState> mAttachmentState;
+ PassResourceUsage mResourceUsage;
+ };
+
+} // namespace dawn_native
+
+#endif // DAWNNATIVE_RENDERBUNDLE_H_
diff --git a/src/dawn_native/RenderBundleEncoder.cpp b/src/dawn_native/RenderBundleEncoder.cpp
new file mode 100644
index 0000000..b4febef
--- /dev/null
+++ b/src/dawn_native/RenderBundleEncoder.cpp
@@ -0,0 +1,99 @@
+// Copyright 2019 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/RenderBundleEncoder.h"
+
+#include "dawn_native/CommandValidation.h"
+#include "dawn_native/Commands.h"
+#include "dawn_native/Device.h"
+#include "dawn_native/RenderPipeline.h"
+#include "dawn_native/ValidationUtils_autogen.h"
+
+namespace dawn_native {
+
+ MaybeError ValidateRenderBundleEncoderDescriptor(
+ const DeviceBase* device,
+ const RenderBundleEncoderDescriptor* descriptor) {
+ if (!IsValidSampleCount(descriptor->sampleCount)) {
+ return DAWN_VALIDATION_ERROR("Sample count is not supported");
+ }
+
+ if (descriptor->colorFormatsCount > kMaxColorAttachments) {
+ return DAWN_VALIDATION_ERROR("Color formats count exceeds maximum");
+ }
+
+ if (descriptor->colorFormatsCount == 0 && !descriptor->depthStencilFormat) {
+ return DAWN_VALIDATION_ERROR("Should have at least one attachment format");
+ }
+
+ for (uint32_t i = 0; i < descriptor->colorFormatsCount; ++i) {
+ DAWN_TRY(ValidateTextureFormat(descriptor->colorFormats[i]));
+ }
+
+ if (descriptor->depthStencilFormat != nullptr) {
+ DAWN_TRY(ValidateTextureFormat(*descriptor->depthStencilFormat));
+ }
+
+ return {};
+ }
+
+ RenderBundleEncoderBase::RenderBundleEncoderBase(
+ DeviceBase* device,
+ const RenderBundleEncoderDescriptor* descriptor)
+ : RenderEncoderBase(device, &mEncodingContext),
+ mEncodingContext(device, this),
+ mAttachmentState(device->GetOrCreateAttachmentState(descriptor)) {
+ }
+
+ RenderBundleEncoderBase::RenderBundleEncoderBase(DeviceBase* device, ErrorTag errorTag)
+ : RenderEncoderBase(device, &mEncodingContext, errorTag), mEncodingContext(device, this) {
+ }
+
+ // static
+ RenderBundleEncoderBase* RenderBundleEncoderBase::MakeError(DeviceBase* device) {
+ return new RenderBundleEncoderBase(device, ObjectBase::kError);
+ }
+
+ const AttachmentState* RenderBundleEncoderBase::GetAttachmentState() const {
+ return mAttachmentState.Get();
+ }
+
+ CommandIterator RenderBundleEncoderBase::AcquireCommands() {
+ return mEncodingContext.AcquireCommands();
+ }
+
+ RenderBundleBase* RenderBundleEncoderBase::Finish(const RenderBundleDescriptor* descriptor) {
+ if (GetDevice()->ConsumedError(ValidateFinish(descriptor))) {
+ return RenderBundleBase::MakeError(GetDevice());
+ }
+ ASSERT(!IsError());
+
+ return new RenderBundleBase(this, descriptor, mAttachmentState.Get(),
+ std::move(mResourceUsage));
+ }
+
+ MaybeError RenderBundleEncoderBase::ValidateFinish(const RenderBundleDescriptor* descriptor) {
+ DAWN_TRY(GetDevice()->ValidateObject(this));
+
+ // Even if Finish() validation fails, calling it will mutate the internal state of the
+ // encoding context. Subsequent calls to encode commands will generate errors.
+ DAWN_TRY(mEncodingContext.Finish());
+
+ CommandIterator* commands = mEncodingContext.GetIterator();
+
+ DAWN_TRY(ValidateRenderBundle(commands, mAttachmentState.Get(), &mResourceUsage));
+ return {};
+ }
+
+} // namespace dawn_native
diff --git a/src/dawn_native/RenderBundleEncoder.h b/src/dawn_native/RenderBundleEncoder.h
new file mode 100644
index 0000000..aa32201
--- /dev/null
+++ b/src/dawn_native/RenderBundleEncoder.h
@@ -0,0 +1,53 @@
+// Copyright 2019 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.
+
+#ifndef DAWNNATIVE_RENDERBUNDLEENCODER_H_
+#define DAWNNATIVE_RENDERBUNDLEENCODER_H_
+
+#include "dawn_native/AttachmentState.h"
+#include "dawn_native/EncodingContext.h"
+#include "dawn_native/Error.h"
+#include "dawn_native/RenderBundle.h"
+#include "dawn_native/RenderEncoderBase.h"
+
+namespace dawn_native {
+
+ MaybeError ValidateRenderBundleEncoderDescriptor(
+ const DeviceBase* device,
+ const RenderBundleEncoderDescriptor* descriptor);
+ class RenderBundleEncoderBase : public RenderEncoderBase {
+ public:
+ RenderBundleEncoderBase(DeviceBase* device,
+ const RenderBundleEncoderDescriptor* descriptor);
+
+ static RenderBundleEncoderBase* MakeError(DeviceBase* device);
+
+ const AttachmentState* GetAttachmentState() const;
+
+ RenderBundleBase* Finish(const RenderBundleDescriptor* descriptor);
+
+ CommandIterator AcquireCommands();
+
+ private:
+ RenderBundleEncoderBase(DeviceBase* device, ErrorTag errorTag);
+
+ MaybeError ValidateFinish(const RenderBundleDescriptor* descriptor);
+
+ EncodingContext mEncodingContext;
+ Ref<AttachmentState> mAttachmentState;
+ PassResourceUsage mResourceUsage;
+ };
+} // namespace dawn_native
+
+#endif // DAWNNATIVE_RENDERBUNDLEENCODER_H_
diff --git a/src/dawn_native/RenderPassEncoder.cpp b/src/dawn_native/RenderPassEncoder.cpp
index 54d5db5..27f5df3 100644
--- a/src/dawn_native/RenderPassEncoder.cpp
+++ b/src/dawn_native/RenderPassEncoder.cpp
@@ -19,6 +19,7 @@
#include "dawn_native/CommandEncoder.h"
#include "dawn_native/Commands.h"
#include "dawn_native/Device.h"
+#include "dawn_native/RenderBundle.h"
#include "dawn_native/RenderPipeline.h"
#include <math.h>
@@ -130,4 +131,24 @@
});
}
+ void RenderPassEncoderBase::ExecuteBundles(uint32_t count,
+ RenderBundleBase* const* renderBundles) {
+ mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
+ for (uint32_t i = 0; i < count; ++i) {
+ DAWN_TRY(GetDevice()->ValidateObject(renderBundles[i]));
+ }
+
+ ExecuteBundlesCmd* cmd =
+ allocator->Allocate<ExecuteBundlesCmd>(Command::ExecuteBundles);
+ cmd->count = count;
+
+ Ref<RenderBundleBase>* bundles = allocator->AllocateData<Ref<RenderBundleBase>>(count);
+ for (uint32_t i = 0; i < count; ++i) {
+ bundles[i] = renderBundles[i];
+ }
+
+ return {};
+ });
+ }
+
} // namespace dawn_native
diff --git a/src/dawn_native/RenderPassEncoder.h b/src/dawn_native/RenderPassEncoder.h
index b961079..4b7c06d 100644
--- a/src/dawn_native/RenderPassEncoder.h
+++ b/src/dawn_native/RenderPassEncoder.h
@@ -20,10 +20,12 @@
namespace dawn_native {
+ class RenderBundleBase;
+
// This is called RenderPassEncoderBase to match the code generator expectations. Note that it
// is a pure frontend type to record in its parent CommandEncoder and never has a backend
// implementation.
- // TODO(cwallez@chromium.org): Remove that generator limitation and rename to ComputePassEncoder
+ // TODO(cwallez@chromium.org): Remove that generator limitation and rename to RenderPassEncoder
class RenderPassEncoderBase : public RenderEncoderBase {
public:
RenderPassEncoderBase(DeviceBase* device,
@@ -45,6 +47,7 @@
float minDepth,
float maxDepth);
void SetScissorRect(uint32_t x, uint32_t y, uint32_t width, uint32_t height);
+ void ExecuteBundles(uint32_t count, RenderBundleBase* const* renderBundles);
protected:
RenderPassEncoderBase(DeviceBase* device,
diff --git a/src/dawn_native/RenderPipeline.h b/src/dawn_native/RenderPipeline.h
index 7a00e3a..490d178 100644
--- a/src/dawn_native/RenderPipeline.h
+++ b/src/dawn_native/RenderPipeline.h
@@ -28,6 +28,7 @@
struct BeginRenderPassCmd;
class DeviceBase;
+ class RenderBundleEncoderBase;
MaybeError ValidateRenderPipelineDescriptor(const DeviceBase* device,
const RenderPipelineDescriptor* descriptor);
diff --git a/src/tests/unittests/validation/RenderBundleValidationTests.cpp b/src/tests/unittests/validation/RenderBundleValidationTests.cpp
new file mode 100644
index 0000000..daac454
--- /dev/null
+++ b/src/tests/unittests/validation/RenderBundleValidationTests.cpp
@@ -0,0 +1,931 @@
+// Copyright 2019 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 "tests/unittests/validation/ValidationTest.h"
+
+#include "common/Constants.h"
+
+#include "utils/ComboRenderBundleEncoderDescriptor.h"
+#include "utils/ComboRenderPipelineDescriptor.h"
+#include "utils/DawnHelpers.h"
+
+namespace {
+
+ class RenderBundleValidationTest : public ValidationTest {
+ protected:
+ void SetUp() override {
+ ValidationTest::SetUp();
+
+ vsModule = utils::CreateShaderModule(device, utils::ShaderStage::Vertex, R"(
+ #version 450
+ layout(location = 0) in vec2 pos;
+ layout (set = 0, binding = 0) uniform vertexUniformBuffer {
+ mat2 transform;
+ };
+ void main() {
+ })");
+
+ fsModule = utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"(
+ #version 450
+ layout (set = 1, binding = 0) uniform fragmentUniformBuffer {
+ vec4 color;
+ };
+ layout (set = 1, binding = 1) buffer storageBuffer {
+ float dummy[];
+ };
+ void main() {
+ })");
+
+ dawn::BindGroupLayout bgls[] = {
+ utils::MakeBindGroupLayout(
+ device, {{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}}),
+ utils::MakeBindGroupLayout(
+ device,
+ {
+ {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer},
+ {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::StorageBuffer},
+ })};
+
+ dawn::PipelineLayoutDescriptor pipelineLayoutDesc;
+ pipelineLayoutDesc.bindGroupLayoutCount = 2;
+ pipelineLayoutDesc.bindGroupLayouts = bgls;
+
+ pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc);
+
+ utils::ComboRenderPipelineDescriptor descriptor = MakeRenderPipelineDescriptor();
+ pipeline = device.CreateRenderPipeline(&descriptor);
+
+ float data[4];
+ dawn::Buffer buffer = utils::CreateBufferFromData(device, data, 4 * sizeof(float),
+ dawn::BufferUsageBit::Uniform);
+
+ constexpr static float kVertices[] = {-1.f, 1.f, 1.f, -1.f, -1.f, 1.f};
+
+ vertexBuffer = utils::CreateBufferFromData(device, kVertices, sizeof(kVertices),
+ dawn::BufferUsageBit::Vertex);
+
+ // Dummy storage buffer.
+ dawn::Buffer storageBuffer = utils::CreateBufferFromData(
+ device, kVertices, sizeof(kVertices), dawn::BufferUsageBit::Storage);
+
+ // Vertex buffer with storage usage for testing read+write error usage.
+ vertexStorageBuffer = utils::CreateBufferFromData(
+ device, kVertices, sizeof(kVertices),
+ dawn::BufferUsageBit::Vertex | dawn::BufferUsageBit::Storage);
+
+ bg0 = utils::MakeBindGroup(device, bgls[0], {{0, buffer, 0, 4 * sizeof(float)}});
+ bg1 = utils::MakeBindGroup(
+ device, bgls[1],
+ {{0, buffer, 0, 4 * sizeof(float)}, {1, storageBuffer, 0, sizeof(kVertices)}});
+
+ bg1Vertex = utils::MakeBindGroup(device, bgls[1],
+ {{0, buffer, 0, 4 * sizeof(float)},
+ {1, vertexStorageBuffer, 0, sizeof(kVertices)}});
+ }
+
+ utils::ComboRenderPipelineDescriptor MakeRenderPipelineDescriptor() {
+ utils::ComboRenderPipelineDescriptor descriptor(device);
+ descriptor.layout = pipelineLayout;
+ descriptor.cVertexStage.module = vsModule;
+ descriptor.cFragmentStage.module = fsModule;
+ descriptor.cVertexInput.bufferCount = 1;
+ descriptor.cVertexInput.cBuffers[0].stride = 2 * sizeof(float);
+ descriptor.cVertexInput.cBuffers[0].attributeCount = 1;
+ descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float2;
+
+ return descriptor;
+ }
+
+ dawn::ShaderModule vsModule;
+ dawn::ShaderModule fsModule;
+ dawn::PipelineLayout pipelineLayout;
+ dawn::RenderPipeline pipeline;
+ dawn::Buffer vertexBuffer;
+ dawn::Buffer vertexStorageBuffer;
+ const uint64_t zeroOffset = 0;
+ dawn::BindGroup bg0;
+ dawn::BindGroup bg1;
+ dawn::BindGroup bg1Vertex;
+ };
+
+} // anonymous namespace
+
+// Test creating and encoding an empty render bundle.
+TEST_F(RenderBundleValidationTest, Empty) {
+ DummyRenderPass renderPass(device);
+
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = renderPass.attachmentFormat;
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ commandEncoder.Finish();
+}
+
+// Test executing zero render bundles.
+TEST_F(RenderBundleValidationTest, ZeroBundles) {
+ DummyRenderPass renderPass(device);
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(0, nullptr);
+ pass.EndPass();
+ commandEncoder.Finish();
+}
+
+// Test successfully creating and encoding a render bundle into a command buffer.
+TEST_F(RenderBundleValidationTest, SimpleSuccess) {
+ DummyRenderPass renderPass(device);
+
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = renderPass.attachmentFormat;
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1, 0, nullptr);
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ renderBundleEncoder.Draw(3, 0, 0, 0);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ commandEncoder.Finish();
+}
+
+// Test render bundles do not inherit command buffer state
+TEST_F(RenderBundleValidationTest, StateInheritance) {
+ DummyRenderPass renderPass(device);
+
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = renderPass.attachmentFormat;
+
+ // Render bundle does not inherit pipeline so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+
+ pass.SetPipeline(pipeline);
+
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1, 0, nullptr);
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ renderBundleEncoder.Draw(3, 0, 0, 0);
+ ASSERT_DEVICE_ERROR(dawn::RenderBundle renderBundle = renderBundleEncoder.Finish());
+
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle does not inherit bind groups so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ renderBundleEncoder.Draw(3, 0, 0, 0);
+ ASSERT_DEVICE_ERROR(dawn::RenderBundle renderBundle = renderBundleEncoder.Finish());
+
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle does not inherit pipeline and bind groups so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ renderBundleEncoder.Draw(3, 0, 0, 0);
+ ASSERT_DEVICE_ERROR(dawn::RenderBundle renderBundle = renderBundleEncoder.Finish());
+
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle does not inherit buffers so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1, 0, nullptr);
+ renderBundleEncoder.Draw(3, 0, 0, 0);
+ ASSERT_DEVICE_ERROR(dawn::RenderBundle renderBundle = renderBundleEncoder.Finish());
+
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+}
+
+// Test render bundles do not persist command buffer state
+TEST_F(RenderBundleValidationTest, StatePersistence) {
+ DummyRenderPass renderPass(device);
+
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = renderPass.attachmentFormat;
+
+ // Render bundle does not persist pipeline so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.Draw(3, 0, 0, 0);
+ pass.EndPass();
+
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle does not persist bind groups so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1, 0, nullptr);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.SetPipeline(pipeline);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.Draw(3, 0, 0, 0);
+ pass.EndPass();
+
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle does not persist pipeline and bind groups so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1, 0, nullptr);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.Draw(3, 0, 0, 0);
+ pass.EndPass();
+
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle does not persist buffers so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+ pass.Draw(3, 0, 0, 0);
+ pass.EndPass();
+
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+}
+
+// Test executing render bundles clears command buffer state
+TEST_F(RenderBundleValidationTest, ClearsState) {
+ DummyRenderPass renderPass(device);
+
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = renderPass.attachmentFormat;
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ // Render bundle clears pipeline so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ pass.SetPipeline(pipeline);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.Draw(3, 0, 0, 0);
+ pass.EndPass();
+
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle clears bind groups so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.SetPipeline(pipeline);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.Draw(3, 0, 0, 0);
+ pass.EndPass();
+
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle clears pipeline and bind groups so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.Draw(3, 0, 0, 0);
+ pass.EndPass();
+
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Render bundle clears buffers so the draw is invalid.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+ pass.Draw(3, 0, 0, 0);
+ pass.EndPass();
+
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Test executing 0 bundles does not clear command buffer state.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.ExecuteBundles(0, nullptr);
+ pass.Draw(3, 0, 0, 0);
+
+ pass.EndPass();
+ commandEncoder.Finish();
+ }
+}
+
+// Test creating and encoding multiple render bundles.
+TEST_F(RenderBundleValidationTest, MultipleBundles) {
+ DummyRenderPass renderPass(device);
+
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = renderPass.attachmentFormat;
+
+ dawn::RenderBundle renderBundles[2] = {};
+
+ dawn::RenderBundleEncoder renderBundleEncoder0 = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder0.SetPipeline(pipeline);
+ renderBundleEncoder0.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder0.SetBindGroup(1, bg1, 0, nullptr);
+ renderBundleEncoder0.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ renderBundleEncoder0.Draw(3, 1, 0, 0);
+ renderBundles[0] = renderBundleEncoder0.Finish();
+
+ dawn::RenderBundleEncoder renderBundleEncoder1 = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder1.SetPipeline(pipeline);
+ renderBundleEncoder1.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder1.SetBindGroup(1, bg1, 0, nullptr);
+ renderBundleEncoder1.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ renderBundleEncoder1.Draw(3, 1, 0, 0);
+ renderBundles[1] = renderBundleEncoder1.Finish();
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(2, renderBundles);
+ pass.EndPass();
+ commandEncoder.Finish();
+}
+
+// Test that is is valid to execute a render bundle more than once.
+TEST_F(RenderBundleValidationTest, ExecuteMultipleTimes) {
+ DummyRenderPass renderPass(device);
+
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = renderPass.attachmentFormat;
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1, 0, nullptr);
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ renderBundleEncoder.Draw(3, 1, 0, 0);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ commandEncoder.Finish();
+}
+
+// Test that it is an error to call Finish() on a render bundle encoder twice.
+TEST_F(RenderBundleValidationTest, FinishTwice) {
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = dawn::TextureFormat::RGBA8Uint;
+
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.Finish();
+ ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish());
+}
+
+// Test that it is invalid to create a render bundle with no texture formats
+TEST_F(RenderBundleValidationTest, RequiresAtLeastOneTextureFormat) {
+ // Test failure case.
+ {
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc));
+ }
+
+ // Test success with one color format.
+ {
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = dawn::TextureFormat::RGBA8Uint;
+ device.CreateRenderBundleEncoder(&desc);
+ }
+
+ // Test success with a depth stencil format.
+ {
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.cDepthStencilFormat = dawn::TextureFormat::Depth24PlusStencil8;
+ desc.depthStencilFormat = &desc.cDepthStencilFormat;
+ device.CreateRenderBundleEncoder(&desc);
+ }
+}
+
+// Test that resource usages are validated inside render bundles.
+TEST_F(RenderBundleValidationTest, UsageTracking) {
+ DummyRenderPass renderPass(device);
+
+ utils::ComboRenderBundleEncoderDescriptor desc = {};
+ desc.colorFormatsCount = 1;
+ desc.cColorFormats[0] = renderPass.attachmentFormat;
+
+ dawn::RenderBundle renderBundle0;
+ dawn::RenderBundle renderBundle1;
+
+ // First base case is successful. |bg1Vertex| does not reference |vertexBuffer|.
+ {
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1Vertex, 0, nullptr);
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ renderBundleEncoder.Draw(3, 0, 0, 0);
+ renderBundle0 = renderBundleEncoder.Finish();
+ }
+
+ // Second base case is successful. |bg1| does not reference |vertexStorageBuffer|
+ {
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1, 0, nullptr);
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexStorageBuffer, &zeroOffset);
+ renderBundleEncoder.Draw(3, 0, 0, 0);
+ renderBundle1 = renderBundleEncoder.Finish();
+ }
+
+ // Test that a render bundle which sets a buffer as both vertex and storage is invalid.
+ // |bg1Vertex| references |vertexStorageBuffer|
+ {
+ dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.SetBindGroup(0, bg0, 0, nullptr);
+ renderBundleEncoder.SetBindGroup(1, bg1Vertex, 0, nullptr);
+ renderBundleEncoder.SetVertexBuffers(0, 1, &vertexStorageBuffer, &zeroOffset);
+ renderBundleEncoder.Draw(3, 0, 0, 0);
+ ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish());
+ }
+
+ // When both render bundles are in the same pass, |vertexStorageBuffer| is used
+ // as both read and write usage. This is invalid.
+ // renderBundle0 uses |vertexStorageBuffer| as a storage buffer.
+ // renderBundle1 uses |vertexStorageBuffer| as a vertex buffer.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle0);
+ pass.ExecuteBundles(1, &renderBundle1);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // |vertexStorageBuffer| is used as both read and write usage. This is invalid.
+ // The render pass uses |vertexStorageBuffer| as a storage buffer.
+ // renderBundle1 uses |vertexStorageBuffer| as a vertex buffer.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1Vertex, 0, nullptr);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.Draw(3, 0, 0, 0);
+
+ pass.ExecuteBundles(1, &renderBundle1);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // |vertexStorageBuffer| is used as both read and write usage. This is invalid.
+ // renderBundle0 uses |vertexStorageBuffer| as a storage buffer.
+ // The render pass uses |vertexStorageBuffer| as a vertex buffer.
+ {
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+
+ pass.ExecuteBundles(1, &renderBundle0);
+
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg0, 0, nullptr);
+ pass.SetBindGroup(1, bg1, 0, nullptr);
+ pass.SetVertexBuffers(0, 1, &vertexStorageBuffer, &zeroOffset);
+ pass.Draw(3, 0, 0, 0);
+
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+}
+
+// Test that encoding SetPipline with an incompatible color format produces an error.
+TEST_F(RenderBundleValidationTest, PipelineColorFormatMismatch) {
+ utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
+ renderBundleDesc.colorFormatsCount = 3;
+ renderBundleDesc.cColorFormats[0] = dawn::TextureFormat::RGBA8Unorm;
+ renderBundleDesc.cColorFormats[1] = dawn::TextureFormat::RG16Float;
+ renderBundleDesc.cColorFormats[2] = dawn::TextureFormat::R16Sint;
+
+ utils::ComboRenderPipelineDescriptor renderPipelineDesc = MakeRenderPipelineDescriptor();
+ renderPipelineDesc.colorStateCount = 3;
+ renderPipelineDesc.cColorStates[0]->format = dawn::TextureFormat::RGBA8Unorm;
+ renderPipelineDesc.cColorStates[1]->format = dawn::TextureFormat::RG16Float;
+ renderPipelineDesc.cColorStates[2]->format = dawn::TextureFormat::R16Sint;
+
+ // Test the success case.
+ {
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.Finish();
+ }
+
+ // Test the failure case for mismatched format types.
+ {
+ utils::ComboRenderPipelineDescriptor desc = renderPipelineDesc;
+ desc.cColorStates[1]->format = dawn::TextureFormat::RGBA8Unorm;
+
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish());
+ }
+
+ // Test the failure case for missing format
+ {
+ utils::ComboRenderPipelineDescriptor desc = renderPipelineDesc;
+ desc.colorStateCount = 2;
+
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish());
+ }
+}
+
+// Test that encoding SetPipline with an incompatible depth stencil format produces an error.
+TEST_F(RenderBundleValidationTest, PipelineDepthStencilFormatMismatch) {
+ utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
+ renderBundleDesc.colorFormatsCount = 1;
+ renderBundleDesc.cColorFormats[0] = dawn::TextureFormat::RGBA8Unorm;
+ renderBundleDesc.cDepthStencilFormat = dawn::TextureFormat::Depth24PlusStencil8;
+ renderBundleDesc.depthStencilFormat = &renderBundleDesc.cDepthStencilFormat;
+
+ utils::ComboRenderPipelineDescriptor renderPipelineDesc = MakeRenderPipelineDescriptor();
+ renderPipelineDesc.colorStateCount = 1;
+ renderPipelineDesc.cColorStates[0]->format = dawn::TextureFormat::RGBA8Unorm;
+ renderPipelineDesc.depthStencilState = &renderPipelineDesc.cDepthStencilState;
+ renderPipelineDesc.cDepthStencilState.format = dawn::TextureFormat::Depth24PlusStencil8;
+
+ // Test the success case.
+ {
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.Finish();
+ }
+
+ // Test the failure case for mismatched format.
+ {
+ utils::ComboRenderPipelineDescriptor desc = renderPipelineDesc;
+ desc.cDepthStencilState.format = dawn::TextureFormat::Depth24Plus;
+ desc.depthStencilState = &desc.cDepthStencilState;
+
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish());
+ }
+
+ // Test the failure case for missing format.
+ {
+ utils::ComboRenderPipelineDescriptor desc = renderPipelineDesc;
+ desc.depthStencilState = nullptr;
+
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&desc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish());
+ }
+}
+
+// Test that encoding SetPipline with an incompatible sample count produces an error.
+TEST_F(RenderBundleValidationTest, PipelineSampleCountMismatch) {
+ utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
+ renderBundleDesc.colorFormatsCount = 1;
+ renderBundleDesc.cColorFormats[0] = dawn::TextureFormat::RGBA8Unorm;
+ renderBundleDesc.sampleCount = 4;
+
+ utils::ComboRenderPipelineDescriptor renderPipelineDesc = MakeRenderPipelineDescriptor();
+ renderPipelineDesc.colorStateCount = 1;
+ renderPipelineDesc.cColorStates[0]->format = dawn::TextureFormat::RGBA8Unorm;
+ renderPipelineDesc.sampleCount = 4;
+
+ // Test the success case.
+ {
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ renderBundleEncoder.Finish();
+ }
+
+ // Test the failure case.
+ {
+ renderPipelineDesc.sampleCount = 1;
+
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc);
+ renderBundleEncoder.SetPipeline(pipeline);
+ ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish());
+ }
+}
+
+// Test that encoding ExecuteBundles with an incompatible color format produces an error.
+TEST_F(RenderBundleValidationTest, RenderPassColorFormatMismatch) {
+ utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
+ renderBundleDesc.colorFormatsCount = 3;
+ renderBundleDesc.cColorFormats[0] = dawn::TextureFormat::RGBA8Unorm;
+ renderBundleDesc.cColorFormats[1] = dawn::TextureFormat::RG16Float;
+ renderBundleDesc.cColorFormats[2] = dawn::TextureFormat::R16Sint;
+
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ dawn::TextureDescriptor textureDesc = {};
+ textureDesc.usage = dawn::TextureUsageBit::OutputAttachment;
+ textureDesc.size = dawn::Extent3D({400, 400, 1});
+
+ textureDesc.format = dawn::TextureFormat::RGBA8Unorm;
+ dawn::Texture tex0 = device.CreateTexture(&textureDesc);
+
+ textureDesc.format = dawn::TextureFormat::RG16Float;
+ dawn::Texture tex1 = device.CreateTexture(&textureDesc);
+
+ textureDesc.format = dawn::TextureFormat::R16Sint;
+ dawn::Texture tex2 = device.CreateTexture(&textureDesc);
+
+ // Test the success case
+ {
+ utils::ComboRenderPassDescriptor renderPass({
+ tex0.CreateDefaultView(),
+ tex1.CreateDefaultView(),
+ tex2.CreateDefaultView(),
+ });
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ commandEncoder.Finish();
+ }
+
+ // Test the failure case for mismatched format
+ {
+ utils::ComboRenderPassDescriptor renderPass({
+ tex0.CreateDefaultView(),
+ tex1.CreateDefaultView(),
+ tex0.CreateDefaultView(),
+ });
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Test the failure case for missing format
+ {
+ utils::ComboRenderPassDescriptor renderPass({
+ tex0.CreateDefaultView(),
+ tex1.CreateDefaultView(),
+ });
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+}
+
+// Test that encoding ExecuteBundles with an incompatible depth stencil format produces an
+// error.
+TEST_F(RenderBundleValidationTest, RenderPassDepthStencilFormatMismatch) {
+ utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
+ renderBundleDesc.colorFormatsCount = 1;
+ renderBundleDesc.cColorFormats[0] = dawn::TextureFormat::RGBA8Unorm;
+ renderBundleDesc.cDepthStencilFormat = dawn::TextureFormat::Depth24Plus;
+ renderBundleDesc.depthStencilFormat = &renderBundleDesc.cDepthStencilFormat;
+
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ dawn::TextureDescriptor textureDesc = {};
+ textureDesc.usage = dawn::TextureUsageBit::OutputAttachment;
+ textureDesc.size = dawn::Extent3D({400, 400, 1});
+
+ textureDesc.format = dawn::TextureFormat::RGBA8Unorm;
+ dawn::Texture tex0 = device.CreateTexture(&textureDesc);
+
+ textureDesc.format = dawn::TextureFormat::Depth24Plus;
+ dawn::Texture tex1 = device.CreateTexture(&textureDesc);
+
+ textureDesc.format = dawn::TextureFormat::Depth32Float;
+ dawn::Texture tex2 = device.CreateTexture(&textureDesc);
+
+ // Test the success case
+ {
+ utils::ComboRenderPassDescriptor renderPass({tex0.CreateDefaultView()},
+ tex1.CreateDefaultView());
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ commandEncoder.Finish();
+ }
+
+ // Test the failure case for mismatched format
+ {
+ utils::ComboRenderPassDescriptor renderPass({tex0.CreateDefaultView()},
+ tex2.CreateDefaultView());
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+
+ // Test the failure case for missing format
+ {
+ utils::ComboRenderPassDescriptor renderPass({tex0.CreateDefaultView()});
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+}
+
+// Test that encoding ExecuteBundles with an incompatible sample count produces an error.
+TEST_F(RenderBundleValidationTest, RenderPassSampleCountMismatch) {
+ utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
+ renderBundleDesc.colorFormatsCount = 1;
+ renderBundleDesc.cColorFormats[0] = dawn::TextureFormat::RGBA8Unorm;
+
+ dawn::RenderBundleEncoder renderBundleEncoder =
+ device.CreateRenderBundleEncoder(&renderBundleDesc);
+ dawn::RenderBundle renderBundle = renderBundleEncoder.Finish();
+
+ dawn::TextureDescriptor textureDesc = {};
+ textureDesc.usage = dawn::TextureUsageBit::OutputAttachment;
+ textureDesc.size = dawn::Extent3D({400, 400, 1});
+
+ textureDesc.format = dawn::TextureFormat::RGBA8Unorm;
+ dawn::Texture tex0 = device.CreateTexture(&textureDesc);
+
+ textureDesc.sampleCount = 4;
+ dawn::Texture tex1 = device.CreateTexture(&textureDesc);
+
+ // Test the success case
+ {
+ utils::ComboRenderPassDescriptor renderPass({tex0.CreateDefaultView()});
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ commandEncoder.Finish();
+ }
+
+ // Test the failure case
+ {
+ utils::ComboRenderPassDescriptor renderPass({tex1.CreateDefaultView()});
+
+ dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
+ pass.ExecuteBundles(1, &renderBundle);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+ }
+}
diff --git a/src/utils/ComboRenderBundleEncoderDescriptor.cpp b/src/utils/ComboRenderBundleEncoderDescriptor.cpp
new file mode 100644
index 0000000..86e1f26
--- /dev/null
+++ b/src/utils/ComboRenderBundleEncoderDescriptor.cpp
@@ -0,0 +1,30 @@
+// Copyright 2019 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 "utils/ComboRenderBundleEncoderDescriptor.h"
+
+#include "utils/DawnHelpers.h"
+
+namespace utils {
+
+ ComboRenderBundleEncoderDescriptor::ComboRenderBundleEncoderDescriptor() {
+ dawn::RenderBundleEncoderDescriptor* descriptor = this;
+
+ descriptor->sampleCount = 1;
+ descriptor->depthStencilFormat = nullptr;
+ descriptor->colorFormatsCount = 0;
+ descriptor->colorFormats = &cColorFormats[0];
+ }
+
+} // namespace utils
diff --git a/src/utils/ComboRenderBundleEncoderDescriptor.h b/src/utils/ComboRenderBundleEncoderDescriptor.h
new file mode 100644
index 0000000..e829693
--- /dev/null
+++ b/src/utils/ComboRenderBundleEncoderDescriptor.h
@@ -0,0 +1,36 @@
+// Copyright 2019 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.
+
+#ifndef UTILS_COMBORENDERBUNDLEENCODERDESCRIPTOR_H_
+#define UTILS_COMBORENDERBUNDLEENCODERDESCRIPTOR_H_
+
+#include <dawn/dawncpp.h>
+
+#include "common/Constants.h"
+
+#include <array>
+
+namespace utils {
+
+ class ComboRenderBundleEncoderDescriptor : public dawn::RenderBundleEncoderDescriptor {
+ public:
+ ComboRenderBundleEncoderDescriptor();
+
+ std::array<dawn::TextureFormat, kMaxColorAttachments> cColorFormats;
+ dawn::TextureFormat cDepthStencilFormat;
+ };
+
+} // namespace utils
+
+#endif // UTILS_COMBORENDERBUNDLEENCODERDESCRIPTOR_H_