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/unittests/wire/WireTest.h"
16
17 #include "common/Constants.h"
18
19 #include <array>
20
21 using namespace testing;
22 using namespace dawn_wire;
23
24 class WireArgumentTests : public WireTest {
25 public:
WireArgumentTests()26 WireArgumentTests() {
27 }
28 ~WireArgumentTests() override = default;
29 };
30
31 // Test that the wire is able to send numerical values
TEST_F(WireArgumentTests,ValueArgument)32 TEST_F(WireArgumentTests, ValueArgument) {
33 WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
34 WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr);
35 wgpuComputePassEncoderDispatch(pass, 1, 2, 3);
36
37 WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
38 EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
39
40 WGPUComputePassEncoder apiPass = api.GetNewComputePassEncoder();
41 EXPECT_CALL(api, CommandEncoderBeginComputePass(apiEncoder, nullptr)).WillOnce(Return(apiPass));
42
43 EXPECT_CALL(api, ComputePassEncoderDispatch(apiPass, 1, 2, 3)).Times(1);
44
45 FlushClient();
46 }
47
48 // Test that the wire is able to send arrays of numerical values
TEST_F(WireArgumentTests,ValueArrayArgument)49 TEST_F(WireArgumentTests, ValueArrayArgument) {
50 // Create a bindgroup.
51 WGPUBindGroupLayoutDescriptor bglDescriptor = {};
52 bglDescriptor.entryCount = 0;
53 bglDescriptor.entries = nullptr;
54
55 WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor);
56 WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
57 EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl));
58
59 WGPUBindGroupDescriptor bindGroupDescriptor = {};
60 bindGroupDescriptor.layout = bgl;
61 bindGroupDescriptor.entryCount = 0;
62 bindGroupDescriptor.entries = nullptr;
63
64 WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDescriptor);
65 WGPUBindGroup apiBindGroup = api.GetNewBindGroup();
66 EXPECT_CALL(api, DeviceCreateBindGroup(apiDevice, _)).WillOnce(Return(apiBindGroup));
67
68 // Use the bindgroup in SetBindGroup that takes an array of value offsets.
69 WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
70 WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr);
71
72 std::array<uint32_t, 4> testOffsets = {0, 42, 0xDEAD'BEEFu, 0xFFFF'FFFFu};
73 wgpuComputePassEncoderSetBindGroup(pass, 0, bindGroup, testOffsets.size(), testOffsets.data());
74
75 WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
76 EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
77
78 WGPUComputePassEncoder apiPass = api.GetNewComputePassEncoder();
79 EXPECT_CALL(api, CommandEncoderBeginComputePass(apiEncoder, nullptr)).WillOnce(Return(apiPass));
80
81 EXPECT_CALL(api, ComputePassEncoderSetBindGroup(
82 apiPass, 0, apiBindGroup, testOffsets.size(),
83 MatchesLambda([testOffsets](const uint32_t* offsets) -> bool {
84 for (size_t i = 0; i < testOffsets.size(); i++) {
85 if (offsets[i] != testOffsets[i]) {
86 return false;
87 }
88 }
89 return true;
90 })));
91
92 FlushClient();
93 }
94
95 // Test that the wire is able to send C strings
TEST_F(WireArgumentTests,CStringArgument)96 TEST_F(WireArgumentTests, CStringArgument) {
97 // Create shader module
98 WGPUShaderModuleDescriptor vertexDescriptor = {};
99 WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vertexDescriptor);
100 WGPUShaderModule apiVsModule = api.GetNewShaderModule();
101 EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule));
102
103 // Create the color state descriptor
104 WGPUBlendComponent blendComponent = {};
105 blendComponent.operation = WGPUBlendOperation_Add;
106 blendComponent.srcFactor = WGPUBlendFactor_One;
107 blendComponent.dstFactor = WGPUBlendFactor_One;
108 WGPUBlendState blendState = {};
109 blendState.alpha = blendComponent;
110 blendState.color = blendComponent;
111 WGPUColorTargetState colorTargetState = {};
112 colorTargetState.format = WGPUTextureFormat_RGBA8Unorm;
113 colorTargetState.blend = &blendState;
114 colorTargetState.writeMask = WGPUColorWriteMask_All;
115
116 // Create the depth-stencil state
117 WGPUStencilFaceState stencilFace = {};
118 stencilFace.compare = WGPUCompareFunction_Always;
119 stencilFace.failOp = WGPUStencilOperation_Keep;
120 stencilFace.depthFailOp = WGPUStencilOperation_Keep;
121 stencilFace.passOp = WGPUStencilOperation_Keep;
122
123 WGPUDepthStencilState depthStencilState = {};
124 depthStencilState.format = WGPUTextureFormat_Depth24PlusStencil8;
125 depthStencilState.depthWriteEnabled = false;
126 depthStencilState.depthCompare = WGPUCompareFunction_Always;
127 depthStencilState.stencilBack = stencilFace;
128 depthStencilState.stencilFront = stencilFace;
129 depthStencilState.stencilReadMask = 0xff;
130 depthStencilState.stencilWriteMask = 0xff;
131 depthStencilState.depthBias = 0;
132 depthStencilState.depthBiasSlopeScale = 0.0;
133 depthStencilState.depthBiasClamp = 0.0;
134
135 // Create the pipeline layout
136 WGPUPipelineLayoutDescriptor layoutDescriptor = {};
137 layoutDescriptor.bindGroupLayoutCount = 0;
138 layoutDescriptor.bindGroupLayouts = nullptr;
139 WGPUPipelineLayout layout = wgpuDeviceCreatePipelineLayout(device, &layoutDescriptor);
140 WGPUPipelineLayout apiLayout = api.GetNewPipelineLayout();
141 EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout));
142
143 // Create pipeline
144 WGPURenderPipelineDescriptor pipelineDescriptor = {};
145
146 pipelineDescriptor.vertex.module = vsModule;
147 pipelineDescriptor.vertex.entryPoint = "main";
148 pipelineDescriptor.vertex.bufferCount = 0;
149 pipelineDescriptor.vertex.buffers = nullptr;
150
151 WGPUFragmentState fragment = {};
152 fragment.module = vsModule;
153 fragment.entryPoint = "main";
154 fragment.targetCount = 1;
155 fragment.targets = &colorTargetState;
156 pipelineDescriptor.fragment = &fragment;
157
158 pipelineDescriptor.multisample.count = 1;
159 pipelineDescriptor.multisample.mask = 0xFFFFFFFF;
160 pipelineDescriptor.multisample.alphaToCoverageEnabled = false;
161 pipelineDescriptor.layout = layout;
162 pipelineDescriptor.primitive.topology = WGPUPrimitiveTopology_TriangleList;
163 pipelineDescriptor.primitive.frontFace = WGPUFrontFace_CCW;
164 pipelineDescriptor.primitive.cullMode = WGPUCullMode_None;
165 pipelineDescriptor.depthStencil = &depthStencilState;
166
167 wgpuDeviceCreateRenderPipeline(device, &pipelineDescriptor);
168
169 WGPURenderPipeline apiDummyPipeline = api.GetNewRenderPipeline();
170 EXPECT_CALL(api,
171 DeviceCreateRenderPipeline(
172 apiDevice, MatchesLambda([](const WGPURenderPipelineDescriptor* desc) -> bool {
173 return desc->vertex.entryPoint == std::string("main");
174 })))
175 .WillOnce(Return(apiDummyPipeline));
176
177 FlushClient();
178 }
179
180 // Test that the wire is able to send objects as value arguments
TEST_F(WireArgumentTests,ObjectAsValueArgument)181 TEST_F(WireArgumentTests, ObjectAsValueArgument) {
182 WGPUCommandEncoder cmdBufEncoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
183 WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
184 EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
185
186 WGPUBufferDescriptor descriptor = {};
187 descriptor.size = 8;
188 descriptor.usage =
189 static_cast<WGPUBufferUsage>(WGPUBufferUsage_CopySrc | WGPUBufferUsage_CopyDst);
190
191 WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor);
192 WGPUBuffer apiBuffer = api.GetNewBuffer();
193 EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
194 .WillOnce(Return(apiBuffer))
195 .RetiresOnSaturation();
196
197 wgpuCommandEncoderCopyBufferToBuffer(cmdBufEncoder, buffer, 0, buffer, 4, 4);
198 EXPECT_CALL(api, CommandEncoderCopyBufferToBuffer(apiEncoder, apiBuffer, 0, apiBuffer, 4, 4));
199
200 FlushClient();
201 }
202
203 // Test that the wire is able to send array of objects
TEST_F(WireArgumentTests,ObjectsAsPointerArgument)204 TEST_F(WireArgumentTests, ObjectsAsPointerArgument) {
205 WGPUCommandBuffer cmdBufs[2];
206 WGPUCommandBuffer apiCmdBufs[2];
207
208 // Create two command buffers we need to use a GMock sequence otherwise the order of the
209 // CreateCommandEncoder might be swapped since they are equivalent in term of matchers
210 Sequence s;
211 for (int i = 0; i < 2; ++i) {
212 WGPUCommandEncoder cmdBufEncoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
213 cmdBufs[i] = wgpuCommandEncoderFinish(cmdBufEncoder, nullptr);
214
215 WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder();
216 EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr))
217 .InSequence(s)
218 .WillOnce(Return(apiCmdBufEncoder));
219
220 apiCmdBufs[i] = api.GetNewCommandBuffer();
221 EXPECT_CALL(api, CommandEncoderFinish(apiCmdBufEncoder, nullptr))
222 .WillOnce(Return(apiCmdBufs[i]));
223 }
224
225 // Submit command buffer and check we got a call with both API-side command buffers
226 wgpuQueueSubmit(queue, 2, cmdBufs);
227
228 EXPECT_CALL(
229 api, QueueSubmit(apiQueue, 2, MatchesLambda([=](const WGPUCommandBuffer* cmdBufs) -> bool {
230 return cmdBufs[0] == apiCmdBufs[0] && cmdBufs[1] == apiCmdBufs[1];
231 })));
232
233 FlushClient();
234 }
235
236 // Test that the wire is able to send structures that contain pure values (non-objects)
TEST_F(WireArgumentTests,StructureOfValuesArgument)237 TEST_F(WireArgumentTests, StructureOfValuesArgument) {
238 WGPUSamplerDescriptor descriptor = {};
239 descriptor.magFilter = WGPUFilterMode_Linear;
240 descriptor.minFilter = WGPUFilterMode_Nearest;
241 descriptor.mipmapFilter = WGPUFilterMode_Linear;
242 descriptor.addressModeU = WGPUAddressMode_ClampToEdge;
243 descriptor.addressModeV = WGPUAddressMode_Repeat;
244 descriptor.addressModeW = WGPUAddressMode_MirrorRepeat;
245 descriptor.lodMinClamp = kLodMin;
246 descriptor.lodMaxClamp = kLodMax;
247 descriptor.compare = WGPUCompareFunction_Never;
248
249 wgpuDeviceCreateSampler(device, &descriptor);
250
251 WGPUSampler apiDummySampler = api.GetNewSampler();
252 EXPECT_CALL(api, DeviceCreateSampler(
253 apiDevice, MatchesLambda([](const WGPUSamplerDescriptor* desc) -> bool {
254 return desc->nextInChain == nullptr &&
255 desc->magFilter == WGPUFilterMode_Linear &&
256 desc->minFilter == WGPUFilterMode_Nearest &&
257 desc->mipmapFilter == WGPUFilterMode_Linear &&
258 desc->addressModeU == WGPUAddressMode_ClampToEdge &&
259 desc->addressModeV == WGPUAddressMode_Repeat &&
260 desc->addressModeW == WGPUAddressMode_MirrorRepeat &&
261 desc->compare == WGPUCompareFunction_Never &&
262 desc->lodMinClamp == kLodMin && desc->lodMaxClamp == kLodMax;
263 })))
264 .WillOnce(Return(apiDummySampler));
265
266 FlushClient();
267 }
268
269 // Test that the wire is able to send structures that contain objects
TEST_F(WireArgumentTests,StructureOfObjectArrayArgument)270 TEST_F(WireArgumentTests, StructureOfObjectArrayArgument) {
271 WGPUBindGroupLayoutDescriptor bglDescriptor = {};
272 bglDescriptor.entryCount = 0;
273 bglDescriptor.entries = nullptr;
274
275 WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor);
276 WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
277 EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl));
278
279 WGPUPipelineLayoutDescriptor descriptor = {};
280 descriptor.bindGroupLayoutCount = 1;
281 descriptor.bindGroupLayouts = &bgl;
282
283 wgpuDeviceCreatePipelineLayout(device, &descriptor);
284
285 WGPUPipelineLayout apiDummyLayout = api.GetNewPipelineLayout();
286 EXPECT_CALL(api, DeviceCreatePipelineLayout(
287 apiDevice,
288 MatchesLambda([apiBgl](const WGPUPipelineLayoutDescriptor* desc) -> bool {
289 return desc->nextInChain == nullptr &&
290 desc->bindGroupLayoutCount == 1 &&
291 desc->bindGroupLayouts[0] == apiBgl;
292 })))
293 .WillOnce(Return(apiDummyLayout));
294
295 FlushClient();
296 }
297
298 // Test that the wire is able to send structures that contain objects
TEST_F(WireArgumentTests,StructureOfStructureArrayArgument)299 TEST_F(WireArgumentTests, StructureOfStructureArrayArgument) {
300 static constexpr int NUM_BINDINGS = 3;
301 WGPUBindGroupLayoutEntry entries[NUM_BINDINGS]{
302 {nullptr,
303 0,
304 WGPUShaderStage_Vertex,
305 {},
306 {nullptr, WGPUSamplerBindingType_Filtering},
307 {},
308 {}},
309 {nullptr,
310 1,
311 WGPUShaderStage_Vertex,
312 {},
313 {},
314 {nullptr, WGPUTextureSampleType_Float, WGPUTextureViewDimension_2D, false},
315 {}},
316 {nullptr,
317 2,
318 static_cast<WGPUShaderStage>(WGPUShaderStage_Vertex | WGPUShaderStage_Fragment),
319 {nullptr, WGPUBufferBindingType_Uniform, false, 0},
320 {},
321 {},
322 {}},
323 };
324 WGPUBindGroupLayoutDescriptor bglDescriptor = {};
325 bglDescriptor.entryCount = NUM_BINDINGS;
326 bglDescriptor.entries = entries;
327
328 wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor);
329 WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
330 EXPECT_CALL(
331 api,
332 DeviceCreateBindGroupLayout(
333 apiDevice, MatchesLambda([entries](const WGPUBindGroupLayoutDescriptor* desc) -> bool {
334 for (int i = 0; i < NUM_BINDINGS; ++i) {
335 const auto& a = desc->entries[i];
336 const auto& b = entries[i];
337 if (a.binding != b.binding || a.visibility != b.visibility ||
338 a.buffer.type != b.buffer.type || a.sampler.type != b.sampler.type ||
339 a.texture.sampleType != b.texture.sampleType) {
340 return false;
341 }
342 }
343 return desc->nextInChain == nullptr && desc->entryCount == 3;
344 })))
345 .WillOnce(Return(apiBgl));
346
347 FlushClient();
348 }
349
350 // Test passing nullptr instead of objects - array of objects version
TEST_F(WireArgumentTests,DISABLED_NullptrInArray)351 TEST_F(WireArgumentTests, DISABLED_NullptrInArray) {
352 WGPUBindGroupLayout nullBGL = nullptr;
353
354 WGPUPipelineLayoutDescriptor descriptor = {};
355 descriptor.bindGroupLayoutCount = 1;
356 descriptor.bindGroupLayouts = &nullBGL;
357
358 wgpuDeviceCreatePipelineLayout(device, &descriptor);
359 EXPECT_CALL(api,
360 DeviceCreatePipelineLayout(
361 apiDevice, MatchesLambda([](const WGPUPipelineLayoutDescriptor* desc) -> bool {
362 return desc->nextInChain == nullptr && desc->bindGroupLayoutCount == 1 &&
363 desc->bindGroupLayouts[0] == nullptr;
364 })))
365 .WillOnce(Return(nullptr));
366
367 FlushClient();
368 }
369