From f872e6924cbb7ff75738b9ad6560cdb789a91ac1 Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Wed, 13 Feb 2019 10:15:38 +0000 Subject: [PATCH] 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 Reviewed-by: Kai Ninomiya Reviewed-by: Yunchao He --- dawn.json | 2 +- generator/templates/dawn_wire/WireCmd.cpp | 19 +++ src/tests/unittests/WireTests.cpp | 135 ++++++++++++++++++++++ 3 files changed, 155 insertions(+), 1 deletion(-) diff --git a/dawn.json b/dawn.json index 84c8dbecf4..b72217f3ff 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 75ac7f4a22..0b3f95c674 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(buffer); diff --git a/src/tests/unittests/WireTests.cpp b/src/tests/unittests/WireTests.cpp index ccabc406e2..27af06f34d 100644 --- a/src/tests/unittests/WireTests.cpp +++ b/src/tests/unittests/WireTests.cpp @@ -439,6 +439,141 @@ TEST_F(WireTests, CStringArgument) { 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