blob: 4d21fa772d9fff7e261ce6ef5b4b00462966349b [file] [log] [blame] [edit]
// 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/wire/WireTest.h"
#include "common/Constants.h"
#include <array>
using namespace testing;
using namespace dawn_wire;
class WireArgumentTests : public WireTest {
public:
WireArgumentTests() {
}
~WireArgumentTests() override = default;
};
// Test that the wire is able to send numerical values
TEST_F(WireArgumentTests, ValueArgument) {
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr);
wgpuComputePassEncoderDispatch(pass, 1, 2, 3);
WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
WGPUComputePassEncoder apiPass = api.GetNewComputePassEncoder();
EXPECT_CALL(api, CommandEncoderBeginComputePass(apiEncoder, nullptr)).WillOnce(Return(apiPass));
EXPECT_CALL(api, ComputePassEncoderDispatch(apiPass, 1, 2, 3)).Times(1);
FlushClient();
}
// Test that the wire is able to send arrays of numerical values
TEST_F(WireArgumentTests, ValueArrayArgument) {
// Create a bindgroup.
WGPUBindGroupLayoutDescriptor bglDescriptor = {};
bglDescriptor.entryCount = 0;
bglDescriptor.entries = nullptr;
WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor);
WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl));
WGPUBindGroupDescriptor bindGroupDescriptor = {};
bindGroupDescriptor.layout = bgl;
bindGroupDescriptor.entryCount = 0;
bindGroupDescriptor.entries = nullptr;
WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDescriptor);
WGPUBindGroup apiBindGroup = api.GetNewBindGroup();
EXPECT_CALL(api, DeviceCreateBindGroup(apiDevice, _)).WillOnce(Return(apiBindGroup));
// Use the bindgroup in SetBindGroup that takes an array of value offsets.
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr);
std::array<uint32_t, 4> testOffsets = {0, 42, 0xDEAD'BEEFu, 0xFFFF'FFFFu};
wgpuComputePassEncoderSetBindGroup(pass, 0, bindGroup, testOffsets.size(), testOffsets.data());
WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
WGPUComputePassEncoder apiPass = api.GetNewComputePassEncoder();
EXPECT_CALL(api, CommandEncoderBeginComputePass(apiEncoder, nullptr)).WillOnce(Return(apiPass));
EXPECT_CALL(api, ComputePassEncoderSetBindGroup(
apiPass, 0, apiBindGroup, testOffsets.size(),
MatchesLambda([testOffsets](const uint32_t* offsets) -> bool {
for (size_t i = 0; i < testOffsets.size(); i++) {
if (offsets[i] != testOffsets[i]) {
return false;
}
}
return true;
})));
FlushClient();
}
// Test that the wire is able to send C strings
TEST_F(WireArgumentTests, CStringArgument) {
// Create shader module
WGPUShaderModuleDescriptor vertexDescriptor = {};
WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vertexDescriptor);
WGPUShaderModule apiVsModule = api.GetNewShaderModule();
EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule));
// Create the color state descriptor
WGPUBlendComponent blendComponent = {};
blendComponent.operation = WGPUBlendOperation_Add;
blendComponent.srcFactor = WGPUBlendFactor_One;
blendComponent.dstFactor = WGPUBlendFactor_One;
WGPUBlendState blendState = {};
blendState.alpha = blendComponent;
blendState.color = blendComponent;
WGPUColorTargetState colorTargetState = {};
colorTargetState.format = WGPUTextureFormat_RGBA8Unorm;
colorTargetState.blend = &blendState;
colorTargetState.writeMask = WGPUColorWriteMask_All;
// Create the depth-stencil state
WGPUStencilFaceState stencilFace = {};
stencilFace.compare = WGPUCompareFunction_Always;
stencilFace.failOp = WGPUStencilOperation_Keep;
stencilFace.depthFailOp = WGPUStencilOperation_Keep;
stencilFace.passOp = WGPUStencilOperation_Keep;
WGPUDepthStencilState depthStencilState = {};
depthStencilState.format = WGPUTextureFormat_Depth24PlusStencil8;
depthStencilState.depthWriteEnabled = false;
depthStencilState.depthCompare = WGPUCompareFunction_Always;
depthStencilState.stencilBack = stencilFace;
depthStencilState.stencilFront = stencilFace;
depthStencilState.stencilReadMask = 0xff;
depthStencilState.stencilWriteMask = 0xff;
depthStencilState.depthBias = 0;
depthStencilState.depthBiasSlopeScale = 0.0;
depthStencilState.depthBiasClamp = 0.0;
// Create the pipeline layout
WGPUPipelineLayoutDescriptor layoutDescriptor = {};
layoutDescriptor.bindGroupLayoutCount = 0;
layoutDescriptor.bindGroupLayouts = nullptr;
WGPUPipelineLayout layout = wgpuDeviceCreatePipelineLayout(device, &layoutDescriptor);
WGPUPipelineLayout apiLayout = api.GetNewPipelineLayout();
EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout));
// Create pipeline
WGPURenderPipelineDescriptor pipelineDescriptor = {};
pipelineDescriptor.vertex.module = vsModule;
pipelineDescriptor.vertex.entryPoint = "main";
pipelineDescriptor.vertex.bufferCount = 0;
pipelineDescriptor.vertex.buffers = nullptr;
WGPUFragmentState fragment = {};
fragment.module = vsModule;
fragment.entryPoint = "main";
fragment.targetCount = 1;
fragment.targets = &colorTargetState;
pipelineDescriptor.fragment = &fragment;
pipelineDescriptor.multisample.count = 1;
pipelineDescriptor.multisample.mask = 0xFFFFFFFF;
pipelineDescriptor.multisample.alphaToCoverageEnabled = false;
pipelineDescriptor.layout = layout;
pipelineDescriptor.primitive.topology = WGPUPrimitiveTopology_TriangleList;
pipelineDescriptor.primitive.frontFace = WGPUFrontFace_CCW;
pipelineDescriptor.primitive.cullMode = WGPUCullMode_None;
pipelineDescriptor.depthStencil = &depthStencilState;
wgpuDeviceCreateRenderPipeline(device, &pipelineDescriptor);
WGPURenderPipeline apiDummyPipeline = api.GetNewRenderPipeline();
EXPECT_CALL(api,
DeviceCreateRenderPipeline(
apiDevice, MatchesLambda([](const WGPURenderPipelineDescriptor* desc) -> bool {
return desc->vertex.entryPoint == std::string("main");
})))
.WillOnce(Return(apiDummyPipeline));
FlushClient();
}
// Test that the wire is able to send objects as value arguments
TEST_F(WireArgumentTests, ObjectAsValueArgument) {
WGPUCommandEncoder cmdBufEncoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
WGPUBufferDescriptor descriptor = {};
descriptor.size = 8;
descriptor.usage =
static_cast<WGPUBufferUsage>(WGPUBufferUsage_CopySrc | WGPUBufferUsage_CopyDst);
WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor);
WGPUBuffer apiBuffer = api.GetNewBuffer();
EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
.WillOnce(Return(apiBuffer))
.RetiresOnSaturation();
wgpuCommandEncoderCopyBufferToBuffer(cmdBufEncoder, buffer, 0, buffer, 4, 4);
EXPECT_CALL(api, CommandEncoderCopyBufferToBuffer(apiEncoder, apiBuffer, 0, apiBuffer, 4, 4));
FlushClient();
}
// Test that the wire is able to send array of objects
TEST_F(WireArgumentTests, ObjectsAsPointerArgument) {
WGPUCommandBuffer cmdBufs[2];
WGPUCommandBuffer apiCmdBufs[2];
// Create two command buffers we need to use a GMock sequence otherwise the order of the
// CreateCommandEncoder might be swapped since they are equivalent in term of matchers
Sequence s;
for (int i = 0; i < 2; ++i) {
WGPUCommandEncoder cmdBufEncoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
cmdBufs[i] = wgpuCommandEncoderFinish(cmdBufEncoder, nullptr);
WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder();
EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr))
.InSequence(s)
.WillOnce(Return(apiCmdBufEncoder));
apiCmdBufs[i] = api.GetNewCommandBuffer();
EXPECT_CALL(api, CommandEncoderFinish(apiCmdBufEncoder, nullptr))
.WillOnce(Return(apiCmdBufs[i]));
}
// Submit command buffer and check we got a call with both API-side command buffers
wgpuQueueSubmit(queue, 2, cmdBufs);
EXPECT_CALL(
api, QueueSubmit(apiQueue, 2, MatchesLambda([=](const WGPUCommandBuffer* cmdBufs) -> bool {
return cmdBufs[0] == apiCmdBufs[0] && cmdBufs[1] == apiCmdBufs[1];
})));
FlushClient();
}
// Test that the wire is able to send structures that contain pure values (non-objects)
TEST_F(WireArgumentTests, StructureOfValuesArgument) {
WGPUSamplerDescriptor descriptor = {};
descriptor.magFilter = WGPUFilterMode_Linear;
descriptor.minFilter = WGPUFilterMode_Nearest;
descriptor.mipmapFilter = WGPUFilterMode_Linear;
descriptor.addressModeU = WGPUAddressMode_ClampToEdge;
descriptor.addressModeV = WGPUAddressMode_Repeat;
descriptor.addressModeW = WGPUAddressMode_MirrorRepeat;
descriptor.lodMinClamp = kLodMin;
descriptor.lodMaxClamp = kLodMax;
descriptor.compare = WGPUCompareFunction_Never;
wgpuDeviceCreateSampler(device, &descriptor);
WGPUSampler apiDummySampler = api.GetNewSampler();
EXPECT_CALL(api, DeviceCreateSampler(
apiDevice, MatchesLambda([](const WGPUSamplerDescriptor* desc) -> bool {
return desc->nextInChain == nullptr &&
desc->magFilter == WGPUFilterMode_Linear &&
desc->minFilter == WGPUFilterMode_Nearest &&
desc->mipmapFilter == WGPUFilterMode_Linear &&
desc->addressModeU == WGPUAddressMode_ClampToEdge &&
desc->addressModeV == WGPUAddressMode_Repeat &&
desc->addressModeW == WGPUAddressMode_MirrorRepeat &&
desc->compare == WGPUCompareFunction_Never &&
desc->lodMinClamp == kLodMin && desc->lodMaxClamp == kLodMax;
})))
.WillOnce(Return(apiDummySampler));
FlushClient();
}
// Test that the wire is able to send structures that contain objects
TEST_F(WireArgumentTests, StructureOfObjectArrayArgument) {
WGPUBindGroupLayoutDescriptor bglDescriptor = {};
bglDescriptor.entryCount = 0;
bglDescriptor.entries = nullptr;
WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor);
WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl));
WGPUPipelineLayoutDescriptor descriptor = {};
descriptor.bindGroupLayoutCount = 1;
descriptor.bindGroupLayouts = &bgl;
wgpuDeviceCreatePipelineLayout(device, &descriptor);
WGPUPipelineLayout apiDummyLayout = api.GetNewPipelineLayout();
EXPECT_CALL(api, DeviceCreatePipelineLayout(
apiDevice,
MatchesLambda([apiBgl](const WGPUPipelineLayoutDescriptor* desc) -> bool {
return desc->nextInChain == nullptr &&
desc->bindGroupLayoutCount == 1 &&
desc->bindGroupLayouts[0] == apiBgl;
})))
.WillOnce(Return(apiDummyLayout));
FlushClient();
}
// Test that the wire is able to send structures that contain objects
TEST_F(WireArgumentTests, StructureOfStructureArrayArgument) {
static constexpr int NUM_BINDINGS = 3;
WGPUBindGroupLayoutEntry entries[NUM_BINDINGS]{
{nullptr,
0,
WGPUShaderStage_Vertex,
{},
{nullptr, WGPUSamplerBindingType_Filtering},
{},
{}},
{nullptr,
1,
WGPUShaderStage_Vertex,
{},
{},
{nullptr, WGPUTextureSampleType_Float, WGPUTextureViewDimension_2D, false},
{}},
{nullptr,
2,
static_cast<WGPUShaderStage>(WGPUShaderStage_Vertex | WGPUShaderStage_Fragment),
{nullptr, WGPUBufferBindingType_Uniform, false, 0},
{},
{},
{}},
};
WGPUBindGroupLayoutDescriptor bglDescriptor = {};
bglDescriptor.entryCount = NUM_BINDINGS;
bglDescriptor.entries = entries;
wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor);
WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
EXPECT_CALL(
api,
DeviceCreateBindGroupLayout(
apiDevice, MatchesLambda([entries](const WGPUBindGroupLayoutDescriptor* desc) -> bool {
for (int i = 0; i < NUM_BINDINGS; ++i) {
const auto& a = desc->entries[i];
const auto& b = entries[i];
if (a.binding != b.binding || a.visibility != b.visibility ||
a.buffer.type != b.buffer.type || a.sampler.type != b.sampler.type ||
a.texture.sampleType != b.texture.sampleType) {
return false;
}
}
return desc->nextInChain == nullptr && desc->entryCount == 3;
})))
.WillOnce(Return(apiBgl));
FlushClient();
}
// Test passing nullptr instead of objects - array of objects version
TEST_F(WireArgumentTests, DISABLED_NullptrInArray) {
WGPUBindGroupLayout nullBGL = nullptr;
WGPUPipelineLayoutDescriptor descriptor = {};
descriptor.bindGroupLayoutCount = 1;
descriptor.bindGroupLayouts = &nullBGL;
wgpuDeviceCreatePipelineLayout(device, &descriptor);
EXPECT_CALL(api,
DeviceCreatePipelineLayout(
apiDevice, MatchesLambda([](const WGPUPipelineLayoutDescriptor* desc) -> bool {
return desc->nextInChain == nullptr && desc->bindGroupLayoutCount == 1 &&
desc->bindGroupLayouts[0] == nullptr;
})))
.WillOnce(Return(nullptr));
FlushClient();
}