// 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::CreateShaderModuleFromWGSL(device, R"( [[location(0)]] var pos : vec2; [[block]] struct S { [[offset(0)]] transform : mat2x2; }; [[set(0), binding(0)]] var uniforms : S; [[stage(vertex)]] fn main() -> void { })"); fsModule = utils::CreateShaderModuleFromWGSL(device, R"( [[block]] struct Uniforms { [[offset(0)]] color : vec4; }; [[set(1), binding(0)]] var uniforms : Uniforms; [[block]] struct Storage { [[offset(0)]] dummy : [[stride(4)]] array; }; [[set(1), binding(1)]] var ssbo : [[access(read_write)]] Storage; [[stage(fragment)]] fn main() -> void { })"); 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[8]; wgpu::Buffer buffer = utils::CreateBufferFromData(device, data, 8 * 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, 8 * 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, 8 * 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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::RenderAttachment; 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::RenderAttachment; 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::RenderAttachment; 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. }