1 // Copyright 2020 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 namespace {
21 struct CreateReadyPipelineTask {
22 wgpu::ComputePipeline computePipeline = nullptr;
23 wgpu::RenderPipeline renderPipeline = nullptr;
24 bool isCompleted = false;
25 std::string message;
26 };
27 } // anonymous namespace
28
29 class CreateReadyPipelineTest : public DawnTest {
30 protected:
31 CreateReadyPipelineTask task;
32 };
33
34 // Verify the basic use of CreateReadyComputePipeline works on all backends.
TEST_P(CreateReadyPipelineTest,BasicUseOfCreateReadyComputePipeline)35 TEST_P(CreateReadyPipelineTest, BasicUseOfCreateReadyComputePipeline) {
36 const char* computeShader = R"(
37 #version 450
38 layout(std140, set = 0, binding = 0) buffer SSBO { uint value; } ssbo;
39 void main() {
40 ssbo.value = 1u;
41 })";
42
43 wgpu::ComputePipelineDescriptor csDesc;
44 csDesc.computeStage.module =
45 utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
46 csDesc.computeStage.entryPoint = "main";
47
48 device.CreateReadyComputePipeline(
49 &csDesc,
50 [](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
51 const char* message, void* userdata) {
52 ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_Success, status);
53
54 CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
55 task->computePipeline = wgpu::ComputePipeline::Acquire(returnPipeline);
56 task->isCompleted = true;
57 task->message = message;
58 },
59 &task);
60
61 wgpu::BufferDescriptor bufferDesc;
62 bufferDesc.size = sizeof(uint32_t);
63 bufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
64 wgpu::Buffer ssbo = device.CreateBuffer(&bufferDesc);
65
66 wgpu::CommandBuffer commands;
67 {
68 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
69 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
70
71 while (!task.isCompleted) {
72 WaitABit();
73 }
74 ASSERT_TRUE(task.message.empty());
75 ASSERT_NE(nullptr, task.computePipeline.Get());
76 wgpu::BindGroup bindGroup =
77 utils::MakeBindGroup(device, task.computePipeline.GetBindGroupLayout(0),
78 {
79 {0, ssbo, 0, sizeof(uint32_t)},
80 });
81 pass.SetBindGroup(0, bindGroup);
82 pass.SetPipeline(task.computePipeline);
83
84 pass.Dispatch(1);
85 pass.EndPass();
86
87 commands = encoder.Finish();
88 }
89
90 queue.Submit(1, &commands);
91
92 constexpr uint32_t kExpected = 1u;
93 EXPECT_BUFFER_U32_EQ(kExpected, ssbo, 0);
94 }
95
96 // Verify CreateReadyComputePipeline() works as expected when there is any error that happens during
97 // the creation of the compute pipeline. The SPEC requires that during the call of
98 // CreateReadyComputePipeline() any error won't be forwarded to the error scope / unhandled error
99 // callback.
TEST_P(CreateReadyPipelineTest,CreateComputePipelineFailed)100 TEST_P(CreateReadyPipelineTest, CreateComputePipelineFailed) {
101 DAWN_SKIP_TEST_IF(IsDawnValidationSkipped());
102
103 const char* computeShader = R"(
104 #version 450
105 layout(std140, set = 0, binding = 0) buffer SSBO { uint value; } ssbo;
106 void main() {
107 ssbo.value = 1u;
108 })";
109
110 wgpu::ComputePipelineDescriptor csDesc;
111 csDesc.computeStage.module =
112 utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
113 csDesc.computeStage.entryPoint = "main0";
114
115 device.CreateReadyComputePipeline(
116 &csDesc,
117 [](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
118 const char* message, void* userdata) {
119 ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_Error, status);
120
121 CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
122 task->computePipeline = wgpu::ComputePipeline::Acquire(returnPipeline);
123 task->isCompleted = true;
124 task->message = message;
125 },
126 &task);
127
128 while (!task.isCompleted) {
129 WaitABit();
130 }
131
132 ASSERT_FALSE(task.message.empty());
133 ASSERT_EQ(nullptr, task.computePipeline.Get());
134 }
135
136 // Verify the basic use of CreateReadyRenderPipeline() works on all backends.
TEST_P(CreateReadyPipelineTest,BasicUseOfCreateReadyRenderPipeline)137 TEST_P(CreateReadyPipelineTest, BasicUseOfCreateReadyRenderPipeline) {
138 constexpr wgpu::TextureFormat kRenderAttachmentFormat = wgpu::TextureFormat::RGBA8Unorm;
139
140 const char* vertexShader = R"(
141 #version 450
142 void main() {
143 gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
144 gl_PointSize = 1.0f;
145 })";
146 const char* fragmentShader = R"(
147 #version 450
148 layout(location = 0) out vec4 o_color;
149 void main() {
150 o_color = vec4(0.f, 1.f, 0.f, 1.f);
151 })";
152
153 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
154 wgpu::ShaderModule vsModule =
155 utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vertexShader);
156 wgpu::ShaderModule fsModule =
157 utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader);
158 renderPipelineDescriptor.vertexStage.module = vsModule;
159 renderPipelineDescriptor.cFragmentStage.module = fsModule;
160 renderPipelineDescriptor.cColorStates[0].format = kRenderAttachmentFormat;
161 renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
162
163 device.CreateReadyRenderPipeline(
164 &renderPipelineDescriptor,
165 [](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline,
166 const char* message, void* userdata) {
167 ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_Success, status);
168
169 CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
170 task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline);
171 task->isCompleted = true;
172 task->message = message;
173 },
174 &task);
175
176 wgpu::TextureDescriptor textureDescriptor;
177 textureDescriptor.size = {1, 1, 1};
178 textureDescriptor.format = kRenderAttachmentFormat;
179 textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
180 wgpu::Texture outputTexture = device.CreateTexture(&textureDescriptor);
181
182 utils::ComboRenderPassDescriptor renderPassDescriptor({outputTexture.CreateView()});
183 renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
184 renderPassDescriptor.cColorAttachments[0].clearColor = {1.f, 0.f, 0.f, 1.f};
185
186 wgpu::CommandBuffer commands;
187 {
188 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
189 wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
190
191 while (!task.isCompleted) {
192 WaitABit();
193 }
194 ASSERT_TRUE(task.message.empty());
195 ASSERT_NE(nullptr, task.renderPipeline.Get());
196
197 renderPassEncoder.SetPipeline(task.renderPipeline);
198 renderPassEncoder.Draw(1);
199 renderPassEncoder.EndPass();
200 commands = encoder.Finish();
201 }
202
203 queue.Submit(1, &commands);
204
205 EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), outputTexture, 0, 0);
206 }
207
208 // Verify CreateReadyRenderPipeline() works as expected when there is any error that happens during
209 // the creation of the render pipeline. The SPEC requires that during the call of
210 // CreateReadyRenderPipeline() any error won't be forwarded to the error scope / unhandled error
211 // callback.
TEST_P(CreateReadyPipelineTest,CreateRenderPipelineFailed)212 TEST_P(CreateReadyPipelineTest, CreateRenderPipelineFailed) {
213 DAWN_SKIP_TEST_IF(IsDawnValidationSkipped());
214
215 constexpr wgpu::TextureFormat kRenderAttachmentFormat = wgpu::TextureFormat::Depth32Float;
216
217 const char* vertexShader = R"(
218 #version 450
219 void main() {
220 gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
221 gl_PointSize = 1.0f;
222 })";
223 const char* fragmentShader = R"(
224 #version 450
225 layout(location = 0) out vec4 o_color;
226 void main() {
227 o_color = vec4(0.f, 1.f, 0.f, 1.f);
228 })";
229
230 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
231 wgpu::ShaderModule vsModule =
232 utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vertexShader);
233 wgpu::ShaderModule fsModule =
234 utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader);
235 renderPipelineDescriptor.vertexStage.module = vsModule;
236 renderPipelineDescriptor.cFragmentStage.module = fsModule;
237 renderPipelineDescriptor.cColorStates[0].format = kRenderAttachmentFormat;
238 renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
239
240 device.CreateReadyRenderPipeline(
241 &renderPipelineDescriptor,
242 [](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline,
243 const char* message, void* userdata) {
244 ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_Error, status);
245
246 CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
247 task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline);
248 task->isCompleted = true;
249 task->message = message;
250 },
251 &task);
252
253 while (!task.isCompleted) {
254 WaitABit();
255 }
256
257 ASSERT_FALSE(task.message.empty());
258 ASSERT_EQ(nullptr, task.computePipeline.Get());
259 }
260
261 // Verify there is no error when the device is released before the callback of
262 // CreateReadyComputePipeline() is called.
TEST_P(CreateReadyPipelineTest,ReleaseDeviceBeforeCallbackOfCreateReadyComputePipeline)263 TEST_P(CreateReadyPipelineTest, ReleaseDeviceBeforeCallbackOfCreateReadyComputePipeline) {
264 const char* computeShader = R"(
265 #version 450
266 void main() {
267 })";
268
269 wgpu::ComputePipelineDescriptor csDesc;
270 csDesc.computeStage.module =
271 utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
272 csDesc.computeStage.entryPoint = "main";
273
274 device.CreateReadyComputePipeline(
275 &csDesc,
276 [](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
277 const char* message, void* userdata) {
278 ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_DeviceDestroyed,
279 status);
280
281 CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
282 task->computePipeline = wgpu::ComputePipeline::Acquire(returnPipeline);
283 task->isCompleted = true;
284 task->message = message;
285 },
286 &task);
287 }
288
289 // Verify there is no error when the device is released before the callback of
290 // CreateReadyRenderPipeline() is called.
TEST_P(CreateReadyPipelineTest,ReleaseDeviceBeforeCallbackOfCreateReadyRenderPipeline)291 TEST_P(CreateReadyPipelineTest, ReleaseDeviceBeforeCallbackOfCreateReadyRenderPipeline) {
292 const char* vertexShader = R"(
293 #version 450
294 void main() {
295 gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
296 gl_PointSize = 1.0f;
297 })";
298 const char* fragmentShader = R"(
299 #version 450
300 layout(location = 0) out vec4 o_color;
301 void main() {
302 o_color = vec4(0.f, 1.f, 0.f, 1.f);
303 })";
304
305 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
306 wgpu::ShaderModule vsModule =
307 utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vertexShader);
308 wgpu::ShaderModule fsModule =
309 utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader);
310 renderPipelineDescriptor.vertexStage.module = vsModule;
311 renderPipelineDescriptor.cFragmentStage.module = fsModule;
312 renderPipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm;
313 renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
314
315 device.CreateReadyRenderPipeline(
316 &renderPipelineDescriptor,
317 [](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline,
318 const char* message, void* userdata) {
319 ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_DeviceDestroyed,
320 status);
321
322 CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
323 task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline);
324 task->isCompleted = true;
325 task->message = message;
326 },
327 &task);
328 }
329
330 DAWN_INSTANTIATE_TEST(CreateReadyPipelineTest,
331 D3D12Backend(),
332 MetalBackend(),
333 OpenGLBackend(),
334 VulkanBackend());
335