// 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/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" #include "utils/WGPUHelpers.h" constexpr uint32_t kRTSize = 16; constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; class RenderPassTest : public DawnTest { protected: void SetUp() override { DawnTest::SetUp(); // Shaders to draw a bottom-left triangle in blue. mVSModule = utils::CreateShaderModuleFromWGSL(device, R"( [[builtin(vertex_idx)]] var VertexIndex : u32; [[builtin(position)]] var Position : vec4; [[stage(vertex)]] fn main() -> void { const pos : array, 3> = array, 3>( vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2(-1.0, -1.0)); Position = vec4(pos[VertexIndex], 0.0, 1.0); })"); wgpu::ShaderModule fsModule = utils::CreateShaderModuleFromWGSL(device, R"( [[location(0)]] var fragColor : vec4; [[stage(fragment)]] fn main() -> void { fragColor = vec4(0.0, 0.0, 1.0, 1.0); })"); utils::ComboRenderPipelineDescriptor descriptor(device); descriptor.vertexStage.module = mVSModule; descriptor.cFragmentStage.module = fsModule; descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleList; descriptor.cColorStates[0].format = kFormat; pipeline = device.CreateRenderPipeline(&descriptor); } wgpu::Texture CreateDefault2DTexture() { wgpu::TextureDescriptor descriptor; descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = kRTSize; descriptor.size.height = kRTSize; descriptor.size.depth = 1; descriptor.sampleCount = 1; descriptor.format = kFormat; descriptor.mipLevelCount = 1; descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; return device.CreateTexture(&descriptor); } wgpu::ShaderModule mVSModule; wgpu::RenderPipeline pipeline; }; // Test using two different render passes in one commandBuffer works correctly. TEST_P(RenderPassTest, TwoRenderPassesInOneCommandBuffer) { if (IsOpenGL() || IsMetal()) { // crbug.com/950768 // This test is consistently failing on OpenGL and flaky on Metal. return; } wgpu::Texture renderTarget1 = CreateDefault2DTexture(); wgpu::Texture renderTarget2 = CreateDefault2DTexture(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { // In the first render pass we clear renderTarget1 to red and draw a blue triangle in the // bottom left of renderTarget1. utils::ComboRenderPassDescriptor renderPass({renderTarget1.CreateView()}); renderPass.cColorAttachments[0].clearColor = {1.0f, 0.0f, 0.0f, 1.0f}; wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline); pass.Draw(3); pass.EndPass(); } { // In the second render pass we clear renderTarget2 to green and draw a blue triangle in the // bottom left of renderTarget2. utils::ComboRenderPassDescriptor renderPass({renderTarget2.CreateView()}); renderPass.cColorAttachments[0].clearColor = {0.0f, 1.0f, 0.0f, 1.0f}; wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline); pass.Draw(3); pass.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(RGBA8::kBlue, renderTarget1, 1, kRTSize - 1); EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderTarget1, kRTSize - 1, 1); EXPECT_PIXEL_RGBA8_EQ(RGBA8::kBlue, renderTarget2, 1, kRTSize - 1); EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderTarget2, kRTSize - 1, 1); } // Verify that the content in the color attachment will not be changed if there is no corresponding // fragment shader outputs in the render pipeline, the load operation is LoadOp::Load and the store // operation is StoreOp::Store. TEST_P(RenderPassTest, NoCorrespondingFragmentShaderOutputs) { wgpu::Texture renderTarget = CreateDefault2DTexture(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::TextureView renderTargetView = renderTarget.CreateView(); utils::ComboRenderPassDescriptor renderPass({renderTargetView}); renderPass.cColorAttachments[0].clearColor = {1.0f, 0.0f, 0.0f, 1.0f}; renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); { // First we draw a blue triangle in the bottom left of renderTarget. pass.SetPipeline(pipeline); pass.Draw(3); } { // Next we use a pipeline whose fragment shader has no outputs. wgpu::ShaderModule fsModule = utils::CreateShaderModuleFromWGSL(device, R"( [[stage(fragment)]] fn main() -> void { })"); utils::ComboRenderPipelineDescriptor descriptor(device); descriptor.vertexStage.module = mVSModule; descriptor.cFragmentStage.module = fsModule; descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleList; descriptor.cColorStates[0].format = kFormat; wgpu::RenderPipeline pipelineWithNoFragmentOutput = device.CreateRenderPipeline(&descriptor); pass.SetPipeline(pipelineWithNoFragmentOutput); pass.Draw(3); } pass.EndPass(); wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(RGBA8::kBlue, renderTarget, 2, kRTSize - 1); EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderTarget, kRTSize - 1, 1); } DAWN_INSTANTIATE_TEST(RenderPassTest, D3D12Backend(), D3D12Backend({}, {"use_d3d12_render_pass"}), MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend());