| 1 | // Copyright (C) 2008-2012 NVIDIA Corporation. |
| 2 | // Copyright (C) 2020 The Qt Company Ltd. |
| 3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
| 4 | |
| 5 | #include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h> |
| 6 | #include <QtQuick3DUtils/private/qquick3dprofiler_p.h> |
| 7 | |
| 8 | QT_BEGIN_NAMESPACE |
| 9 | |
| 10 | static const QVector3D g_fullScreenRectFaces[] = { |
| 11 | QVector3D(-1, -1, 0), |
| 12 | QVector3D(-1, 1, 0), |
| 13 | QVector3D(1, 1, 0), |
| 14 | QVector3D(1, -1, 0), |
| 15 | |
| 16 | QVector3D(-1, -1, 1), |
| 17 | QVector3D(-1, 1, 1), |
| 18 | QVector3D(1, 1, 1), |
| 19 | QVector3D(1, -1, 1), |
| 20 | |
| 21 | QVector3D(-1, -1, -1), |
| 22 | QVector3D(-1, 1, -1), |
| 23 | QVector3D(1, 1, -1), |
| 24 | QVector3D(1, -1, -1), |
| 25 | }; |
| 26 | |
| 27 | static const QVector2D g_fullScreenRectUVs[] = { |
| 28 | QVector2D(0, 0), |
| 29 | QVector2D(0, 1), |
| 30 | QVector2D(1, 1), |
| 31 | QVector2D(1, 0) |
| 32 | }; |
| 33 | |
| 34 | static const quint16 g_rectIndex[] = { |
| 35 | 0, 1, 2, 0, 2, 3, // front face - 0, 1, 2, 3 |
| 36 | 0, 4, 5, 0, 5, 1, // left face - 0, 4, 5, 1 |
| 37 | 1, 5, 6, 1, 6, 2, // top face - 1, 5, 6, 2 |
| 38 | 3, 2, 6, 3, 6, 7, // right face - 3, 2, 6, 7 |
| 39 | 0, 3, 7, 0, 7, 4, // bottom face - 0, 3, 7, 4 |
| 40 | 7, 6, 5, 7, 5, 4 // back face - 7, 6, 5, 4 |
| 41 | }; |
| 42 | |
| 43 | void QSSGRhiQuadRenderer::ensureBuffers(QSSGRhiContext *rhiCtx, QRhiResourceUpdateBatch *rub) |
| 44 | { |
| 45 | if (!m_vbuf) { |
| 46 | constexpr int vertexCount = 8; |
| 47 | m_vbuf = std::make_shared<QSSGRhiBuffer>(args&: *rhiCtx, |
| 48 | args: QRhiBuffer::Immutable, |
| 49 | args: QRhiBuffer::VertexBuffer, |
| 50 | args: quint32(5 * sizeof(float)), |
| 51 | args: 5 * vertexCount * sizeof(float)); |
| 52 | m_vbuf->buffer()->setName(QByteArrayLiteral("quad vertex buffer" )); |
| 53 | float buf[5 * vertexCount]; |
| 54 | float *p = buf; |
| 55 | for (int i = 0; i < vertexCount; ++i) { |
| 56 | *p++ = g_fullScreenRectFaces[i].x(); |
| 57 | *p++ = g_fullScreenRectFaces[i].y(); |
| 58 | *p++ = g_fullScreenRectFaces[i].z(); |
| 59 | *p++ = g_fullScreenRectUVs[i % 4].x(); |
| 60 | *p++ = g_fullScreenRectUVs[i % 4].y(); |
| 61 | } |
| 62 | rub->uploadStaticBuffer(buf: m_vbuf->buffer(), data: buf); |
| 63 | } |
| 64 | if (!m_ibuf) { |
| 65 | m_ibuf = std::make_shared<QSSGRhiBuffer>(args&: *rhiCtx, |
| 66 | args: QRhiBuffer::Immutable, |
| 67 | args: QRhiBuffer::IndexBuffer, |
| 68 | args: 0, |
| 69 | args: 6 * sizeof(quint16), |
| 70 | args: QRhiCommandBuffer::IndexUInt16); |
| 71 | m_ibuf->buffer()->setName(QByteArrayLiteral("quad index buffer" )); |
| 72 | const quint16 buf[] = { 0, 1, 2, 0, 2, 3 }; |
| 73 | rub->uploadStaticBuffer(buf: m_ibuf->buffer(), data: buf); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | void QSSGRhiQuadRenderer::prepareQuad(QSSGRhiContext *rhiCtx, QRhiResourceUpdateBatch *maybeRub) |
| 78 | { |
| 79 | QRhiResourceUpdateBatch *rub = maybeRub ? maybeRub : rhiCtx->rhi()->nextResourceUpdateBatch(); |
| 80 | ensureBuffers(rhiCtx, rub); |
| 81 | rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates: rub); |
| 82 | } |
| 83 | |
| 84 | void QSSGRhiQuadRenderer::recordRenderQuad(QSSGRhiContext *rhiCtx, |
| 85 | QSSGRhiGraphicsPipelineState *ps, QRhiShaderResourceBindings *srb, |
| 86 | QRhiRenderPassDescriptor *rpDesc, Flags flags) |
| 87 | { |
| 88 | // ps must have viewport and shaderPipeline set already |
| 89 | auto &ia = QSSGRhiInputAssemblerStatePrivate::get(ps&: *ps); |
| 90 | if (flags.testFlag(flag: UvCoords)) { |
| 91 | ia.inputLayout.setAttributes({ |
| 92 | { 0, 0, QRhiVertexInputAttribute::Float3, 0 }, |
| 93 | { 0, 1, QRhiVertexInputAttribute::Float2, 3 * sizeof(float) } |
| 94 | }); |
| 95 | ia.inputs << QSSGRhiInputAssemblerState::PositionSemantic << QSSGRhiInputAssemblerState::TexCoord0Semantic; |
| 96 | } else { |
| 97 | ia.inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } }); |
| 98 | ia.inputs << QSSGRhiInputAssemblerState::PositionSemantic; |
| 99 | } |
| 100 | ia.inputLayout.setBindings({ 5 * sizeof(float) }); |
| 101 | ia.topology = QRhiGraphicsPipeline::Triangles; |
| 102 | |
| 103 | ps->flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, on: flags.testFlag(flag: DepthTest)); |
| 104 | ps->flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: flags.testFlag(flag: DepthWrite)); |
| 105 | ps->cullMode = QRhiGraphicsPipeline::None; |
| 106 | if (flags.testFlag(flag: PremulBlend)) { |
| 107 | ps->flags |= QSSGRhiGraphicsPipelineState::Flag::BlendEnabled; |
| 108 | ps->targetBlend.srcColor = QRhiGraphicsPipeline::One; |
| 109 | ps->targetBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha; |
| 110 | ps->targetBlend.srcAlpha = QRhiGraphicsPipeline::One; |
| 111 | ps->targetBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha; |
| 112 | } else { // set to default, since we may not have had a renderable previously |
| 113 | ps->targetBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha; |
| 114 | ps->targetBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha; |
| 115 | ps->targetBlend.srcAlpha = QRhiGraphicsPipeline::One; |
| 116 | ps->targetBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha; |
| 117 | } |
| 118 | |
| 119 | QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx); |
| 120 | QRhiGraphicsPipeline *pipeline = rhiCtxD->pipeline(ps: *ps, rpDesc, srb); |
| 121 | // Make sure that we were able to create the pipeline before trying to use it |
| 122 | // When GraphicsPipeline creation fails it should return nullptr and print a warning |
| 123 | if (!pipeline) |
| 124 | return; |
| 125 | |
| 126 | QRhiCommandBuffer *cb = rhiCtx->commandBuffer(); |
| 127 | cb->setGraphicsPipeline(pipeline); |
| 128 | cb->setShaderResources(srb); |
| 129 | cb->setViewport(ps->viewport); |
| 130 | |
| 131 | quint32 vertexOffset = flags.testAnyFlags(flags: RenderBehind) ? 5 * 4 * sizeof(float) : 0; |
| 132 | |
| 133 | QRhiCommandBuffer::VertexInput vb(m_vbuf->buffer(), vertexOffset); |
| 134 | Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall); |
| 135 | cb->setVertexInput(startBinding: 0, bindingCount: 1, bindings: &vb, indexBuf: m_ibuf->buffer(), indexOffset: m_ibuf->indexFormat()); |
| 136 | cb->drawIndexed(indexCount: 6); |
| 137 | QSSGRHICTX_STAT(rhiCtx, drawIndexed(6, 1)); |
| 138 | Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), QByteArrayLiteral("render_quad" )); |
| 139 | } |
| 140 | |
| 141 | void QSSGRhiQuadRenderer::recordRenderQuadPass(QSSGRhiContext *rhiCtx, |
| 142 | QSSGRhiGraphicsPipelineState *ps, QRhiShaderResourceBindings *srb, |
| 143 | QRhiTextureRenderTarget *rt, Flags flags) |
| 144 | { |
| 145 | QRhiCommandBuffer *cb = rhiCtx->commandBuffer(); |
| 146 | cb->beginPass(rt, colorClearValue: Qt::black, depthStencilClearValue: { 1.0f, 0 }, resourceUpdates: nullptr, flags: rhiCtx->commonPassFlags()); |
| 147 | QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rt)); |
| 148 | recordRenderQuad(rhiCtx, ps, srb, rpDesc: rt->renderPassDescriptor(), flags); |
| 149 | cb->endPass(); |
| 150 | QSSGRHICTX_STAT(rhiCtx, endRenderPass()); |
| 151 | } |
| 152 | |
| 153 | void QSSGRhiCubeRenderer::prepareCube(QSSGRhiContext *rhiCtx, QRhiResourceUpdateBatch *maybeRub) |
| 154 | { |
| 155 | QRhiResourceUpdateBatch *rub = maybeRub ? maybeRub : rhiCtx->rhi()->nextResourceUpdateBatch(); |
| 156 | ensureBuffers(rhiCtx, rub); |
| 157 | rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates: rub); |
| 158 | } |
| 159 | |
| 160 | //### The flags UvCoords and RenderBehind are ignored |
| 161 | void QSSGRhiCubeRenderer::recordRenderCube(QSSGRhiContext *rhiCtx, QSSGRhiGraphicsPipelineState *ps, QRhiShaderResourceBindings *srb, QRhiRenderPassDescriptor *rpDesc, QSSGRhiQuadRenderer::Flags flags) |
| 162 | { |
| 163 | auto &ia = QSSGRhiInputAssemblerStatePrivate::get(ps&: *ps); |
| 164 | // ps must have viewport and shaderPipeline set already |
| 165 | ia.inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } }); |
| 166 | ia.inputs << QSSGRhiInputAssemblerState::PositionSemantic; |
| 167 | ia.inputLayout.setBindings({ 3 * sizeof(float) }); |
| 168 | ia.topology = QRhiGraphicsPipeline::Triangles; |
| 169 | |
| 170 | ps->flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, on: flags.testFlag(flag: QSSGRhiQuadRenderer::DepthTest)); |
| 171 | ps->flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: flags.testFlag(flag: QSSGRhiQuadRenderer::DepthWrite)); |
| 172 | ps->cullMode = QRhiGraphicsPipeline::None; |
| 173 | if (flags.testFlag(flag: QSSGRhiQuadRenderer::PremulBlend)) { |
| 174 | ps->flags |= QSSGRhiGraphicsPipelineState::Flag::BlendEnabled; |
| 175 | ps->targetBlend.srcColor = QRhiGraphicsPipeline::One; |
| 176 | ps->targetBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha; |
| 177 | ps->targetBlend.srcAlpha = QRhiGraphicsPipeline::One; |
| 178 | ps->targetBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha; |
| 179 | } else { // set to default, since we may not have had a renderable previously |
| 180 | ps->targetBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha; |
| 181 | ps->targetBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha; |
| 182 | ps->targetBlend.srcAlpha = QRhiGraphicsPipeline::One; |
| 183 | ps->targetBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha; |
| 184 | } |
| 185 | |
| 186 | QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx); |
| 187 | QRhiGraphicsPipeline *pipeline = rhiCtxD->pipeline(ps: *ps, rpDesc, srb); |
| 188 | // Make sure that we were able to create the pipeline before trying to use it |
| 189 | // When GraphicsPipeline creation fails it should return nullptr and print a warning |
| 190 | if (!pipeline) |
| 191 | return; |
| 192 | |
| 193 | QRhiCommandBuffer *cb = rhiCtx->commandBuffer(); |
| 194 | cb->setGraphicsPipeline(pipeline); |
| 195 | cb->setShaderResources(srb); |
| 196 | cb->setViewport(ps->viewport); |
| 197 | |
| 198 | QRhiCommandBuffer::VertexInput vb(m_vbuf->buffer(), 0); |
| 199 | Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall); |
| 200 | cb->setVertexInput(startBinding: 0, bindingCount: 1, bindings: &vb, indexBuf: m_ibuf->buffer(), indexOffset: m_ibuf->indexFormat()); |
| 201 | cb->drawIndexed(indexCount: 36); |
| 202 | QSSGRHICTX_STAT(rhiCtx, drawIndexed(36, 1)); |
| 203 | Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderCall, 36, QByteArrayLiteral("render_cube" )); |
| 204 | } |
| 205 | |
| 206 | void QSSGRhiCubeRenderer::ensureBuffers(QSSGRhiContext *rhiCtx, QRhiResourceUpdateBatch *rub) |
| 207 | { |
| 208 | if (!m_vbuf) { |
| 209 | constexpr int vertexCount = 8; |
| 210 | m_vbuf = std::make_shared<QSSGRhiBuffer>(args&: *rhiCtx, |
| 211 | args: QRhiBuffer::Immutable, |
| 212 | args: QRhiBuffer::VertexBuffer, |
| 213 | args: quint32(3 * sizeof(float)), |
| 214 | args: 3 * vertexCount * sizeof(float)); |
| 215 | m_vbuf->buffer()->setName(QByteArrayLiteral("cube vertex buffer" )); |
| 216 | |
| 217 | float buf[3 * vertexCount]; |
| 218 | float *p = buf; |
| 219 | for (int i = 0; i < vertexCount; ++i) { |
| 220 | *p++ = g_fullScreenRectFaces[4 + i].x(); |
| 221 | *p++ = g_fullScreenRectFaces[4 + i].y(); |
| 222 | *p++ = g_fullScreenRectFaces[4 + i].z(); |
| 223 | } |
| 224 | rub->uploadStaticBuffer(buf: m_vbuf->buffer(), data: buf); |
| 225 | } |
| 226 | if (!m_ibuf) { |
| 227 | m_ibuf = std::make_shared<QSSGRhiBuffer>(args&: *rhiCtx, |
| 228 | args: QRhiBuffer::Immutable, |
| 229 | args: QRhiBuffer::IndexBuffer, |
| 230 | args: 0, |
| 231 | args: sizeof(g_rectIndex), |
| 232 | args: QRhiCommandBuffer::IndexUInt16); |
| 233 | m_ibuf->buffer()->setName(QByteArrayLiteral("cube index buffer" )); |
| 234 | rub->uploadStaticBuffer(buf: m_ibuf->buffer(), data: g_rectIndex); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | QT_END_NAMESPACE |
| 239 | |