• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/DawnTest.h"
16 
17 #include "utils/ComboRenderPipelineDescriptor.h"
18 #include "utils/WGPUHelpers.h"
19 
20 using ::testing::HasSubstr;
21 
22 constexpr uint32_t kRTSize = 4;
23 
24 class DestroyTest : public DawnTest {
25   protected:
SetUp()26     void SetUp() override {
27         DawnTest::SetUp();
28         DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("skip_validation"));
29 
30         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
31 
32         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
33               [[stage(vertex)]]
34               fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
35                   return pos;
36               })");
37 
38         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
39               [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
40                   return vec4<f32>(0.0, 1.0, 0.0, 1.0);
41               })");
42 
43         utils::ComboRenderPipelineDescriptor descriptor;
44         descriptor.vertex.module = vsModule;
45         descriptor.cFragment.module = fsModule;
46         descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
47         descriptor.vertex.bufferCount = 1;
48         descriptor.cBuffers[0].arrayStride = 4 * sizeof(float);
49         descriptor.cBuffers[0].attributeCount = 1;
50         descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
51         descriptor.cTargets[0].format = renderPass.colorFormat;
52 
53         pipeline = device.CreateRenderPipeline(&descriptor);
54 
55         vertexBuffer = utils::CreateBufferFromData<float>(
56             device, wgpu::BufferUsage::Vertex,
57             {// The bottom left triangle
58              -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f});
59 
60         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
61         encoder.BeginRenderPass(&renderPass.renderPassInfo).EndPass();
62         wgpu::CommandBuffer commands = encoder.Finish();
63         queue.Submit(1, &commands);
64     }
65 
66     utils::BasicRenderPass renderPass;
67     wgpu::RenderPipeline pipeline;
68     wgpu::Buffer vertexBuffer;
69 
CreateTriangleCommandBuffer()70     wgpu::CommandBuffer CreateTriangleCommandBuffer() {
71         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
72         {
73             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
74             pass.SetPipeline(pipeline);
75             pass.SetVertexBuffer(0, vertexBuffer);
76             pass.Draw(3);
77             pass.EndPass();
78         }
79         wgpu::CommandBuffer commands = encoder.Finish();
80         return commands;
81     }
82 };
83 
84 // Destroy before submit will result in error, and nothing drawn
TEST_P(DestroyTest,BufferDestroyBeforeSubmit)85 TEST_P(DestroyTest, BufferDestroyBeforeSubmit) {
86     RGBA8 notFilled(0, 0, 0, 0);
87 
88     wgpu::CommandBuffer commands = CreateTriangleCommandBuffer();
89     vertexBuffer.Destroy();
90     ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
91 
92     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 1, 3);
93 }
94 
95 // Destroy after submit will draw successfully
TEST_P(DestroyTest,BufferDestroyAfterSubmit)96 TEST_P(DestroyTest, BufferDestroyAfterSubmit) {
97     RGBA8 filled(0, 255, 0, 255);
98 
99     wgpu::CommandBuffer commands = CreateTriangleCommandBuffer();
100     queue.Submit(1, &commands);
101 
102     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
103     vertexBuffer.Destroy();
104 }
105 
106 // First submit succeeds, draws triangle, second submit fails
107 // after destroy is called on the buffer, pixel does not change
TEST_P(DestroyTest,BufferSubmitDestroySubmit)108 TEST_P(DestroyTest, BufferSubmitDestroySubmit) {
109     RGBA8 filled(0, 255, 0, 255);
110 
111     wgpu::CommandBuffer commands = CreateTriangleCommandBuffer();
112     queue.Submit(1, &commands);
113     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
114 
115     vertexBuffer.Destroy();
116 
117     // Submit fails because vertex buffer was destroyed
118     ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
119 
120     // Pixel stays the same
121     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
122 }
123 
124 // Destroy texture before submit should fail submit
TEST_P(DestroyTest,TextureDestroyBeforeSubmit)125 TEST_P(DestroyTest, TextureDestroyBeforeSubmit) {
126     wgpu::CommandBuffer commands = CreateTriangleCommandBuffer();
127     renderPass.color.Destroy();
128     ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
129 }
130 
131 // Destroy after submit will draw successfully
TEST_P(DestroyTest,TextureDestroyAfterSubmit)132 TEST_P(DestroyTest, TextureDestroyAfterSubmit) {
133     RGBA8 filled(0, 255, 0, 255);
134 
135     wgpu::CommandBuffer commands = CreateTriangleCommandBuffer();
136     queue.Submit(1, &commands);
137 
138     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
139     renderPass.color.Destroy();
140 }
141 
142 // First submit succeeds, draws triangle, second submit fails
143 // after destroy is called on the texture
TEST_P(DestroyTest,TextureSubmitDestroySubmit)144 TEST_P(DestroyTest, TextureSubmitDestroySubmit) {
145     RGBA8 filled(0, 255, 0, 255);
146 
147     wgpu::CommandBuffer commands = CreateTriangleCommandBuffer();
148     queue.Submit(1, &commands);
149     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
150 
151     renderPass.color.Destroy();
152 
153     // Submit fails because texture was destroyed
154     ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
155 }
156 
157 // Attempting to set an object label after it has been destroyed should not cause an error.
TEST_P(DestroyTest,DestroyThenSetLabel)158 TEST_P(DestroyTest, DestroyThenSetLabel) {
159     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
160     std::string label = "test";
161     wgpu::BufferDescriptor descriptor;
162     descriptor.size = 4;
163     descriptor.usage = wgpu::BufferUsage::Uniform;
164     wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
165     buffer.Destroy();
166     buffer.SetLabel(label.c_str());
167 }
168 
169 // Device destroy before buffer submit will result in error.
TEST_P(DestroyTest,DestroyDeviceBeforeSubmit)170 TEST_P(DestroyTest, DestroyDeviceBeforeSubmit) {
171     // TODO(crbug.com/dawn/628) Add more comprehensive tests with destroy and backends.
172     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
173     wgpu::CommandBuffer commands = CreateTriangleCommandBuffer();
174 
175     // Tests normally don't expect a device lost error, but since we are destroying the device, we
176     // actually do, so we need to override the default device lost callback.
177     ExpectDeviceDestruction();
178     device.Destroy();
179     ASSERT_DEVICE_ERROR_MSG(queue.Submit(1, &commands), HasSubstr("[Device] is lost."));
180 }
181 
182 // Regression test for crbug.com/1276928 where a lingering BGL reference in Vulkan with at least one
183 // BG instance could cause bad memory reads because members in the BGL whose destuctors expected a
184 // live device were not released until after the device was destroyed.
TEST_P(DestroyTest,DestroyDeviceLingeringBGL)185 TEST_P(DestroyTest, DestroyDeviceLingeringBGL) {
186     // Create and hold the layout reference so that its destructor gets called after the device has
187     // been destroyed via device.Destroy().
188     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
189         device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
190     utils::MakeBindGroup(device, layout, {{0, device.CreateSampler()}});
191 
192     // Tests normally don't expect a device lost error, but since we are destroying the device, we
193     // actually do, so we need to override the default device lost callback.
194     ExpectDeviceDestruction();
195     device.Destroy();
196 }
197 
198 DAWN_INSTANTIATE_TEST(DestroyTest,
199                       D3D12Backend(),
200                       MetalBackend(),
201                       OpenGLBackend(),
202                       OpenGLESBackend(),
203                       VulkanBackend());
204