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 | |