Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgrhiparticles.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
6
7#include <qfloat16.h>
8
9#include <QtQuick3DUtils/private/qssgutils_p.h>
10
11#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
13#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
14
15QT_BEGIN_NAMESPACE
16
17static const QRhiShaderResourceBinding::StageFlags VISIBILITY_ALL =
18 QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
19
21{
23 float pointLightConstantAtt[4] = {1.0f, 1.0f, 1.0f, 1.0f};
24 float pointLightLinearAtt[4] = {0.0f};
25 float pointLightQuadAtt[4] = {0.0f};
28 float spotLightConstantAtt[4] = {1.0f, 1.0f, 1.0f, 1.0f};
29 float spotLightLinearAtt[4] = {0.0f};
30 float spotLightQuadAtt[4] = {0.0f};
33 float spotLightConeAngle[4] = {0.0f};
34 float spotLightInnerConeAngle[4] = {0.0f};
35};
36
37void QSSGParticleRenderer::updateUniformsForParticles(const QSSGLayerRenderData &inData,
38 QSSGRhiShaderPipeline &shaders,
39 QSSGRhiContext *rhiCtx,
40 char *ubufData,
41 QSSGParticlesRenderable &renderable,
42 const QSSGRenderCameraList &cameras)
43{
44 const QMatrix4x4 clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
45
46 QSSGRhiShaderPipeline::CommonUniformIndices &cui = shaders.commonUniformIndices;
47
48 const int viewCount = cameras.count();
49
50 // Pull the camera transforms from the render data once.
51 QMatrix4x4 camGlobalTransforms[2] { QMatrix4x4{Qt::Uninitialized}, QMatrix4x4{Qt::Uninitialized} };
52 if (viewCount < 2) {
53 camGlobalTransforms[0] = inData.getGlobalTransform(*cameras[0]);
54 } else {
55 for (size_t viewIndex = 0; viewIndex != std::size(camGlobalTransforms); ++viewIndex)
56 camGlobalTransforms[viewIndex] = inData.getGlobalTransform(*cameras[viewIndex]);
57 }
58
59 if (viewCount < 2) {
60 const QMatrix4x4 projection = clipSpaceCorrMatrix * cameras[0]->projection;
61 shaders.setUniform(ubufData, "qt_projectionMatrix", projection.constData(), 16 * sizeof(float), &cui.projectionMatrixIdx);
62 const QMatrix4x4 viewMatrix = camGlobalTransforms[0].inverted();
63 shaders.setUniform(ubufData, "qt_viewMatrix", viewMatrix.constData(), 16 * sizeof(float), &cui.viewMatrixIdx);
64 } else {
65 QVarLengthArray<QMatrix4x4, 2> projectionMatrices(viewCount);
66 QVarLengthArray<QMatrix4x4, 2> viewMatrices(viewCount);
67 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
68 projectionMatrices[viewIndex] = clipSpaceCorrMatrix * cameras[viewIndex]->projection;
69 viewMatrices[viewIndex] = camGlobalTransforms[viewIndex].inverted();
70 }
71 shaders.setUniformArray(ubufData, "qt_projectionMatrix", projectionMatrices.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.projectionMatrixIdx);
72 shaders.setUniformArray(ubufData, "qt_viewMatrix", viewMatrices.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.viewMatrixIdx);
73 }
74
75 const QMatrix4x4 &modelMatrix = renderable.globalTransform;
76 shaders.setUniform(ubufData, "qt_modelMatrix", modelMatrix.constData(), 16 * sizeof(float), &cui.modelMatrixIdx);
77
78 const QVector2D camProperties(cameras[0]->clipPlanes);
79 shaders.setUniform(ubufData, "qt_cameraProperties", &camProperties, 2 * sizeof(float), &cui.cameraPropertiesIdx);
80
81 QVector2D oneOverSize = QVector2D(1.0f, 1.0f);
82 auto &particleBuffer = renderable.particles.m_particleBuffer;
83 const quint32 particlesPerSlice = particleBuffer.particlesPerSlice();
84 oneOverSize = QVector2D(1.0f / particleBuffer.size().width(), 1.0f / particleBuffer.size().height());
85 shaders.setUniform(ubufData, "qt_oneOverParticleImageSize", &oneOverSize, 2 * sizeof(float));
86 shaders.setUniform(ubufData, "qt_countPerSlice", &particlesPerSlice, 1 * sizeof(quint32));
87
88 // Global opacity of the particles node
89 shaders.setUniform(ubufData, "qt_opacity", &renderable.opacity, 1 * sizeof(float));
90
91 float blendImages = renderable.particles.m_blendImages ? 1.0f : 0.0f;
92 float imageCount = float(renderable.particles.m_spriteImageCount);
93 float ooImageCount = 1.0f / imageCount;
94
95 QVector4D spriteConfig(imageCount, ooImageCount, 0.0f, blendImages);
96 shaders.setUniform(ubufData, "qt_spriteConfig", &spriteConfig, 4 * sizeof(float));
97
98 const float billboard = renderable.particles.m_billboard ? 1.0f : 0.0f;
99 shaders.setUniform(ubufData, "qt_billboard", &billboard, 1 * sizeof(float));
100
101 // Lights
102 QVector3D theLightAmbientTotal;
103 bool hasLights = !renderable.particles.m_lights.isEmpty();
104 int pointLight = 0;
105 int spotLight = 0;
106 if (hasLights) {
107 ParticleLightData lightData;
108 auto &lights = renderable.lights;
109 for (quint32 lightIdx = 0, lightEnd = lights.size();
110 lightIdx < lightEnd && lightIdx < QSSG_MAX_NUM_LIGHTS; ++lightIdx) {
111 QSSGRenderLight *theLight(lights[lightIdx].light);
112 // Ignore lights which are not specified for the particle
113 if (!renderable.particles.m_lights.contains(theLight))
114 continue;
115
116 const QMatrix4x4 lightGlobalTransform = inData.getGlobalTransform(*theLight);
117
118 if (theLight->type == QSSGRenderLight::Type::DirectionalLight) {
119 theLightAmbientTotal += theLight->m_diffuseColor * theLight->m_brightness;
120 } else if (theLight->type == QSSGRenderLight::Type::PointLight && pointLight < 4) {
121 lightData.pointLightColor[pointLight] = QVector4D(theLight->m_diffuseColor * theLight->m_brightness, 1.0f);
122 lightData.pointLightPos[pointLight] = QVector4D(QSSGRenderNode::getGlobalPos(lightGlobalTransform), 1.0f);
123 lightData.pointLightConstantAtt[pointLight] = QSSGUtils::aux::translateConstantAttenuation(theLight->m_constantFade);
124 lightData.pointLightLinearAtt[pointLight] = QSSGUtils::aux::translateLinearAttenuation(theLight->m_linearFade);
125 lightData.pointLightQuadAtt[pointLight] = QSSGUtils::aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
126 pointLight++;
127 } else if (theLight->type == QSSGRenderLight::Type::SpotLight && spotLight < 4) {
128 lightData.spotLightColor[spotLight] = QVector4D(theLight->m_diffuseColor * theLight->m_brightness, 1.0f);
129 lightData.spotLightPos[spotLight] = QVector4D(QSSGRenderNode::getGlobalPos(lightGlobalTransform), 1.0f);
130 lightData.spotLightDir[spotLight] = QVector4D(lights[lightIdx].direction, 0.0f);
131 lightData.spotLightConstantAtt[spotLight] = QSSGUtils::aux::translateConstantAttenuation(theLight->m_constantFade);
132 lightData.spotLightLinearAtt[spotLight] = QSSGUtils::aux::translateLinearAttenuation(theLight->m_linearFade);
133 lightData.spotLightQuadAtt[spotLight] = QSSGUtils::aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
134 float coneAngle = theLight->m_coneAngle;
135 // Inner cone angle must always be < cone angle, to not have possible undefined behavior for shader smoothstep
136 float innerConeAngle = std::min(theLight->m_innerConeAngle, coneAngle - 0.01f);
137 lightData.spotLightConeAngle[spotLight] = qDegreesToRadians(coneAngle);
138 lightData.spotLightInnerConeAngle[spotLight] = qDegreesToRadians(innerConeAngle);
139 spotLight++;
140 }
141 theLightAmbientTotal += theLight->m_ambientColor;
142 }
143 // Copy light data
144 int lightOffset = shaders.offsetOfUniform("qt_pointLightPosition");
145 if (lightOffset >= 0)
146 memcpy(ubufData + lightOffset, &lightData, sizeof(ParticleLightData));
147 }
148 shaders.setUniform(ubufData, "qt_light_ambient_total", &theLightAmbientTotal, 3 * sizeof(float), &cui.light_ambient_totalIdx);
149 int enablePointLights = pointLight > 0 ? 1 : 0;
150 int enableSpotLights = spotLight > 0 ? 1 : 0;
151 shaders.setUniform(ubufData, "qt_pointLights", &enablePointLights, sizeof(int));
152 shaders.setUniform(ubufData, "qt_spotLights", &enableSpotLights, sizeof(int));
153
154 // Line particle uniform
155 int segmentCount = particleBuffer.segments();
156 if (segmentCount) {
157 shaders.setUniform(ubufData, "qt_lineSegmentCount", &segmentCount, sizeof(int));
158 float alphaFade = renderable.particles.m_alphaFade;
159 float sizeModifier = renderable.particles.m_sizeModifier;
160 float texcoordScale = renderable.particles.m_texcoordScale;
161 auto image = renderable.firstImage;
162 if (image && image->m_texture.m_texture) {
163 const auto size = image->m_texture.m_texture->pixelSize();
164 texcoordScale *= float(size.height()) / float(size.width());
165 }
166 shaders.setUniform(ubufData, "qt_alphaFade", &alphaFade, sizeof(float));
167 shaders.setUniform(ubufData, "qt_sizeModifier", &sizeModifier, sizeof(float));
168 shaders.setUniform(ubufData, "qt_texcoordScale", &texcoordScale, sizeof(float));
169 }
170}
171
172void QSSGParticleRenderer::updateUniformsForParticleModel(QSSGRhiShaderPipeline &shaderPipeline,
173 char *ubufData,
174 const QSSGRenderModel *model,
175 quint32 offset)
176{
177 auto &particleBuffer = *model->particleBuffer;
178 const quint32 particlesPerSlice = particleBuffer.particlesPerSlice();
179 const QVector2D oneOverSize = QVector2D(1.0f / particleBuffer.size().width(), 1.0f / particleBuffer.size().height());
180 shaderPipeline.setUniform(ubufData, "qt_oneOverParticleImageSize", &oneOverSize, 2 * sizeof(float));
181 shaderPipeline.setUniform(ubufData, "qt_countPerSlice", &particlesPerSlice, sizeof(quint32));
182 const QMatrix4x4 &particleMatrix = model->particleMatrix;
183 shaderPipeline.setUniform(ubufData, "qt_particleMatrix", &particleMatrix, 16 * sizeof(float));
184 shaderPipeline.setUniform(ubufData, "qt_particleIndexOffset", &offset, sizeof(quint32));
185}
186
187static void fillTargetBlend(QRhiGraphicsPipeline::TargetBlend &targetBlend, QSSGRenderParticles::BlendMode mode)
188{
189 switch (mode) {
190 case QSSGRenderParticles::BlendMode::Screen:
191 targetBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
192 targetBlend.dstColor = QRhiGraphicsPipeline::One;
193 targetBlend.srcAlpha = QRhiGraphicsPipeline::One;
194 targetBlend.dstAlpha = QRhiGraphicsPipeline::One;
195 break;
196 case QSSGRenderParticles::BlendMode::Multiply:
197 targetBlend.srcColor = QRhiGraphicsPipeline::DstColor;
198 targetBlend.dstColor = QRhiGraphicsPipeline::Zero;
199 targetBlend.srcAlpha = QRhiGraphicsPipeline::One;
200 targetBlend.dstAlpha = QRhiGraphicsPipeline::One;
201 break;
202 default:
203 // Source over as default
204 targetBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
205 targetBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
206 targetBlend.srcAlpha = QRhiGraphicsPipeline::One;
207 targetBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
208 break;
209
210 }
211}
212
213static void sortParticles(const QSSGLayerRenderData &renderData, QByteArray &result, QList<QSSGRhiSortData> &sortData,
214 const QSSGParticleBuffer &buffer, const QSSGRenderParticles &particles,
215 const QVector3D &cameraDirection, bool animatedParticles)
216{
217 const QMatrix4x4 &modelMatrix = renderData.getGlobalTransform(particles);
218 const QMatrix4x4 &invModelMatrix = modelMatrix.inverted();
219 QVector3D dir = invModelMatrix.map(cameraDirection);
220 QVector3D n = dir.normalized();
221 const auto segments = buffer.segments();
222 auto particleCount = buffer.particleCount();
223 const bool lineParticles = segments > 0;
224 if (lineParticles)
225 particleCount /= segments;
226 sortData.resize(particleCount);
227 sortData.fill({});
228
229 const auto srcParticlePointer = [](int line, int segment, int sc, int ss, int pps, const char *source) -> const QSSGLineParticle * {
230 int pi = (line * sc + segment) / pps;
231 int i = (line * sc + segment) % pps;
232 const QSSGLineParticle *sp = reinterpret_cast<const QSSGLineParticle *>(source + pi * ss);
233 return sp + i;
234 };
235
236 // create sort data
237 {
238 const auto slices = buffer.sliceCount();
239 const auto ss = buffer.sliceStride();
240 const auto pps = buffer.particlesPerSlice();
241
242 QSSGRhiSortData *dst = sortData.data();
243 const char *source = buffer.pointer();
244 const char *begin = source;
245 int i = 0;
246 if (lineParticles) {
247 for (i = 0; i < particleCount; i++) {
248 QSSGRhiSortData lineData;
249 const QSSGLineParticle *lineBegin = srcParticlePointer(i, 0, segments, ss, pps, source);
250 lineData.indexOrOffset = i;
251 lineData.d = QVector3D::dotProduct(lineBegin->position, n);
252 for (int j = 1; j < buffer.segments(); j++) {
253 const QSSGLineParticle *p = srcParticlePointer(i, j, segments, ss, pps, source);
254 lineData.d = qMin(lineData.d, QVector3D::dotProduct(p->position, n));
255 }
256 *dst++ = lineData;
257 }
258 } else if (animatedParticles) {
259 for (int s = 0; s < slices; s++) {
260 const QSSGParticleAnimated *sp = reinterpret_cast<const QSSGParticleAnimated *>(source);
261 for (int p = 0; p < pps && i < particleCount; p++) {
262 *dst = { QVector3D::dotProduct(sp->position, n), int(reinterpret_cast<const char *>(sp) - begin)};
263 sp++;
264 dst++;
265 i++;
266 }
267 source += ss;
268 }
269 } else {
270 for (int s = 0; s < slices; s++) {
271 const QSSGParticleSimple *sp = reinterpret_cast<const QSSGParticleSimple *>(source);
272 for (int p = 0; p < pps && i < particleCount; p++) {
273 *dst = { QVector3D::dotProduct(sp->position, n), int(reinterpret_cast<const char *>(sp) - begin)};
274 sp++;
275 dst++;
276 i++;
277 }
278 source += ss;
279 }
280 }
281 }
282
283 // sort
284 result.resize(buffer.bufferSize());
285 std::sort(sortData.begin(), sortData.end(), [](const QSSGRhiSortData &a, const QSSGRhiSortData &b){
286 return a.d > b.d;
287 });
288
289 auto copyParticles = [&](QByteArray &dst, const QList<QSSGRhiSortData> &data, const QSSGParticleBuffer &buffer) {
290 const auto slices = buffer.sliceCount();
291 const auto ss = buffer.sliceStride();
292 const auto pps = buffer.particlesPerSlice();
293 const QSSGRhiSortData *sdata = data.data();
294 char *dest = dst.data();
295 const char *source = buffer.pointer();
296 int i = 0;
297 if (lineParticles) {
298 int seg = 0;
299 for (int s = 0; s < slices; s++) {
300 QSSGLineParticle *dp = reinterpret_cast<QSSGLineParticle *>(dest);
301 for (int p = 0; p < pps && i < particleCount; p++) {
302 *dp = *srcParticlePointer(sdata->indexOrOffset, seg, segments, ss, pps, source);
303 dp++;
304 seg++;
305 if (seg == segments) {
306 sdata++;
307 i++;
308 seg = 0;
309 }
310 }
311 dest += ss;
312 }
313 } else if (animatedParticles) {
314 for (int s = 0; s < slices; s++) {
315 QSSGParticleAnimated *dp = reinterpret_cast<QSSGParticleAnimated *>(dest);
316 for (int p = 0; p < pps && i < particleCount; p++) {
317 *dp = *reinterpret_cast<const QSSGParticleAnimated *>(source + sdata->indexOrOffset);
318 dp++;
319 sdata++;
320 i++;
321 }
322 dest += ss;
323 }
324 } else {
325 for (int s = 0; s < slices; s++) {
326 QSSGParticleSimple *dp = reinterpret_cast<QSSGParticleSimple *>(dest);
327 for (int p = 0; p < pps && i < particleCount; p++) {
328 *dp = *reinterpret_cast<const QSSGParticleSimple *>(source + sdata->indexOrOffset);
329 dp++;
330 sdata++;
331 i++;
332 }
333 dest += ss;
334 }
335 }
336 };
337
338 // write result
339 copyParticles(result, sortData, buffer);
340}
341
342static QByteArray convertParticleData(QByteArray &dest, const QByteArray &data, bool convert)
343{
344 if (!convert)
345 return data;
346 int count = data.size() / 4;
347 if (dest.size() != count * 2)
348 dest.resize(2 * count);
349 qFloatToFloat16(reinterpret_cast<qfloat16 *>(dest.data()), reinterpret_cast<const float *>(data.constData()), count);
350 return dest;
351}
352
353void QSSGParticleRenderer::rhiPrepareRenderable(QSSGRhiShaderPipeline &shaderPipeline,
354 QSSGPassKey passKey,
355 QSSGRhiContext *rhiCtx,
356 QSSGRhiGraphicsPipelineState *ps,
357 QSSGParticlesRenderable &renderable,
358 const QSSGLayerRenderData &inData,
359 QRhiRenderPassDescriptor *renderPassDescriptor,
360 int samples,
361 int viewCount,
362 QSSGRenderCamera *alteredCamera,
363 QSSGRenderTextureCubeFace cubeFace,
364 QSSGReflectionMapEntry *entry)
365{
366 const void *node = &renderable.particles;
367 const bool needsConversion = !rhiCtx->rhi()->isTextureFormatSupported(QRhiTexture::RGBA32F);
368
369 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
370 QSSGRhiDrawCallData &dcd = QSSGRhiContextPrivate::get(rhiCtx)->drawCallData({ passKey, node, entry, cubeFaceIdx });
371 shaderPipeline.ensureUniformBuffer(&dcd.ubuf);
372
373 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
374 if (!alteredCamera) {
375 updateUniformsForParticles(inData, shaderPipeline, rhiCtx, ubufData, renderable, inData.renderedCameras);
376 } else {
377 QSSGRenderCameraList cameras({ alteredCamera });
378 updateUniformsForParticles(inData, shaderPipeline, rhiCtx, ubufData, renderable, cameras);
379 }
380 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
381
382 QSSGRhiParticleData &particleData = QSSGRhiContextPrivate::get(rhiCtx)->particleData(&renderable.particles);
383 const QSSGParticleBuffer &particleBuffer = renderable.particles.m_particleBuffer;
384 int particleCount = particleBuffer.particleCount();
385 if (particleData.texture == nullptr || particleData.particleCount != particleCount) {
386 QSize size(particleBuffer.size());
387 if (!particleData.texture) {
388 particleData.texture = rhiCtx->rhi()->newTexture(needsConversion ? QRhiTexture::RGBA16F : QRhiTexture::RGBA32F, size);
389 particleData.texture->create();
390 } else {
391 particleData.texture->setPixelSize(size);
392 particleData.texture->create();
393 }
394 particleData.particleCount = particleCount;
395 }
396
397 bool sortingChanged = particleData.sorting != renderable.particles.m_depthSorting;
398 if (sortingChanged && !renderable.particles.m_depthSorting) {
399 particleData.sortData.clear();
400 particleData.sortedData.clear();
401 }
402 particleData.sorting = renderable.particles.m_depthSorting;
403
404 QByteArray uploadData;
405
406 if (renderable.particles.m_depthSorting) {
407 bool animatedParticles = renderable.particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::Animated;
408 if (!alteredCamera) {
409 sortParticles(inData, particleData.sortedData, particleData.sortData, particleBuffer, renderable.particles, inData.renderedCameraData.value()[0].direction, animatedParticles);
410 } else {
411 const QMatrix4x4 globalTransform = inData.getGlobalTransform(*alteredCamera);
412 sortParticles(inData, particleData.sortedData, particleData.sortData, particleBuffer, renderable.particles, QSSGRenderNode::getScalingCorrectDirection(globalTransform), animatedParticles);
413 }
414 uploadData = convertParticleData(particleData.convertData, particleData.sortedData, needsConversion);
415 } else {
416 uploadData = convertParticleData(particleData.convertData, particleBuffer.data(), needsConversion);
417 }
418
419 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
420 QRhiTextureSubresourceUploadDescription upload;
421 upload.setData(uploadData);
422 QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, upload));
423 rub->uploadTexture(particleData.texture, uploadDesc);
424 rhiCtx->commandBuffer()->resourceUpdate(rub);
425
426 auto &ia = QSSGRhiInputAssemblerStatePrivate::get(*ps);
427 ia.topology = QRhiGraphicsPipeline::TriangleStrip;
428 ia.inputLayout = QRhiVertexInputLayout();
429 ia.inputs.clear();
430
431 ps->samples = samples;
432 ps->viewCount = viewCount;
433 ps->cullMode = QRhiGraphicsPipeline::None;
434
435 if (inData.orderIndependentTransparencyEnabled == false) {
436 if (renderable.renderableFlags.hasTransparency())
437 fillTargetBlend(ps->targetBlend[0], renderable.particles.m_blendMode);
438 else
439 ps->targetBlend[0] = QRhiGraphicsPipeline::TargetBlend();
440 }
441
442 QSSGRhiShaderResourceBindingList bindings;
443 bindings.addUniformBuffer(0, VISIBILITY_ALL, dcd.ubuf, 0, shaderPipeline.ub0Size());
444
445 // Texture maps
446 // we only have one image
447 QSSGRenderableImage *renderableImage = renderable.firstImage;
448
449 int samplerBinding = shaderPipeline.bindingForTexture("qt_sprite");
450 if (samplerBinding >= 0) {
451 QRhiTexture *texture = renderableImage ? renderableImage->m_texture.m_texture : nullptr;
452 if (samplerBinding >= 0 && texture) {
453 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
454 QRhiSampler *sampler = rhiCtx->sampler({ QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
455 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
456 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
457 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
458 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
459 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
460 });
461 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
462 } else {
463 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
464 QRhiTexture *texture = rhiCtx->dummyTexture({}, rub, QSize(4, 4), Qt::white);
465 rhiCtx->commandBuffer()->resourceUpdate(rub);
466 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
467 QRhiSampler::Nearest,
468 QRhiSampler::None,
469 QRhiSampler::ClampToEdge,
470 QRhiSampler::ClampToEdge,
471 QRhiSampler::Repeat
472 });
473 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
474 }
475 }
476
477 samplerBinding = shaderPipeline.bindingForTexture("qt_particleTexture");
478 if (samplerBinding >= 0) {
479 QRhiTexture *texture = particleData.texture;
480 if (samplerBinding >= 0 && texture) {
481 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
482 QRhiSampler::Nearest,
483 QRhiSampler::None,
484 QRhiSampler::ClampToEdge,
485 QRhiSampler::ClampToEdge,
486 QRhiSampler::Repeat
487 });
488 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::VertexStage, texture, sampler);
489 }
490 }
491
492 samplerBinding = shaderPipeline.bindingForTexture("qt_colorTable");
493 if (samplerBinding >= 0) {
494 bool hasTexture = false;
495 if (renderable.colorTable) {
496 QRhiTexture *texture = renderable.colorTable->m_texture.m_texture;
497 if (texture) {
498 hasTexture = true;
499 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
500 QRhiSampler::Nearest,
501 QRhiSampler::None,
502 QRhiSampler::ClampToEdge,
503 QRhiSampler::ClampToEdge,
504 QRhiSampler::Repeat
505 });
506 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
507 }
508 }
509
510 if (!hasTexture) {
511 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
512 QRhiTexture *texture = rhiCtx->dummyTexture({}, rub, QSize(4, 4), Qt::white);
513 rhiCtx->commandBuffer()->resourceUpdate(rub);
514 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
515 QRhiSampler::Nearest,
516 QRhiSampler::None,
517 QRhiSampler::ClampToEdge,
518 QRhiSampler::ClampToEdge,
519 QRhiSampler::Repeat
520 });
521 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
522 }
523 }
524
525 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
526
527 // TODO: This is identical to other renderables. Make into a function?
528 QRhiShaderResourceBindings *&srb = dcd.srb;
529 bool srbChanged = false;
530 if (!srb || bindings != dcd.bindings) {
531 srb = rhiCtxD->srb(bindings);
532 rhiCtxD->releaseCachedSrb(dcd.bindings);
533 dcd.bindings = bindings;
534 srbChanged = true;
535 }
536
537 if (cubeFace == QSSGRenderTextureCubeFaceNone)
538 renderable.rhiRenderData.mainPass.srb = srb;
539 else
540 renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
541
542 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
543 if (dcd.pipeline
544 && !srbChanged
545 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash
546 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
547 && dcd.ps == *ps)
548 {
549 if (cubeFace == QSSGRenderTextureCubeFaceNone)
550 renderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
551 else
552 renderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
553 } else {
554 if (cubeFace == QSSGRenderTextureCubeFaceNone) {
555 renderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
556 renderPassDescriptor,
557 srb);
558 dcd.pipeline = renderable.rhiRenderData.mainPass.pipeline;
559 } else {
560 renderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
561 renderPassDescriptor,
562 srb);
563 dcd.pipeline = renderable.rhiRenderData.reflectionPass.pipeline;
564 }
565 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
566 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;
567 dcd.ps = *ps;
568 }
569}
570
571void QSSGParticleRenderer::prepareParticlesForModel(QSSGRhiShaderPipeline &shaderPipeline,
572 QSSGRhiContext *rhiCtx,
573 QSSGRhiShaderResourceBindingList &bindings,
574 const QSSGRenderModel *model)
575{
576 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
577 QSSGRhiParticleData &particleData = rhiCtxD->particleData(model);
578 const QSSGParticleBuffer &particleBuffer = *model->particleBuffer;
579 int particleCount = particleBuffer.particleCount();
580 bool update = particleBuffer.serial() != particleData.serial;
581 const bool needsConversion = !rhiCtx->rhi()->isTextureFormatSupported(QRhiTexture::RGBA32F);
582 if (particleData.texture == nullptr || particleData.particleCount != particleCount) {
583 QSize size(particleBuffer.size());
584 if (!particleData.texture) {
585 particleData.texture = rhiCtx->rhi()->newTexture(needsConversion ? QRhiTexture::RGBA16F : QRhiTexture::RGBA32F, size);
586 particleData.texture->create();
587 } else {
588 particleData.texture->setPixelSize(size);
589 particleData.texture->create();
590 }
591 particleData.particleCount = particleCount;
592 update = true;
593 }
594
595 if (update) {
596 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
597 QRhiTextureSubresourceUploadDescription upload;
598 upload.setData(convertParticleData(particleData.convertData, particleBuffer.data(), needsConversion));
599 QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, upload));
600 rub->uploadTexture(particleData.texture, uploadDesc);
601 rhiCtx->commandBuffer()->resourceUpdate(rub);
602 }
603 particleData.serial = particleBuffer.serial();
604 int samplerBinding = shaderPipeline.bindingForTexture("qt_particleTexture");
605 if (samplerBinding >= 0) {
606 QRhiTexture *texture = particleData.texture;
607 if (samplerBinding >= 0 && texture) {
608 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
609 QRhiSampler::Nearest,
610 QRhiSampler::None,
611 QRhiSampler::ClampToEdge,
612 QRhiSampler::ClampToEdge,
613 QRhiSampler::Repeat
614 });
615 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::VertexStage, texture, sampler);
616 }
617 }
618}
619
620void QSSGParticleRenderer::rhiRenderRenderable(QSSGRhiContext *rhiCtx,
621 QSSGParticlesRenderable &renderable,
622 bool *needsSetViewport,
623 QSSGRenderTextureCubeFace cubeFace,
624 const QSSGRhiGraphicsPipelineState &state)
625{
626 QRhiGraphicsPipeline *ps = renderable.rhiRenderData.mainPass.pipeline;
627 QRhiShaderResourceBindings *srb = renderable.rhiRenderData.mainPass.srb;
628
629 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
630 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
631 ps = renderable.rhiRenderData.reflectionPass.pipeline;
632 srb = renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx];
633 }
634
635 if (!ps || !srb)
636 return;
637
638 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
639
640 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
641 // QRhi optimizes out unnecessary binding of the same pipline
642 cb->setGraphicsPipeline(ps);
643 cb->setVertexInput(0, 0, nullptr);
644 cb->setShaderResources(srb);
645
646 if (needsSetViewport && *needsSetViewport) {
647 cb->setViewport(state.viewport);
648 *needsSetViewport = false;
649 }
650 if (renderable.particles.m_featureLevel >= QSSGRenderParticles::FeatureLevel::Line) {
651 // draw triangle strip with 2 * segmentCount vertices N times
652 int S = renderable.particles.m_particleBuffer.segments();
653 int N = renderable.particles.m_particleBuffer.particleCount() / S;
654 cb->draw(2 * S, N);
655 QSSGRHICTX_STAT(rhiCtx, draw(2 * S, N));
656 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DRenderCall, (2 * S | quint64(N) << 32), renderable.particles.profilingId);
657 } else {
658 // draw triangle strip with 2 triangles N times
659 cb->draw(4, renderable.particles.m_particleBuffer.particleCount());
660 QSSGRHICTX_STAT(rhiCtx, draw(4, renderable.particles.m_particleBuffer.particleCount()));
661 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DRenderCall, (4 | quint64(renderable.particles.m_particleBuffer.particleCount()) << 32), renderable.particles.profilingId);
662 }
663}
664
665QT_END_NAMESPACE
\keyword 16-bit Floating Point Support\inmodule QtCore \inheaderfile QFloat16
Definition qfloat16.h:48
Q_CORE_EXPORT void qFloatToFloat16(qfloat16 *, const float *, qsizetype length) noexcept
#define QSSGRHICTX_STAT(ctx, f)
#define QSSG_MAX_NUM_LIGHTS
static QByteArray convertParticleData(QByteArray &dest, const QByteArray &data, bool convert)
static void fillTargetBlend(QRhiGraphicsPipeline::TargetBlend &targetBlend, QSSGRenderParticles::BlendMode mode)
static void sortParticles(const QSSGLayerRenderData &renderData, QByteArray &result, QList< QSSGRhiSortData > &sortData, const QSSGParticleBuffer &buffer, const QSSGRenderParticles &particles, const QVector3D &cameraDirection, bool animatedParticles)
QVector4D spotLightPos[4]
QVector4D spotLightColor[4]
QVector4D pointLightColor[4]
QVector4D pointLightPos[4]
QVector4D spotLightDir[4]