|  | // 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/WGPUHelpers.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class RenderBundleValidationTest : public ValidationTest { | 
|  | protected: | 
|  | void SetUp() override { | 
|  | ValidationTest::SetUp(); | 
|  |  | 
|  | vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::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::SingleShaderStage::Fragment, R"( | 
|  | #version 450 | 
|  | layout (set = 1, binding = 0) uniform fragmentUniformBuffer { | 
|  | vec4 color; | 
|  | }; | 
|  | layout (set = 1, binding = 1) buffer storageBuffer { | 
|  | float dummy[]; | 
|  | }; | 
|  | void main() { | 
|  | })"); | 
|  |  | 
|  | wgpu::BindGroupLayout bgls[] = { | 
|  | utils::MakeBindGroupLayout( | 
|  | device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}), | 
|  | utils::MakeBindGroupLayout( | 
|  | device, { | 
|  | {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}, | 
|  | {1, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}, | 
|  | })}; | 
|  |  | 
|  | wgpu::PipelineLayoutDescriptor pipelineLayoutDesc = {}; | 
|  | pipelineLayoutDesc.bindGroupLayoutCount = 2; | 
|  | pipelineLayoutDesc.bindGroupLayouts = bgls; | 
|  |  | 
|  | pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc); | 
|  |  | 
|  | utils::ComboRenderPipelineDescriptor descriptor(device); | 
|  | InitializeRenderPipelineDescriptor(&descriptor); | 
|  | pipeline = device.CreateRenderPipeline(&descriptor); | 
|  |  | 
|  | float data[4]; | 
|  | wgpu::Buffer buffer = utils::CreateBufferFromData(device, data, 4 * sizeof(float), | 
|  | wgpu::BufferUsage::Uniform); | 
|  |  | 
|  | constexpr static float kVertices[] = {-1.f, 1.f, 1.f, -1.f, -1.f, 1.f}; | 
|  |  | 
|  | vertexBuffer = utils::CreateBufferFromData(device, kVertices, sizeof(kVertices), | 
|  | wgpu::BufferUsage::Vertex); | 
|  |  | 
|  | // Dummy storage buffer. | 
|  | wgpu::Buffer storageBuffer = utils::CreateBufferFromData( | 
|  | device, kVertices, sizeof(kVertices), wgpu::BufferUsage::Storage); | 
|  |  | 
|  | // Vertex buffer with storage usage for testing read+write error usage. | 
|  | vertexStorageBuffer = | 
|  | utils::CreateBufferFromData(device, kVertices, sizeof(kVertices), | 
|  | wgpu::BufferUsage::Vertex | wgpu::BufferUsage::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)}}); | 
|  | } | 
|  |  | 
|  | void InitializeRenderPipelineDescriptor(utils::ComboRenderPipelineDescriptor* descriptor) { | 
|  | descriptor->layout = pipelineLayout; | 
|  | descriptor->vertexStage.module = vsModule; | 
|  | descriptor->cFragmentStage.module = fsModule; | 
|  | descriptor->cVertexState.vertexBufferCount = 1; | 
|  | descriptor->cVertexState.cVertexBuffers[0].arrayStride = 2 * sizeof(float); | 
|  | descriptor->cVertexState.cVertexBuffers[0].attributeCount = 1; | 
|  | descriptor->cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float2; | 
|  | descriptor->cVertexState.cAttributes[0].shaderLocation = 0; | 
|  | } | 
|  |  | 
|  | wgpu::ShaderModule vsModule; | 
|  | wgpu::ShaderModule fsModule; | 
|  | wgpu::PipelineLayout pipelineLayout; | 
|  | wgpu::RenderPipeline pipeline; | 
|  | wgpu::Buffer vertexBuffer; | 
|  | wgpu::Buffer vertexStorageBuffer; | 
|  | wgpu::BindGroup bg0; | 
|  | wgpu::BindGroup bg1; | 
|  | wgpu::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; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1); | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); | 
|  | renderBundleEncoder.Draw(3, 0, 0, 0); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.EndPass(); | 
|  | commandEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test that render bundle debug groups must be well nested. | 
|  | TEST_F(RenderBundleValidationTest, DebugGroups) { | 
|  | DummyRenderPass renderPass(device); | 
|  |  | 
|  | utils::ComboRenderBundleEncoderDescriptor desc = {}; | 
|  | desc.colorFormatsCount = 1; | 
|  | desc.cColorFormats[0] = renderPass.attachmentFormat; | 
|  |  | 
|  | // Test a single debug group works. | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.PushDebugGroup("group"); | 
|  | renderBundleEncoder.PopDebugGroup(); | 
|  | renderBundleEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test nested debug groups work. | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.PushDebugGroup("group"); | 
|  | renderBundleEncoder.PushDebugGroup("group2"); | 
|  | renderBundleEncoder.PopDebugGroup(); | 
|  | renderBundleEncoder.PopDebugGroup(); | 
|  | renderBundleEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test popping when no group is pushed is invalid. | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.PopDebugGroup(); | 
|  | ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Test popping too many times is invalid. | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.PushDebugGroup("group"); | 
|  | renderBundleEncoder.PopDebugGroup(); | 
|  | renderBundleEncoder.PopDebugGroup(); | 
|  | ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Test that a single debug group must be popped. | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.PushDebugGroup("group"); | 
|  | ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Test that all debug groups must be popped. | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.PushDebugGroup("group"); | 
|  | renderBundleEncoder.PushDebugGroup("group2"); | 
|  | renderBundleEncoder.PopDebugGroup(); | 
|  | ASSERT_DEVICE_ERROR(renderBundleEncoder.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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  |  | 
|  | pass.SetPipeline(pipeline); | 
|  |  | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1); | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); | 
|  | renderBundleEncoder.Draw(3, 0, 0, 0); | 
|  | ASSERT_DEVICE_ERROR(wgpu::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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  |  | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  |  | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); | 
|  | renderBundleEncoder.Draw(3, 0, 0, 0); | 
|  | ASSERT_DEVICE_ERROR(wgpu::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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  |  | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  |  | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); | 
|  | renderBundleEncoder.Draw(3, 0, 0, 0); | 
|  | ASSERT_DEVICE_ERROR(wgpu::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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  |  | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  |  | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1); | 
|  | renderBundleEncoder.Draw(3, 0, 0, 0); | 
|  | ASSERT_DEVICE_ERROR(wgpu::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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | 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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | 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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | pass.Draw(3, 0, 0, 0); | 
|  | pass.EndPass(); | 
|  |  | 
|  | ASSERT_DEVICE_ERROR(commandEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Render bundle does not persist buffers so the draw is invalid. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  | 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; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | // Render bundle clears pipeline so the draw is invalid. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | pass.Draw(3, 0, 0, 0); | 
|  | pass.EndPass(); | 
|  |  | 
|  | ASSERT_DEVICE_ERROR(commandEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Render bundle clears bind groups so the draw is invalid. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | 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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | pass.Draw(3, 0, 0, 0); | 
|  | pass.EndPass(); | 
|  |  | 
|  | ASSERT_DEVICE_ERROR(commandEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Render bundle clears buffers so the draw is invalid. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  | pass.Draw(3, 0, 0, 0); | 
|  | pass.EndPass(); | 
|  |  | 
|  | ASSERT_DEVICE_ERROR(commandEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Test executing 0 bundles does not clear command buffer state. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | 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; | 
|  |  | 
|  | wgpu::RenderBundle renderBundles[2] = {}; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder0 = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder0.SetPipeline(pipeline); | 
|  | renderBundleEncoder0.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder0.SetBindGroup(1, bg1); | 
|  | renderBundleEncoder0.SetVertexBuffer(0, vertexBuffer); | 
|  | renderBundleEncoder0.Draw(3, 1, 0, 0); | 
|  | renderBundles[0] = renderBundleEncoder0.Finish(); | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder1 = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder1.SetPipeline(pipeline); | 
|  | renderBundleEncoder1.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder1.SetBindGroup(1, bg1); | 
|  | renderBundleEncoder1.SetVertexBuffer(0, vertexBuffer); | 
|  | renderBundleEncoder1.Draw(3, 1, 0, 0); | 
|  | renderBundles[1] = renderBundleEncoder1.Finish(); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1); | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); | 
|  | renderBundleEncoder.Draw(3, 1, 0, 0); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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] = wgpu::TextureFormat::RGBA8Uint; | 
|  |  | 
|  | wgpu::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] = wgpu::TextureFormat::RGBA8Uint; | 
|  | device.CreateRenderBundleEncoder(&desc); | 
|  | } | 
|  |  | 
|  | // Test success with a depth stencil format. | 
|  | { | 
|  | utils::ComboRenderBundleEncoderDescriptor desc = {}; | 
|  | desc.depthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; | 
|  | device.CreateRenderBundleEncoder(&desc); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test that render bundle color formats cannot be set to undefined. | 
|  | TEST_F(RenderBundleValidationTest, ColorFormatUndefined) { | 
|  | utils::ComboRenderBundleEncoderDescriptor desc = {}; | 
|  | desc.colorFormatsCount = 1; | 
|  | desc.cColorFormats[0] = wgpu::TextureFormat::Undefined; | 
|  | ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); | 
|  | } | 
|  |  | 
|  | // Test that the render bundle depth stencil format cannot be set to undefined. | 
|  | TEST_F(RenderBundleValidationTest, DepthStencilFormatUndefined) { | 
|  | utils::ComboRenderBundleEncoderDescriptor desc = {}; | 
|  | desc.depthStencilFormat = wgpu::TextureFormat::Undefined; | 
|  | ASSERT_DEVICE_ERROR(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; | 
|  |  | 
|  | wgpu::RenderBundle renderBundle0; | 
|  | wgpu::RenderBundle renderBundle1; | 
|  |  | 
|  | // First base case is successful. |bg1Vertex| does not reference |vertexBuffer|. | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1Vertex); | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); | 
|  | renderBundleEncoder.Draw(3, 0, 0, 0); | 
|  | renderBundle0 = renderBundleEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Second base case is successful. |bg1| does not reference |vertexStorageBuffer| | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1); | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexStorageBuffer); | 
|  | 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| | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.SetBindGroup(0, bg0); | 
|  | renderBundleEncoder.SetBindGroup(1, bg1Vertex); | 
|  | renderBundleEncoder.SetVertexBuffer(0, vertexStorageBuffer); | 
|  | 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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1Vertex); | 
|  | pass.SetVertexBuffer(0, vertexBuffer); | 
|  | 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. | 
|  | { | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  |  | 
|  | pass.ExecuteBundles(1, &renderBundle0); | 
|  |  | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetBindGroup(0, bg0); | 
|  | pass.SetBindGroup(1, bg1); | 
|  | pass.SetVertexBuffer(0, vertexStorageBuffer); | 
|  | 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] = wgpu::TextureFormat::RGBA8Unorm; | 
|  | renderBundleDesc.cColorFormats[1] = wgpu::TextureFormat::RG16Float; | 
|  | renderBundleDesc.cColorFormats[2] = wgpu::TextureFormat::R16Sint; | 
|  |  | 
|  | auto SetupRenderPipelineDescForTest = [this](utils::ComboRenderPipelineDescriptor* desc) { | 
|  | InitializeRenderPipelineDescriptor(desc); | 
|  | desc->colorStateCount = 3; | 
|  | desc->cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; | 
|  | desc->cColorStates[1].format = wgpu::TextureFormat::RG16Float; | 
|  | desc->cColorStates[2].format = wgpu::TextureFormat::R16Sint; | 
|  | }; | 
|  |  | 
|  | // Test the success case. | 
|  | { | 
|  | utils::ComboRenderPipelineDescriptor desc(device); | 
|  | SetupRenderPipelineDescForTest(&desc); | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test the failure case for mismatched format types. | 
|  | { | 
|  | utils::ComboRenderPipelineDescriptor desc(device); | 
|  | SetupRenderPipelineDescForTest(&desc); | 
|  | desc.cColorStates[1].format = wgpu::TextureFormat::RGBA8Unorm; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Test the failure case for missing format | 
|  | { | 
|  | utils::ComboRenderPipelineDescriptor desc(device); | 
|  | SetupRenderPipelineDescForTest(&desc); | 
|  | desc.colorStateCount = 2; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::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] = wgpu::TextureFormat::RGBA8Unorm; | 
|  | renderBundleDesc.depthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; | 
|  |  | 
|  | auto SetupRenderPipelineDescForTest = [this](utils::ComboRenderPipelineDescriptor* desc) { | 
|  | InitializeRenderPipelineDescriptor(desc); | 
|  | desc->colorStateCount = 1; | 
|  | desc->cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; | 
|  | desc->depthStencilState = &desc->cDepthStencilState; | 
|  | desc->cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; | 
|  | }; | 
|  |  | 
|  | // Test the success case. | 
|  | { | 
|  | utils::ComboRenderPipelineDescriptor desc(device); | 
|  | SetupRenderPipelineDescForTest(&desc); | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test the failure case for mismatched format. | 
|  | { | 
|  | utils::ComboRenderPipelineDescriptor desc(device); | 
|  | SetupRenderPipelineDescForTest(&desc); | 
|  | desc.cDepthStencilState.format = wgpu::TextureFormat::Depth24Plus; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); | 
|  | } | 
|  |  | 
|  | // Test the failure case for missing format. | 
|  | { | 
|  | utils::ComboRenderPipelineDescriptor desc(device); | 
|  | SetupRenderPipelineDescForTest(&desc); | 
|  | desc.depthStencilState = nullptr; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::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] = wgpu::TextureFormat::RGBA8Unorm; | 
|  | renderBundleDesc.sampleCount = 4; | 
|  |  | 
|  | utils::ComboRenderPipelineDescriptor renderPipelineDesc(device); | 
|  | InitializeRenderPipelineDescriptor(&renderPipelineDesc); | 
|  | renderPipelineDesc.colorStateCount = 1; | 
|  | renderPipelineDesc.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; | 
|  | renderPipelineDesc.sampleCount = 4; | 
|  |  | 
|  | // Test the success case. | 
|  | { | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc); | 
|  | renderBundleEncoder.SetPipeline(pipeline); | 
|  | renderBundleEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test the failure case. | 
|  | { | 
|  | renderPipelineDesc.sampleCount = 1; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::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] = wgpu::TextureFormat::RGBA8Unorm; | 
|  | renderBundleDesc.cColorFormats[1] = wgpu::TextureFormat::RG16Float; | 
|  | renderBundleDesc.cColorFormats[2] = wgpu::TextureFormat::R16Sint; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | wgpu::TextureDescriptor textureDesc = {}; | 
|  | textureDesc.usage = wgpu::TextureUsage::OutputAttachment; | 
|  | textureDesc.size = wgpu::Extent3D({400, 400, 1}); | 
|  |  | 
|  | textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; | 
|  | wgpu::Texture tex0 = device.CreateTexture(&textureDesc); | 
|  |  | 
|  | textureDesc.format = wgpu::TextureFormat::RG16Float; | 
|  | wgpu::Texture tex1 = device.CreateTexture(&textureDesc); | 
|  |  | 
|  | textureDesc.format = wgpu::TextureFormat::R16Sint; | 
|  | wgpu::Texture tex2 = device.CreateTexture(&textureDesc); | 
|  |  | 
|  | // Test the success case | 
|  | { | 
|  | utils::ComboRenderPassDescriptor renderPass({ | 
|  | tex0.CreateView(), | 
|  | tex1.CreateView(), | 
|  | tex2.CreateView(), | 
|  | }); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.EndPass(); | 
|  | commandEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test the failure case for mismatched format | 
|  | { | 
|  | utils::ComboRenderPassDescriptor renderPass({ | 
|  | tex0.CreateView(), | 
|  | tex1.CreateView(), | 
|  | tex0.CreateView(), | 
|  | }); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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.CreateView(), | 
|  | tex1.CreateView(), | 
|  | }); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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] = wgpu::TextureFormat::RGBA8Unorm; | 
|  | renderBundleDesc.depthStencilFormat = wgpu::TextureFormat::Depth24Plus; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | wgpu::TextureDescriptor textureDesc = {}; | 
|  | textureDesc.usage = wgpu::TextureUsage::OutputAttachment; | 
|  | textureDesc.size = wgpu::Extent3D({400, 400, 1}); | 
|  |  | 
|  | textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; | 
|  | wgpu::Texture tex0 = device.CreateTexture(&textureDesc); | 
|  |  | 
|  | textureDesc.format = wgpu::TextureFormat::Depth24Plus; | 
|  | wgpu::Texture tex1 = device.CreateTexture(&textureDesc); | 
|  |  | 
|  | textureDesc.format = wgpu::TextureFormat::Depth32Float; | 
|  | wgpu::Texture tex2 = device.CreateTexture(&textureDesc); | 
|  |  | 
|  | // Test the success case | 
|  | { | 
|  | utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}, tex1.CreateView()); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.EndPass(); | 
|  | commandEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test the failure case for mismatched format | 
|  | { | 
|  | utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}, tex2.CreateView()); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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.CreateView()}); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::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] = wgpu::TextureFormat::RGBA8Unorm; | 
|  |  | 
|  | wgpu::RenderBundleEncoder renderBundleEncoder = | 
|  | device.CreateRenderBundleEncoder(&renderBundleDesc); | 
|  | wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); | 
|  |  | 
|  | wgpu::TextureDescriptor textureDesc = {}; | 
|  | textureDesc.usage = wgpu::TextureUsage::OutputAttachment; | 
|  | textureDesc.size = wgpu::Extent3D({400, 400, 1}); | 
|  |  | 
|  | textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; | 
|  | wgpu::Texture tex0 = device.CreateTexture(&textureDesc); | 
|  |  | 
|  | textureDesc.sampleCount = 4; | 
|  | wgpu::Texture tex1 = device.CreateTexture(&textureDesc); | 
|  |  | 
|  | // Test the success case | 
|  | { | 
|  | utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.EndPass(); | 
|  | commandEncoder.Finish(); | 
|  | } | 
|  |  | 
|  | // Test the failure case | 
|  | { | 
|  | utils::ComboRenderPassDescriptor renderPass({tex1.CreateView()}); | 
|  |  | 
|  | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | 
|  | wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); | 
|  | pass.ExecuteBundles(1, &renderBundle); | 
|  | pass.EndPass(); | 
|  | ASSERT_DEVICE_ERROR(commandEncoder.Finish()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test that color attachment texture formats must be color renderable and | 
|  | // depth stencil texture formats must be depth/stencil. | 
|  | TEST_F(RenderBundleValidationTest, TextureFormats) { | 
|  | // Test that color formats are validated as color. | 
|  | { | 
|  | utils::ComboRenderBundleEncoderDescriptor desc = {}; | 
|  | desc.colorFormatsCount = 1; | 
|  | desc.cColorFormats[0] = wgpu::TextureFormat::Depth24PlusStencil8; | 
|  | ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); | 
|  | } | 
|  |  | 
|  | // Test that color formats are validated as renderable. | 
|  | { | 
|  | utils::ComboRenderBundleEncoderDescriptor desc = {}; | 
|  | desc.colorFormatsCount = 1; | 
|  | desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Snorm; | 
|  | ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); | 
|  | } | 
|  |  | 
|  | // Test that depth/stencil formats are validated as depth/stencil. | 
|  | { | 
|  | utils::ComboRenderBundleEncoderDescriptor desc = {}; | 
|  | desc.depthStencilFormat = wgpu::TextureFormat::RGBA8Unorm; | 
|  | ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); | 
|  | } | 
|  |  | 
|  | // Don't test non-renerable depth/stencil formats because we don't have any. | 
|  | } |