From 4201cdab1886b5c39fe722a95e1b0ba462b63b71 Mon Sep 17 00:00:00 2001 From: Ben Fletcher Date: Thu, 16 Feb 2023 16:46:22 -0800 Subject: rhi: Metal tessellation shader input output interface blocks Add support for shader input output interface blocks in Metal tessellation pipelines. This feature is builtin to other rhi supported tessellation backends (OpenGL/Vulkan). Metal tessellation is implemented as compute pipelines for vert and tesc, and a render pipeline for tese and frag. The shader conversion from GLSL is handled by SPIRV-Cross, which has a particular way of doing things. Rhi must setup the vertex inputs for the tese - frag render pipeline to read from buffers written by the tesc compute pipeline, following SPIRV-Cross conventions. This includes ensuring correct memory alignment per MSL Specification. In order to enable input output interface blocks, reflection of struct members of QShaderDescription::InOutVariable is required. Reflection of QShaderDescription::BuiltinVariable array dimensions is also required to support variable size tese builtin input gl_ClipDistance. An acompanying patch to QtShaderTools is required. Change-Id: Id94e86caef211485afc187bb79fe3d0619d02cf0 Reviewed-by: Qt CI Bot Reviewed-by: Laszlo Agocs --- tests/auto/gui/rhi/qrhi/data/buildshaders.bat | 4 + .../gui/rhi/qrhi/data/tessinterfaceblocks.frag.qsb | Bin 0 -> 590 bytes .../gui/rhi/qrhi/data/tessinterfaceblocks.tesc | 56 ++++ .../gui/rhi/qrhi/data/tessinterfaceblocks.tesc.qsb | Bin 0 -> 2873 bytes .../gui/rhi/qrhi/data/tessinterfaceblocks.tese | 96 +++++++ .../gui/rhi/qrhi/data/tessinterfaceblocks.tese.qsb | Bin 0 -> 4526 bytes .../gui/rhi/qrhi/data/tessinterfaceblocks.vert | 20 ++ .../gui/rhi/qrhi/data/tessinterfaceblocks.vert.qsb | Bin 0 -> 1306 bytes tests/auto/gui/rhi/qrhi/tst_qrhi.cpp | 318 +++++++++++++++++++++ 9 files changed, 494 insertions(+) create mode 100644 tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.frag.qsb create mode 100644 tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tesc create mode 100644 tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tesc.qsb create mode 100644 tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tese create mode 100644 tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tese.qsb create mode 100644 tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.vert create mode 100644 tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.vert.qsb (limited to 'tests/auto/gui/rhi') diff --git a/tests/auto/gui/rhi/qrhi/data/buildshaders.bat b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat index 8fdca5623c..5b07c7bf2b 100644 --- a/tests/auto/gui/rhi/qrhi/data/buildshaders.bat +++ b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat @@ -22,3 +22,7 @@ qsb --glsl 320es,430 --msl 12 --tess-vertex-count 3 storagebuffer_runtime.tese - qsb --glsl 320es,430 --msl 12 storagebuffer_runtime.frag -o storagebuffer_runtime.frag.qsb qsb --glsl 320es,430 --hlsl 50 -c --msl 12 storagebuffer_runtime.comp -o storagebuffer_runtime.comp.qsb qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o half.vert.qsb half.vert +qsb --glsl 320es,430 --msl 21 --msltess tessinterfaceblocks.vert -o tessinterfaceblocks.vert.qsb +qsb --glsl 320es,430 --msl 21 --tess-mode triangles tessinterfaceblocks.tesc -o tessinterfaceblocks.tesc.qsb +qsb --glsl 320es,430 --msl 21 --tess-vertex-count 3 tessinterfaceblocks.tese -o tessinterfaceblocks.tese.qsb +qsb --glsl 320es,430 --msl 21 simpletess.frag -o tessinterfaceblocks.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.frag.qsb b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.frag.qsb new file mode 100644 index 0000000000..7eda4bed2d Binary files /dev/null and b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.frag.qsb differ diff --git a/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tesc b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tesc new file mode 100644 index 0000000000..92a2dc28fa --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tesc @@ -0,0 +1,56 @@ +#version 440 + +layout(vertices = 3) out; + +layout(location = 4) in VertOut +{ + vec3 v_color; + int a; + float b; +}vOut[]; + +layout(location = 5) out TescOutA { + vec3 color; + int id; +}tcOutA[]; + +layout(location = 10) out TescOutB { + vec2 some; + int other[3]; + vec3 variables; +}tcOutB[]; + +layout(location = 2) patch out TescOutC { + vec3 stuff; + float more_stuff; +}tcOutC; + +void main() +{ + // tesc builtin outputs + gl_TessLevelOuter[0] = 1.0; + gl_TessLevelOuter[1] = 2.0; + gl_TessLevelOuter[2] = 3.0; + gl_TessLevelOuter[3] = 4.0; + gl_TessLevelInner[0] = 5.0; + gl_TessLevelInner[1] = 6.0; + + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + gl_out[gl_InvocationID].gl_PointSize = 10 + gl_InvocationID; + gl_out[gl_InvocationID].gl_ClipDistance[0] = 20.0 + gl_InvocationID; + gl_out[gl_InvocationID].gl_ClipDistance[1] = 40.0 + gl_InvocationID; + gl_out[gl_InvocationID].gl_ClipDistance[2] = 60.0 + gl_InvocationID; + gl_out[gl_InvocationID].gl_ClipDistance[3] = 80.0 + gl_InvocationID; + gl_out[gl_InvocationID].gl_ClipDistance[4] = 100.0 + gl_InvocationID; + + // outputs + tcOutA[gl_InvocationID].color = vOut[gl_InvocationID].v_color; + tcOutA[gl_InvocationID].id = gl_InvocationID + 91; + tcOutB[gl_InvocationID].some = vec2(gl_InvocationID, vOut[gl_InvocationID].a); + tcOutB[gl_InvocationID].other[0] = gl_PrimitiveID + 10; + tcOutB[gl_InvocationID].other[1] = gl_PrimitiveID + 20; + tcOutB[gl_InvocationID].other[2] = gl_PrimitiveID + 30; + tcOutB[gl_InvocationID].variables = vec3(3.0f, vOut[gl_InvocationID].b, 17.0f); + tcOutC.stuff = vec3(1.0, 2.0, 3.0); + tcOutC.more_stuff = 4.0; +} diff --git a/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tesc.qsb b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tesc.qsb new file mode 100644 index 0000000000..b503d596c6 Binary files /dev/null and b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tesc.qsb differ diff --git a/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tese b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tese new file mode 100644 index 0000000000..05430a5f63 --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tese @@ -0,0 +1,96 @@ +#version 440 + +layout(triangles, fractional_odd_spacing, ccw) in; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; +}; + +layout(location = 5) in TescOutA { + vec3 color; + int id; +}tcOutA[]; + +layout(location = 10) in TescOutB { + vec2 some; + int other[3]; + vec3 variables; +}tcOutB[]; + +layout(location = 2) patch in TescOutC { + vec3 stuff; + float more_stuff; +}tcOutC; + +layout(location = 0) out vec3 outColor; + +struct A { + vec3 color; + int id; +}; + +struct B { + vec2 some; + int other[3]; + vec3 variables; +}; + +struct C { + vec3 stuff; + float more_stuff; +}; + +struct Element { + A a[3]; + B b[3]; + C c; + vec4 tesslevelOuter; + vec2 tessLevelInner; + float pointSize[3]; + float clipDistance[3][5]; + vec3 tessCoord; + int patchVerticesIn; + int primitiveID; +}; + +layout(std430, binding = 1) buffer result { + int count; + Element elements[]; +}; + +void main() +{ + gl_Position = mvp * ((gl_TessCoord.x * gl_in[0].gl_Position) + (gl_TessCoord.y * gl_in[1].gl_Position) + (gl_TessCoord.z * gl_in[2].gl_Position)); + outColor = gl_TessCoord.x * tcOutA[0].color + gl_TessCoord.y * tcOutA[1].color + gl_TessCoord.z * tcOutA[2].color; + + count = 1; + + elements[gl_PrimitiveID].c.stuff = tcOutC.stuff; + elements[gl_PrimitiveID].c.more_stuff = tcOutC.more_stuff; + elements[gl_PrimitiveID].tesslevelOuter = vec4(gl_TessLevelOuter[0], gl_TessLevelOuter[1], gl_TessLevelOuter[2], gl_TessLevelOuter[3]); + elements[gl_PrimitiveID].tessLevelInner = vec2(gl_TessLevelInner[0], gl_TessLevelInner[1]); + + for (int i = 0; i < 3; ++i) { + + elements[gl_PrimitiveID].a[i].color = tcOutA[i].color; + elements[gl_PrimitiveID].a[i].id = tcOutA[i].id; + + elements[gl_PrimitiveID].b[i].some = tcOutB[i].some; + elements[gl_PrimitiveID].b[i].other = tcOutB[i].other; + elements[gl_PrimitiveID].b[i].variables = tcOutB[i].variables; + + elements[gl_PrimitiveID].pointSize[i] = gl_in[i].gl_PointSize; + elements[gl_PrimitiveID].clipDistance[i][0] = gl_in[i].gl_ClipDistance[0]; + elements[gl_PrimitiveID].clipDistance[i][1] = gl_in[i].gl_ClipDistance[1]; + elements[gl_PrimitiveID].clipDistance[i][2] = gl_in[i].gl_ClipDistance[2]; + elements[gl_PrimitiveID].clipDistance[i][3] = gl_in[i].gl_ClipDistance[3]; + elements[gl_PrimitiveID].clipDistance[i][4] = gl_in[i].gl_ClipDistance[4]; + + } + + elements[gl_PrimitiveID].tessCoord = gl_TessCoord; + elements[gl_PrimitiveID].patchVerticesIn = 3; + elements[gl_PrimitiveID].primitiveID = gl_PrimitiveID; + +} + diff --git a/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tese.qsb b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tese.qsb new file mode 100644 index 0000000000..898bda454a Binary files /dev/null and b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.tese.qsb differ diff --git a/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.vert b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.vert new file mode 100644 index 0000000000..7c722bb374 --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.vert @@ -0,0 +1,20 @@ +#version 440 + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 color; + + +layout(location = 4) out VertOut +{ + vec3 v_color; + int a; + float b; +}; + +void main() +{ + gl_Position = vec4(position, 1.0); + v_color = color; + a = gl_VertexIndex; + b = 13.0f + gl_VertexIndex; +} diff --git a/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.vert.qsb b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.vert.qsb new file mode 100644 index 0000000000..07384d643c Binary files /dev/null and b/tests/auto/gui/rhi/qrhi/data/tessinterfaceblocks.vert.qsb differ diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index f0b9904834..5723d3f8c8 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -145,6 +145,9 @@ private slots: void tessellation_data(); void tessellation(); + void tessellationInterfaceBlocks_data(); + void tessellationInterfaceBlocks(); + void storageBuffer_data(); void storageBuffer(); void storageBufferRuntimeSizeCompute_data(); @@ -5796,6 +5799,321 @@ void tst_QRhi::tessellation() QVERIFY(greenCount > 50); } +void tst_QRhi::tessellationInterfaceBlocks_data() +{ + rhiTestData(); +} + +void tst_QRhi::tessellationInterfaceBlocks() +{ + QFETCH(QRhi::Implementation, impl); + QFETCH(QRhiInitParams *, initParams); + + // This test is intended for Metal, but will run on other tessellation render pipelines + // + // Metal tessellation uses a combination of compute pipelines for the vert and tesc, and a + // render pipeline for the tese and frag. This test uses input output interface blocks between + // the tesc and tese, and all tese stage builtin inputs to check that the Metal tese-frag + // pipeline vertex inputs are correctly configured. The tese writes the values to a storage + // buffer whose values are checked by the unit test. MSL 2.1 is required for this test. + // (requires support for writing to a storage buffer in the vertex shader within a render + // pipeline) + + QScopedPointer rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr)); + if (!rhi) + QSKIP("QRhi could not be created, skipping testing rendering"); + + if (!rhi->isFeatureSupported(QRhi::Tessellation)) { + // From a Vulkan or Metal implementation we expect tessellation to work, + // even though it is optional (as per spec) for Vulkan. + QVERIFY(rhi->backend() != QRhi::Vulkan); + QVERIFY(rhi->backend() != QRhi::Metal); + QSKIP("Tessellation is not supported with this graphics API, skipping test"); + } + + if (rhi->backend() == QRhi::D3D11 || rhi->backend() == QRhi::D3D12) + QSKIP("Skipping tessellation test on D3D for now, test assets not prepared for HLSL yet"); + + if (rhi->backend() == QRhi::OpenGLES2) + QSKIP("Skipping test on OpenGL as gl_ClipDistance[] support inconsistent"); + + QScopedPointer texture( + rhi->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), 1, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + QVERIFY(texture->create()); + + QScopedPointer rt(rhi->newTextureRenderTarget({ texture.data() })); + QScopedPointer rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(rt->create()); + + static const float triangleVertices[] = { + 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, + }; + + QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch(); + QScopedPointer vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, + sizeof(triangleVertices))); + QVERIFY(vbuf->create()); + u->uploadStaticBuffer(vbuf.data(), triangleVertices); + + QScopedPointer ubuf( + rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64)); + QVERIFY(ubuf->create()); + + // Use the 3D API specific correction matrix that flips Y, so we can use + // the OpenGL-targeted vertex data and the tessellation winding order of + // counter-clockwise to get uniform results. + QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix(); + u->updateDynamicBuffer(ubuf.data(), 0, 64, mvp.constData()); + + QScopedPointer buffer( + rhi->newBuffer(QRhiBuffer::Static, QRhiBuffer::UsageFlag::StorageBuffer, 1024)); + QVERIFY(buffer->create()); + + u->uploadStaticBuffer(buffer.data(), 0, 1024, QByteArray(1024, 0).constData()); + + QScopedPointer srb(rhi->newShaderResourceBindings()); + srb->setBindings( + { QRhiShaderResourceBinding::uniformBuffer( + 0, QRhiShaderResourceBinding::TessellationEvaluationStage, ubuf.data()), + QRhiShaderResourceBinding::bufferLoadStore( + 1, QRhiShaderResourceBinding::TessellationEvaluationStage, buffer.data()) }); + QVERIFY(srb->create()); + + QScopedPointer pipeline(rhi->newGraphicsPipeline()); + + pipeline->setTopology(QRhiGraphicsPipeline::Patches); + pipeline->setPatchControlPointCount(3); + + pipeline->setShaderStages( + { { QRhiShaderStage::Vertex, loadShader(":/data/tessinterfaceblocks.vert.qsb") }, + { QRhiShaderStage::TessellationControl, + loadShader(":/data/tessinterfaceblocks.tesc.qsb") }, + { QRhiShaderStage::TessellationEvaluation, + loadShader(":/data/tessinterfaceblocks.tese.qsb") }, + { QRhiShaderStage::Fragment, loadShader(":/data/tessinterfaceblocks.frag.qsb") } }); + + pipeline->setCullMode(QRhiGraphicsPipeline::Back); // to ensure the winding order is correct + + // won't get the wireframe with OpenGL ES + if (rhi->isFeatureSupported(QRhi::NonFillPolygonMode)) + pipeline->setPolygonMode(QRhiGraphicsPipeline::Line); + + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ { 6 * sizeof(float) } }); + inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float3, 3 * sizeof(float) } }); + + pipeline->setVertexInputLayout(inputLayout); + pipeline->setShaderResourceBindings(srb.data()); + pipeline->setRenderPassDescriptor(rpDesc.data()); + + QVERIFY(pipeline->create()); + + QRhiCommandBuffer *cb = nullptr; + QCOMPARE(rhi->beginOffscreenFrame(&cb), QRhi::FrameOpSuccess); + + cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 }, u); + cb->setGraphicsPipeline(pipeline.data()); + cb->setViewport({ 0, 0, float(rt->pixelSize().width()), float(rt->pixelSize().height()) }); + cb->setShaderResources(); + QRhiCommandBuffer::VertexInput vbufBinding(vbuf.data(), 0); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(3); + + QRhiReadbackResult readResult; + QImage result; + readResult.completed = [&readResult, &result] { + result = QImage(reinterpret_cast(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + }; + QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch(); + readbackBatch->readBackTexture({ texture.data() }, &readResult); + + QRhiBufferReadbackResult bufferReadResult; + bufferReadResult.completed = []() {}; + readbackBatch->readBackBuffer(buffer.data(), 0, 1024, &bufferReadResult); + + cb->endPass(readbackBatch); + + rhi->endOffscreenFrame(); + + if (rhi->isYUpInFramebuffer()) // we used clipSpaceCorrMatrix so this is different from many + // other tests + result = std::move(result).mirrored(); + + QCOMPARE(result.size(), rt->pixelSize()); + + // cannot check rendering results with Null, because there is no rendering there + if (impl == QRhi::Null) + return; + + int redCount = 0, greenCount = 0, blueCount = 0; + for (int y = 0; y < result.height(); ++y) { + const quint32 *p = reinterpret_cast(result.constScanLine(y)); + int x = result.width() - 1; + while (x-- >= 0) { + const QRgb c(*p++); + const int red = qRed(c); + const int green = qGreen(c); + const int blue = qBlue(c); + // just count the color components that are above a certain threshold + if (red > 240) + ++redCount; + if (green > 240) + ++greenCount; + if (blue > 240) + ++blueCount; + } + } + + // make sure we drew something + QVERIFY(redCount > 50); + QVERIFY(blueCount > 50); + QVERIFY(greenCount > 50); + + // StorageBlock("result" "" knownSize=16 binding=1 set=0 runtimeArrayStride=336 QList( + // BlockVariable("int" "count" offset=0 size=4), + // BlockVariable("struct" "elements" offset=16 size=0 array=QList(0) structMembers=QList( + // BlockVariable("struct" "a" offset=0 size=48 array=QList(3) structMembers=QList( + // BlockVariable("vec3" "color" offset=0 size=12), + // BlockVariable("int" "id" offset=12 size=4))), + // BlockVariable("struct" "b" offset=48 size=144 array=QList(3) structMembers=QList( + // BlockVariable("vec2" "some" offset=0 size=8), + // BlockVariable("int" "other" offset=8 size=12 array=QList(3)), + // BlockVariable("vec3" "variables" offset=32 size=12))), + // BlockVariable("struct" "c" offset=192 size=16 structMembers=QList( + // BlockVariable("vec3" "stuff" offset=0 size=12), + // BlockVariable("float" "more_stuff" offset=12 size=4))), + // BlockVariable("vec4" "tesslevelOuter" offset=208 size=16), + // BlockVariable("vec2" "tessLevelInner" offset=224 size=8), + // BlockVariable("float" "pointSize" offset=232 size=12 array=QList(3)), + // BlockVariable("float" "clipDistance" offset=244 size=60 array=QList(5, 3)), + // BlockVariable("vec3" "tessCoord" offset=304 size=12), + // BlockVariable("int" "patchVerticesIn" offset=316 size=4), + // BlockVariable("int" "primitiveID" offset=320 size=4))))) + + // int count + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[0])[0], 1); + + // a[0].color + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 0 + 0])[0], 0.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 0 + 0])[1], 0.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 0 + 0])[2], 1.0f); + + // a[0].id + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 0 + 12])[0], 91); + + // a[1].color + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 16 + 0])[0], 1.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 16 + 0])[1], 0.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 16 + 0])[2], 0.0f); + + // a[1].id + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 16 + 12])[0], 92); + + // a[2].color + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 32 + 0])[0], 0.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 32 + 0])[1], 1.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 32 + 0])[2], 0.0f); + + // a[2].id + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 32 + 12])[0], 93); + + // b[0].some + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 48 + 0])[0], 0.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 48 + 0])[1], 0.0f); + + // b[0].other[0] + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 48 + 8])[0], 10.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 48 + 8])[1], 20.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 48 + 8])[2], 30.0f); + + // b[0].variables + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 48 + 32])[0], 3.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 48 + 32])[1], 13.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 48 + 32])[2], 17.0f); + + // b[1].some + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 96 + 0])[0], 1.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 96 + 0])[1], 1.0f); + + // b[1].other[0] + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 96 + 8])[0], 10.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 96 + 8])[1], 20.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 96 + 8])[2], 30.0f); + + // b[1].variables + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 96 + 32])[0], 3.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 96 + 32])[1], 14.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 96 + 32])[2], 17.0f); + + // b[2].some + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 144 + 0])[0], 2.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 144 + 0])[1], 2.0f); + + // b[2].other[0] + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 144 + 8])[0], 10.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 144 + 8])[1], 20.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 144 + 8])[2], 30.0f); + + // b[2].variables + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 144 + 32])[0], 3.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 144 + 32])[1], 15.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 144 + 32])[2], 17.0f); + + // c.stuff + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 192 + 0])[0], 1.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 192 + 0])[1], 2.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 192 + 0])[2], 3.0f); + + // c.more_stuff + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 192 + 12])[0], 4.0f); + + // tessLevelOuter + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 208 + 0])[0], 1.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 208 + 0])[1], 2.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 208 + 0])[2], 3.0f); + + // tessLevelInner + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 224 + 0])[0], 5.0f); + + // pointSize[0] + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 232 + 0])[0], 10.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 232 + 0])[1], 11.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 232 + 0])[2], 12.0f); + + // clipDistance[0][0] + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 0])[0], 20.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 0])[1], 40.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 0])[2], 60.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 0])[3], 80.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 0])[4], 100.0f); + + // clipDistance[1][0] + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 20])[0], 21.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 20])[1], 41.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 20])[2], 61.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 20])[3], 81.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 20])[4], 101.0f); + + // clipDistance[2][0] + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 40])[0], 22.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 40])[1], 42.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 40])[2], 62.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 40])[3], 82.0f); + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 244 + 40])[4], 102.0f); + + // patchVerticesIn + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 316 + 0])[0], 3); + + // primitiveID + QCOMPARE(reinterpret_cast(&bufferReadResult.data.constData()[16 + 320 + 0])[0], 0); +} + void tst_QRhi::storageBuffer_data() { rhiTestData(); -- cgit v1.2.3