Wire: Add support from optional struct/integer const*
This will allow implementing the following part of the WebGPU IDL using
a nullable pointer instead of an extra hasDepthStencilState boolean:
partial interface GPURenderPipelineDescriptor {
GPUDepthStencilStateDescriptor? depthStencilState;
};
BUG=dawn:102
Change-Id: Iae709831ad857fcef073f18753ab39567a8797da
Reviewed-on: https://dawn-review.googlesource.com/c/4500
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Yunchao He <yunchao.he@intel.com>
diff --git a/dawn.json b/dawn.json
index 84c8dbe..b72217f 100644
--- a/dawn.json
+++ b/dawn.json
@@ -863,7 +863,7 @@
{"name": "primitive topology", "type": "primitive topology"},
{"name": "attachments state", "type": "attachments state descriptor", "annotation": "const*"},
{"name": "sample count", "type": "uint32_t"},
- {"name": "depth stencil state", "type": "depth stencil state descriptor", "annotation": "const*"},
+ {"name": "depth stencil state", "type": "depth stencil state descriptor", "annotation": "const*", "optional": true},
{"name": "num blend states", "type": "uint32_t"},
{"name": "blend states", "type": "blend state descriptor", "annotation": "const*", "length": "num blend states"}
]
diff --git a/generator/templates/dawn_wire/WireCmd.cpp b/generator/templates/dawn_wire/WireCmd.cpp
index 75ac7f4..0b3f95c 100644
--- a/generator/templates/dawn_wire/WireCmd.cpp
+++ b/generator/templates/dawn_wire/WireCmd.cpp
@@ -105,6 +105,10 @@
{% for member in members if member.length == "strlen" %}
size_t {{as_varName(member.name)}}Strlen;
{% endfor %}
+
+ {% for member in members if member.annotation != "value" and member.type.category != "object" %}
+ bool has_{{as_varName(member.name)}};
+ {% endfor %}
};
//* Returns the required transfer size for `record` in addition to the transfer structure.
@@ -120,6 +124,9 @@
//* Gather how much space will be needed for pointer members.
{% for member in members if member.annotation != "value" and member.length != "strlen" %}
+ {% if member.type.category != "object" and member.optional %}
+ if (record.{{as_varName(member.name)}} != nullptr)
+ {% endif %}
{
size_t memberLength = {{member_length(member, "record.")}};
result += memberLength * {{member_transfer_sizeof(member)}};
@@ -176,6 +183,12 @@
//* Allocate space and write the non-value arguments in it.
{% for member in members if member.annotation != "value" and member.length != "strlen" %}
{% set memberName = as_varName(member.name) %}
+
+ {% if member.type.category != "object" and member.optional %}
+ bool has_{{memberName}} = record.{{memberName}} != nullptr;
+ transfer->has_{{memberName}} = has_{{memberName}};
+ if (has_{{memberName}})
+ {% endif %}
{
size_t memberLength = {{member_length(member, "record.")}};
auto memberBuffer = reinterpret_cast<{{member_transfer_type(member)}}*>(*buffer);
@@ -277,6 +290,12 @@
//* Get extra buffer data, and copy pointed to values in extra allocated space.
{% for member in members if member.annotation != "value" and member.length != "strlen" %}
{% set memberName = as_varName(member.name) %}
+
+ {% if member.type.category != "object" and member.optional %}
+ bool has_{{memberName}} = transfer->has_{{memberName}};
+ record->{{memberName}} = nullptr;
+ if (has_{{memberName}})
+ {% endif %}
{
size_t memberLength = {{member_length(member, "record->")}};
auto memberBuffer = reinterpret_cast<const {{member_transfer_type(member)}}*>(buffer);
diff --git a/src/tests/unittests/WireTests.cpp b/src/tests/unittests/WireTests.cpp
index ccabc40..27af06f 100644
--- a/src/tests/unittests/WireTests.cpp
+++ b/src/tests/unittests/WireTests.cpp
@@ -439,6 +439,141 @@
FlushClient();
}
+// Test that the wire is able to send optional pointers to structures
+TEST_F(WireTests, OptionalStructPointer) {
+ // Create shader module
+ dawnShaderModuleDescriptor vertexDescriptor;
+ vertexDescriptor.nextInChain = nullptr;
+ vertexDescriptor.codeSize = 0;
+ dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor);
+ dawnShaderModule apiVsModule = api.GetNewShaderModule();
+ EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _))
+ .WillOnce(Return(apiVsModule));
+
+ // Create the blend state descriptor
+ dawnBlendDescriptor blendDescriptor;
+ blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD;
+ blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE;
+ blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE;
+ dawnBlendStateDescriptor blendStateDescriptor;
+ blendStateDescriptor.nextInChain = nullptr;
+ blendStateDescriptor.alphaBlend = blendDescriptor;
+ blendStateDescriptor.colorBlend = blendDescriptor;
+ blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL;
+
+ // Create the input state
+ dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device);
+ dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder();
+ EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice))
+ .WillOnce(Return(apiInputStateBuilder));
+
+ dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder);
+ dawnInputState apiInputState = api.GetNewInputState();
+ EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder))
+ .WillOnce(Return(apiInputState));
+
+ // Create the depth-stencil state
+ dawnStencilStateFaceDescriptor stencilFace;
+ stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS;
+ stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP;
+ stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP;
+ stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP;
+
+ dawnDepthStencilStateDescriptor depthStencilState;
+ depthStencilState.nextInChain = nullptr;
+ depthStencilState.depthWriteEnabled = false;
+ depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS;
+ depthStencilState.stencilBack = stencilFace;
+ depthStencilState.stencilFront = stencilFace;
+ depthStencilState.stencilReadMask = 0xff;
+ depthStencilState.stencilWriteMask = 0xff;
+
+ // Create the pipeline layout
+ dawnPipelineLayoutDescriptor layoutDescriptor;
+ layoutDescriptor.nextInChain = nullptr;
+ layoutDescriptor.numBindGroupLayouts = 0;
+ layoutDescriptor.bindGroupLayouts = nullptr;
+ dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor);
+ dawnPipelineLayout apiLayout = api.GetNewPipelineLayout();
+ EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _))
+ .WillOnce(Return(apiLayout));
+
+ // Create pipeline
+ dawnRenderPipelineDescriptor pipelineDescriptor;
+ pipelineDescriptor.nextInChain = nullptr;
+
+ dawnPipelineStageDescriptor vertexStage;
+ vertexStage.nextInChain = nullptr;
+ vertexStage.module = vsModule;
+ vertexStage.entryPoint = "main";
+ pipelineDescriptor.vertexStage = &vertexStage;
+
+ dawnPipelineStageDescriptor fragmentStage;
+ fragmentStage.nextInChain = nullptr;
+ fragmentStage.module = vsModule;
+ fragmentStage.entryPoint = "main";
+ pipelineDescriptor.fragmentStage = &fragmentStage;
+
+ dawnAttachmentsStateDescriptor attachmentsState;
+ attachmentsState.nextInChain = nullptr;
+ attachmentsState.numColorAttachments = 1;
+ dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM};
+ dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment};
+ attachmentsState.colorAttachments = colorAttachmentPtr;
+ attachmentsState.hasDepthStencilAttachment = false;
+ // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid
+ // data because we don't have optional substructures yet.
+ attachmentsState.depthStencilAttachment = &colorAttachment;
+ pipelineDescriptor.attachmentsState = &attachmentsState;
+
+ pipelineDescriptor.numBlendStates = 1;
+ pipelineDescriptor.blendStates = &blendStateDescriptor;
+
+ pipelineDescriptor.sampleCount = 1;
+ pipelineDescriptor.layout = layout;
+ pipelineDescriptor.inputState = inputState;
+ pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32;
+ pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ // First case: depthStencilState is not null.
+ pipelineDescriptor.depthStencilState = &depthStencilState;
+ dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor);
+ EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool {
+ return desc->depthStencilState != nullptr &&
+ desc->depthStencilState->nextInChain == nullptr &&
+ desc->depthStencilState->depthWriteEnabled == false &&
+ desc->depthStencilState->depthCompare == DAWN_COMPARE_FUNCTION_ALWAYS &&
+ desc->depthStencilState->stencilBack.compare == DAWN_COMPARE_FUNCTION_ALWAYS &&
+ desc->depthStencilState->stencilBack.failOp == DAWN_STENCIL_OPERATION_KEEP &&
+ desc->depthStencilState->stencilBack.depthFailOp == DAWN_STENCIL_OPERATION_KEEP &&
+ desc->depthStencilState->stencilBack.passOp == DAWN_STENCIL_OPERATION_KEEP &&
+ desc->depthStencilState->stencilFront.compare == DAWN_COMPARE_FUNCTION_ALWAYS &&
+ desc->depthStencilState->stencilFront.failOp == DAWN_STENCIL_OPERATION_KEEP &&
+ desc->depthStencilState->stencilFront.depthFailOp == DAWN_STENCIL_OPERATION_KEEP &&
+ desc->depthStencilState->stencilFront.passOp == DAWN_STENCIL_OPERATION_KEEP &&
+ desc->depthStencilState->stencilReadMask == 0xff &&
+ desc->depthStencilState->stencilWriteMask == 0xff;
+ })))
+ .WillOnce(Return(nullptr));
+
+ FlushClient();
+
+ // Second case: depthStencilState is null.
+ pipelineDescriptor.depthStencilState = nullptr;
+ dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor);
+ EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool {
+ return desc->depthStencilState == nullptr;
+ })))
+ .WillOnce(Return(nullptr));
+
+ EXPECT_CALL(api, ShaderModuleRelease(apiVsModule));
+ EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder));
+ EXPECT_CALL(api, InputStateRelease(apiInputState));
+ EXPECT_CALL(api, PipelineLayoutRelease(apiLayout));
+
+ FlushClient();
+}
+
// Test that the wire is able to send objects as value arguments
TEST_F(WireTests, ObjectAsValueArgument) {
// Create a RenderPassDescriptor