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// Qt-Security score:significant reason:default
4
5
8
9#include <qfloat16.h>
10
11#include <QtQuick3DUtils/private/qssgutils_p.h>
12
13#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
15#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrenderhelpers_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgshaderresourcemergecontext_p.h>
18
19#include <ssg/qssgrendercontextcore.h>
21
22#include "qtquick3d_tracepoints_p.h"
23
25
27 QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
28
30{
32 float pointLightConstantAtt[4] = {1.0f, 1.0f, 1.0f, 1.0f};
33 float pointLightLinearAtt[4] = {0.0f};
34 float pointLightQuadAtt[4] = {0.0f};
37 float spotLightConstantAtt[4] = {1.0f, 1.0f, 1.0f, 1.0f};
38 float spotLightLinearAtt[4] = {0.0f};
39 float spotLightQuadAtt[4] = {0.0f};
42 float spotLightConeAngle[4] = {0.0f};
43 float spotLightInnerConeAngle[4] = {0.0f};
44};
45
46static bool s_shaderCacheEnabled = true;
47void QSSGParticleRenderer::setShaderCacheEnabled(bool enabled)
48{
49 s_shaderCacheEnabled = enabled;
50}
51
52void QSSGParticleRenderer::updateUniformsForParticles(const QSSGLayerRenderData &inData,
53 QSSGRhiShaderPipeline &shaders,
54 QSSGRhiContext *rhiCtx,
55 char *ubufData,
56 QSSGParticlesRenderable &renderable,
57 const QSSGRenderCameraList &cameras)
58{
59 const QMatrix4x4 clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
60
61 QSSGRhiShaderPipeline::CommonUniformIndices &cui = shaders.commonUniformIndices;
62
63 const int viewCount = cameras.count();
64
65 // Pull the camera transforms from the render data once.
66 QMatrix4x4 camGlobalTransforms[2] { QMatrix4x4{Qt::Uninitialized}, QMatrix4x4{Qt::Uninitialized} };
67 if (viewCount < 2) {
68 camGlobalTransforms[0] = inData.getGlobalTransform(*cameras[0]);
69 } else {
70 for (size_t viewIndex = 0; viewIndex != std::size(camGlobalTransforms); ++viewIndex)
71 camGlobalTransforms[viewIndex] = inData.getGlobalTransform(*cameras[viewIndex]);
72 }
73
74 if (viewCount < 2) {
75 const QMatrix4x4 projection = clipSpaceCorrMatrix * cameras[0]->projection;
76 shaders.setUniform(ubufData, "qt_projectionMatrix", projection.constData(), 16 * sizeof(float), &cui.projectionMatrixIdx);
77 const QMatrix4x4 viewMatrix = camGlobalTransforms[0].inverted();
78 shaders.setUniform(ubufData, "qt_viewMatrix", viewMatrix.constData(), 16 * sizeof(float), &cui.viewMatrixIdx);
79 } else {
80 QVarLengthArray<QMatrix4x4, 2> projectionMatrices(viewCount);
81 QVarLengthArray<QMatrix4x4, 2> viewMatrices(viewCount);
82 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
83 projectionMatrices[viewIndex] = clipSpaceCorrMatrix * cameras[viewIndex]->projection;
84 viewMatrices[viewIndex] = camGlobalTransforms[viewIndex].inverted();
85 }
86 shaders.setUniformArray(ubufData, "qt_projectionMatrix", projectionMatrices.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.projectionMatrixIdx);
87 shaders.setUniformArray(ubufData, "qt_viewMatrix", viewMatrices.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.viewMatrixIdx);
88 }
89
90 const QMatrix4x4 &modelMatrix = renderable.globalTransform;
91 shaders.setUniform(ubufData, "qt_modelMatrix", modelMatrix.constData(), 16 * sizeof(float), &cui.modelMatrixIdx);
92
93 const QVector2D camProperties(cameras[0]->clipPlanes);
94 shaders.setUniform(ubufData, "qt_cameraProperties", &camProperties, 2 * sizeof(float), &cui.cameraPropertiesIdx);
95
96 QVector2D oneOverSize = QVector2D(1.0f, 1.0f);
97 auto &particleBuffer = renderable.particles.m_particleBuffer;
98 const quint32 particlesPerSlice = particleBuffer.particlesPerSlice();
99 oneOverSize = QVector2D(1.0f / particleBuffer.size().width(), 1.0f / particleBuffer.size().height());
100 shaders.setUniform(ubufData, "qt_oneOverParticleImageSize", &oneOverSize, 2 * sizeof(float));
101 shaders.setUniform(ubufData, "qt_countPerSlice", &particlesPerSlice, 1 * sizeof(quint32));
102
103 // Global opacity of the particles node
104 shaders.setUniform(ubufData, "qt_opacity", &renderable.opacity, 1 * sizeof(float));
105
106 float blendImages = renderable.particles.m_blendImages ? 1.0f : 0.0f;
107 float imageCount = float(renderable.particles.m_spriteImageCount);
108 float ooImageCount = 1.0f / imageCount;
109
110 QVector4D spriteConfig(imageCount, ooImageCount, 0.0f, blendImages);
111 shaders.setUniform(ubufData, "qt_spriteConfig", &spriteConfig, 4 * sizeof(float));
112
113 const float billboard = renderable.particles.m_billboard ? 1.0f : 0.0f;
114 shaders.setUniform(ubufData, "qt_billboard", &billboard, 1 * sizeof(float));
115
116 // Lights
117 QVector3D theLightAmbientTotal;
118 bool hasLights = !renderable.particles.m_lights.isEmpty();
119 int pointLight = 0;
120 int spotLight = 0;
121 if (hasLights) {
122 ParticleLightData lightData;
123 auto &lights = renderable.lights;
124 for (quint32 lightIdx = 0, lightEnd = lights.size();
125 lightIdx < lightEnd && lightIdx < QSSG_MAX_NUM_LIGHTS; ++lightIdx) {
126 QSSGRenderLight *theLight(lights[lightIdx].light);
127 // Ignore lights which are not specified for the particle
128 if (!renderable.particles.m_lights.contains(theLight))
129 continue;
130
131 const QMatrix4x4 lightGlobalTransform = inData.getGlobalTransform(*theLight);
132
133 if (theLight->type == QSSGRenderLight::Type::DirectionalLight) {
134 theLightAmbientTotal += theLight->m_diffuseColor * theLight->m_brightness;
135 } else if (theLight->type == QSSGRenderLight::Type::PointLight && pointLight < 4) {
136 lightData.pointLightColor[pointLight] = QVector4D(theLight->m_diffuseColor * theLight->m_brightness, 1.0f);
137 lightData.pointLightPos[pointLight] = QVector4D(QSSGRenderNode::getGlobalPos(lightGlobalTransform), 1.0f);
138 lightData.pointLightConstantAtt[pointLight] = QSSGUtils::aux::translateConstantAttenuation(theLight->m_constantFade);
139 lightData.pointLightLinearAtt[pointLight] = QSSGUtils::aux::translateLinearAttenuation(theLight->m_linearFade);
140 lightData.pointLightQuadAtt[pointLight] = QSSGUtils::aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
141 pointLight++;
142 } else if (theLight->type == QSSGRenderLight::Type::SpotLight && spotLight < 4) {
143 lightData.spotLightColor[spotLight] = QVector4D(theLight->m_diffuseColor * theLight->m_brightness, 1.0f);
144 lightData.spotLightPos[spotLight] = QVector4D(QSSGRenderNode::getGlobalPos(lightGlobalTransform), 1.0f);
145 lightData.spotLightDir[spotLight] = QVector4D(lights[lightIdx].direction, 0.0f);
146 lightData.spotLightConstantAtt[spotLight] = QSSGUtils::aux::translateConstantAttenuation(theLight->m_constantFade);
147 lightData.spotLightLinearAtt[spotLight] = QSSGUtils::aux::translateLinearAttenuation(theLight->m_linearFade);
148 lightData.spotLightQuadAtt[spotLight] = QSSGUtils::aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
149 float coneAngle = theLight->m_coneAngle;
150 // Inner cone angle must always be < cone angle, to not have possible undefined behavior for shader smoothstep
151 float innerConeAngle = std::min(theLight->m_innerConeAngle, coneAngle - 0.01f);
152 lightData.spotLightConeAngle[spotLight] = qDegreesToRadians(coneAngle);
153 lightData.spotLightInnerConeAngle[spotLight] = qDegreesToRadians(innerConeAngle);
154 spotLight++;
155 }
156 theLightAmbientTotal += theLight->m_ambientColor;
157 }
158 // Copy light data
159 int lightOffset = shaders.offsetOfUniform("lightData");
160 if (lightOffset >= 0)
161 memcpy(ubufData + lightOffset, &lightData, sizeof(ParticleLightData));
162 }
163 shaders.setUniform(ubufData, "qt_light_ambient_total", &theLightAmbientTotal, 3 * sizeof(float), &cui.light_ambient_totalIdx);
164 int enablePointLights = pointLight > 0 ? 1 : 0;
165 int enableSpotLights = spotLight > 0 ? 1 : 0;
166 shaders.setUniform(ubufData, "qt_pointLights", &enablePointLights, sizeof(int));
167 shaders.setUniform(ubufData, "qt_spotLights", &enableSpotLights, sizeof(int));
168
169 // Line particle uniform
170 int segmentCount = particleBuffer.segments();
171 if (segmentCount) {
172 shaders.setUniform(ubufData, "qt_lineSegmentCount", &segmentCount, sizeof(int));
173 float alphaFade = renderable.particles.m_alphaFade;
174 float sizeModifier = renderable.particles.m_sizeModifier;
175 float texcoordScale = renderable.particles.m_texcoordScale;
176 auto image = renderable.firstImage;
177 if (image && image->m_texture.m_texture) {
178 const auto size = image->m_texture.m_texture->pixelSize();
179 texcoordScale *= float(size.height()) / float(size.width());
180 }
181 shaders.setUniform(ubufData, "qt_alphaFade", &alphaFade, sizeof(float));
182 shaders.setUniform(ubufData, "qt_sizeModifier", &sizeModifier, sizeof(float));
183 shaders.setUniform(ubufData, "qt_texcoordScale", &texcoordScale, sizeof(float));
184 }
185
186#ifdef QSSG_OIT_USE_BUFFERS
187 shaders.setOITImages((QRhiTexture*)inData.oitRenderContext.aBuffer,
188 (QRhiTexture*)inData.oitRenderContext.auxBuffer,
189 (QRhiTexture*)inData.oitRenderContext.counterBuffer);
190 if (inData.oitRenderContext.aBuffer) {
191#else
192 const QSSGRhiRenderableTexture *abuf = inData.getRenderResult(QSSGRenderResult::Key::ABufferImage);
193 const QSSGRhiRenderableTexture *aux = inData.getRenderResult(QSSGRenderResult::Key::AuxiliaryImage);
194 const QSSGRhiRenderableTexture *counter = inData.getRenderResult(QSSGRenderResult::Key::CounterImage);
195 shaders.setOITImages(abuf->texture, aux->texture, counter->texture);
196 if (abuf->texture) {
197#endif
198 int abufWidth = RenderHelpers::rhiCalculateABufferSize(inData.layer.oitNodeCount);
199 int listNodeCount = abufWidth * abufWidth;
200 shaders.setUniform(ubufData, "qt_ABufImageWidth", &abufWidth, sizeof(int), &cui.abufImageWidth);
201 shaders.setUniform(ubufData, "qt_listNodeCount", &listNodeCount, sizeof(int), &cui.listNodeCount);
202 int viewSize[2] = {inData.layerPrepResult.textureDimensions().width(), inData.layerPrepResult.textureDimensions().height()};
203 shaders.setUniform(ubufData, "qt_viewSize", viewSize, sizeof(int) * 2, &cui.viewSize);
204 int samples = rhiCtx->mainPassSampleCount();
205 shaders.setUniform(ubufData, "qt_samples", &samples, sizeof(int), &cui.samples);
206 }
207
208}
209
210void QSSGParticleRenderer::updateUniformsForParticleModel(QSSGRhiShaderPipeline &shaderPipeline,
211 char *ubufData,
212 const QSSGRenderModel *model,
213 quint32 offset)
214{
215 auto &particleBuffer = *model->particleBuffer;
216 const quint32 particlesPerSlice = particleBuffer.particlesPerSlice();
217 const QVector2D oneOverSize = QVector2D(1.0f / particleBuffer.size().width(), 1.0f / particleBuffer.size().height());
218 shaderPipeline.setUniform(ubufData, "qt_oneOverParticleImageSize", &oneOverSize, 2 * sizeof(float));
219 shaderPipeline.setUniform(ubufData, "qt_countPerSlice", &particlesPerSlice, sizeof(quint32));
220 const QMatrix4x4 &particleMatrix = model->particleMatrix;
221 shaderPipeline.setUniform(ubufData, "qt_particleMatrix", &particleMatrix, 16 * sizeof(float));
222 shaderPipeline.setUniform(ubufData, "qt_particleIndexOffset", &offset, sizeof(quint32));
223}
224
225static void fillTargetBlend(QRhiGraphicsPipeline::TargetBlend &targetBlend, QSSGRenderParticles::BlendMode mode)
226{
227 switch (mode) {
228 case QSSGRenderParticles::BlendMode::Screen:
229 targetBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
230 targetBlend.dstColor = QRhiGraphicsPipeline::One;
231 targetBlend.srcAlpha = QRhiGraphicsPipeline::One;
232 targetBlend.dstAlpha = QRhiGraphicsPipeline::One;
233 break;
234 case QSSGRenderParticles::BlendMode::Multiply:
235 targetBlend.srcColor = QRhiGraphicsPipeline::DstColor;
236 targetBlend.dstColor = QRhiGraphicsPipeline::Zero;
237 targetBlend.srcAlpha = QRhiGraphicsPipeline::One;
238 targetBlend.dstAlpha = QRhiGraphicsPipeline::One;
239 break;
240 default:
241 // Source over as default
242 targetBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
243 targetBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
244 targetBlend.srcAlpha = QRhiGraphicsPipeline::One;
245 targetBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
246 break;
247
248 }
249}
250
251static void sortParticles(const QSSGLayerRenderData &renderData, QByteArray &result, QList<QSSGRhiSortData> &sortData,
252 const QSSGParticleBuffer &buffer, const QSSGRenderParticles &particles,
253 const QVector3D &cameraDirection, bool animatedParticles)
254{
255 const QMatrix4x4 &modelMatrix = renderData.getGlobalTransform(particles);
256 const QMatrix4x4 &invModelMatrix = modelMatrix.inverted();
257 QVector3D dir = invModelMatrix.map(cameraDirection);
258 QVector3D n = dir.normalized();
259 const auto segments = buffer.segments();
260 auto particleCount = buffer.particleCount();
261 const bool lineParticles = segments > 0;
262 if (lineParticles)
263 particleCount /= segments;
264 sortData.resize(particleCount);
265 sortData.fill({});
266
267 const auto srcParticlePointer = [](int line, int segment, int sc, int ss, int pps, const char *source) -> const QSSGLineParticle * {
268 int pi = (line * sc + segment) / pps;
269 int i = (line * sc + segment) % pps;
270 const QSSGLineParticle *sp = reinterpret_cast<const QSSGLineParticle *>(source + pi * ss);
271 return sp + i;
272 };
273
274 // create sort data
275 {
276 const auto slices = buffer.sliceCount();
277 const auto ss = buffer.sliceStride();
278 const auto pps = buffer.particlesPerSlice();
279
280 QSSGRhiSortData *dst = sortData.data();
281 const char *source = buffer.pointer();
282 const char *begin = source;
283 int i = 0;
284 if (lineParticles) {
285 for (i = 0; i < particleCount; i++) {
286 QSSGRhiSortData lineData;
287 const QSSGLineParticle *lineBegin = srcParticlePointer(i, 0, segments, ss, pps, source);
288 lineData.indexOrOffset = i;
289 lineData.d = QVector3D::dotProduct(lineBegin->position, n);
290 for (int j = 1; j < buffer.segments(); j++) {
291 const QSSGLineParticle *p = srcParticlePointer(i, j, segments, ss, pps, source);
292 lineData.d = qMin(lineData.d, QVector3D::dotProduct(p->position, n));
293 }
294 *dst++ = lineData;
295 }
296 } else if (animatedParticles) {
297 for (int s = 0; s < slices; s++) {
298 const QSSGParticleAnimated *sp = reinterpret_cast<const QSSGParticleAnimated *>(source);
299 for (int p = 0; p < pps && i < particleCount; p++) {
300 *dst = { QVector3D::dotProduct(sp->position, n), int(reinterpret_cast<const char *>(sp) - begin)};
301 sp++;
302 dst++;
303 i++;
304 }
305 source += ss;
306 }
307 } else {
308 for (int s = 0; s < slices; s++) {
309 const QSSGParticleSimple *sp = reinterpret_cast<const QSSGParticleSimple *>(source);
310 for (int p = 0; p < pps && i < particleCount; p++) {
311 *dst = { QVector3D::dotProduct(sp->position, n), int(reinterpret_cast<const char *>(sp) - begin)};
312 sp++;
313 dst++;
314 i++;
315 }
316 source += ss;
317 }
318 }
319 }
320
321 // sort
322 result.resize(buffer.bufferSize());
323 std::sort(sortData.begin(), sortData.end(), [](const QSSGRhiSortData &a, const QSSGRhiSortData &b){
324 return a.d > b.d;
325 });
326
327 auto copyParticles = [&](QByteArray &dst, const QList<QSSGRhiSortData> &data, const QSSGParticleBuffer &buffer) {
328 const auto slices = buffer.sliceCount();
329 const auto ss = buffer.sliceStride();
330 const auto pps = buffer.particlesPerSlice();
331 const QSSGRhiSortData *sdata = data.data();
332 char *dest = dst.data();
333 const char *source = buffer.pointer();
334 int i = 0;
335 if (lineParticles) {
336 int seg = 0;
337 for (int s = 0; s < slices; s++) {
338 QSSGLineParticle *dp = reinterpret_cast<QSSGLineParticle *>(dest);
339 for (int p = 0; p < pps && i < particleCount; p++) {
340 *dp = *srcParticlePointer(sdata->indexOrOffset, seg, segments, ss, pps, source);
341 dp++;
342 seg++;
343 if (seg == segments) {
344 sdata++;
345 i++;
346 seg = 0;
347 }
348 }
349 dest += ss;
350 }
351 } else if (animatedParticles) {
352 for (int s = 0; s < slices; s++) {
353 QSSGParticleAnimated *dp = reinterpret_cast<QSSGParticleAnimated *>(dest);
354 for (int p = 0; p < pps && i < particleCount; p++) {
355 *dp = *reinterpret_cast<const QSSGParticleAnimated *>(source + sdata->indexOrOffset);
356 dp++;
357 sdata++;
358 i++;
359 }
360 dest += ss;
361 }
362 } else {
363 for (int s = 0; s < slices; s++) {
364 QSSGParticleSimple *dp = reinterpret_cast<QSSGParticleSimple *>(dest);
365 for (int p = 0; p < pps && i < particleCount; p++) {
366 *dp = *reinterpret_cast<const QSSGParticleSimple *>(source + sdata->indexOrOffset);
367 dp++;
368 sdata++;
369 i++;
370 }
371 dest += ss;
372 }
373 }
374 };
375
376 // write result
377 copyParticles(result, sortData, buffer);
378}
379
380static QByteArray convertParticleData(QByteArray &dest, const QByteArray &data, bool convert)
381{
382 if (!convert)
383 return data;
384 int count = data.size() / 4;
385 if (dest.size() != count * 2)
386 dest.resize(2 * count);
387 qFloatToFloat16(reinterpret_cast<qfloat16 *>(dest.data()), reinterpret_cast<const float *>(data.constData()), count);
388 return dest;
389}
390
391void QSSGParticleRenderer::rhiPrepareRenderable(QSSGRhiShaderPipeline &shaderPipeline,
392 QSSGPassKey passKey,
393 QSSGRhiContext *rhiCtx,
394 QSSGRhiGraphicsPipelineState *ps,
395 QSSGParticlesRenderable &renderable,
396 const QSSGLayerRenderData &inData,
397 QRhiRenderPassDescriptor *renderPassDescriptor,
398 int samples,
399 int viewCount,
400 QSSGRenderCamera *alteredCamera,
401 QSSGRenderTextureCubeFace cubeFace,
402 QSSGReflectionMapEntry *entry,
403 bool oit)
404{
405 const void *node = &renderable.particles;
406 const bool needsConversion = !rhiCtx->rhi()->isTextureFormatSupported(QRhiTexture::RGBA32F);
407
408 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
409 QSSGRhiDrawCallData &dcd = QSSGRhiContextPrivate::get(rhiCtx)->drawCallData({ passKey, node, entry, cubeFaceIdx });
410 shaderPipeline.ensureUniformBuffer(&dcd.ubuf);
411
412 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
413 if (!alteredCamera) {
414 updateUniformsForParticles(inData, shaderPipeline, rhiCtx, ubufData, renderable, inData.renderedCameras);
415 } else {
416 QSSGRenderCameraList cameras({ alteredCamera });
417 updateUniformsForParticles(inData, shaderPipeline, rhiCtx, ubufData, renderable, cameras);
418 }
419 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
420
421 QSSGRhiParticleData &particleData = QSSGRhiContextPrivate::get(rhiCtx)->particleData(&renderable.particles);
422 const QSSGParticleBuffer &particleBuffer = renderable.particles.m_particleBuffer;
423 int particleCount = particleBuffer.particleCount();
424 if (particleData.texture == nullptr || particleData.particleCount != particleCount) {
425 QSize size(particleBuffer.size());
426 if (!particleData.texture) {
427 particleData.texture = rhiCtx->rhi()->newTexture(needsConversion ? QRhiTexture::RGBA16F : QRhiTexture::RGBA32F, size);
428 particleData.texture->create();
429 } else {
430 particleData.texture->setPixelSize(size);
431 particleData.texture->create();
432 }
433 particleData.particleCount = particleCount;
434 }
435
436 bool sortingChanged = particleData.sorting != renderable.particles.m_depthSorting;
437 if (sortingChanged && !renderable.particles.m_depthSorting) {
438 particleData.sortData.clear();
439 particleData.sortedData.clear();
440 }
441 particleData.sorting = renderable.particles.m_depthSorting;
442
443 QByteArray uploadData;
444
445 if (renderable.particles.m_depthSorting) {
446 bool animatedParticles = renderable.particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::Animated;
447 if (!alteredCamera) {
448 sortParticles(inData, particleData.sortedData, particleData.sortData, particleBuffer, renderable.particles, inData.renderedCameraData.value()[0].direction, animatedParticles);
449 } else {
450 const QMatrix4x4 globalTransform = inData.getGlobalTransform(*alteredCamera);
451 sortParticles(inData, particleData.sortedData, particleData.sortData, particleBuffer, renderable.particles, QSSGRenderNode::getScalingCorrectDirection(globalTransform), animatedParticles);
452 }
453 uploadData = convertParticleData(particleData.convertData, particleData.sortedData, needsConversion);
454 } else {
455 uploadData = convertParticleData(particleData.convertData, particleBuffer.data(), needsConversion);
456 }
457
458 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
459 QRhiTextureSubresourceUploadDescription upload;
460 upload.setData(uploadData);
461 QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, upload));
462 rub->uploadTexture(particleData.texture, uploadDesc);
463 rhiCtx->commandBuffer()->resourceUpdate(rub);
464
465 auto &ia = QSSGRhiInputAssemblerStatePrivate::get(*ps);
466 ia.topology = QRhiGraphicsPipeline::TriangleStrip;
467 ia.inputLayout = QRhiVertexInputLayout();
468 ia.inputs.clear();
469
470 ps->samples = samples;
471 ps->viewCount = viewCount;
472 ps->cullMode = QRhiGraphicsPipeline::None;
473
474 if (inData.orderIndependentTransparencyEnabled == false) {
475 if (renderable.renderableFlags.hasTransparency())
476 fillTargetBlend(ps->targetBlend[0], renderable.particles.m_blendMode);
477 else
478 ps->targetBlend[0] = QRhiGraphicsPipeline::TargetBlend();
479 }
480
481 QSSGRhiShaderResourceBindingList bindings;
482 bindings.addUniformBuffer(0, VISIBILITY_ALL, dcd.ubuf, 0, shaderPipeline.ub0Size());
483
484 // Texture maps
485 // we only have one image
486 QSSGRenderableImage *renderableImage = renderable.firstImage;
487
488 int samplerBinding = shaderPipeline.bindingForTexture("qt_sprite");
489 if (samplerBinding >= 0) {
490 QRhiTexture *texture = renderableImage ? renderableImage->m_texture.m_texture : nullptr;
491 if (samplerBinding >= 0 && texture) {
492 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
493 QRhiSampler *sampler = rhiCtx->sampler({ QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
494 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
495 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
496 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
497 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
498 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
499 });
500 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
501 } else {
502 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
503 QRhiTexture *texture = rhiCtx->dummyTexture({}, rub, QSize(4, 4), Qt::white);
504 rhiCtx->commandBuffer()->resourceUpdate(rub);
505 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
506 QRhiSampler::Nearest,
507 QRhiSampler::None,
508 QRhiSampler::ClampToEdge,
509 QRhiSampler::ClampToEdge,
510 QRhiSampler::Repeat
511 });
512 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
513 }
514 }
515
516 samplerBinding = shaderPipeline.bindingForTexture("qt_particleTexture");
517 if (samplerBinding >= 0) {
518 QRhiTexture *texture = particleData.texture;
519 if (samplerBinding >= 0 && texture) {
520 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
521 QRhiSampler::Nearest,
522 QRhiSampler::None,
523 QRhiSampler::ClampToEdge,
524 QRhiSampler::ClampToEdge,
525 QRhiSampler::Repeat
526 });
527 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::VertexStage, texture, sampler);
528 }
529 }
530
531 samplerBinding = shaderPipeline.bindingForTexture("qt_colorTable");
532 if (samplerBinding >= 0) {
533 bool hasTexture = false;
534 if (renderable.colorTable) {
535 QRhiTexture *texture = renderable.colorTable->m_texture.m_texture;
536 if (texture) {
537 hasTexture = true;
538 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
539 QRhiSampler::Nearest,
540 QRhiSampler::None,
541 QRhiSampler::ClampToEdge,
542 QRhiSampler::ClampToEdge,
543 QRhiSampler::Repeat
544 });
545 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
546 }
547 }
548
549 if (!hasTexture) {
550 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
551 QRhiTexture *texture = rhiCtx->dummyTexture({}, rub, QSize(4, 4), Qt::white);
552 rhiCtx->commandBuffer()->resourceUpdate(rub);
553 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
554 QRhiSampler::Nearest,
555 QRhiSampler::None,
556 QRhiSampler::ClampToEdge,
557 QRhiSampler::ClampToEdge,
558 QRhiSampler::Repeat
559 });
560 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
561 }
562 }
563
564 if (oit && inData.layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList)
565 RenderHelpers::addAccumulatorImageBindings(&shaderPipeline, bindings);
566
567 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
568
569 // TODO: This is identical to other renderables. Make into a function?
570 QRhiShaderResourceBindings *&srb = dcd.srb;
571 bool srbChanged = false;
572 if (!srb || bindings != dcd.bindings) {
573 srb = rhiCtxD->srb(bindings);
574 rhiCtxD->releaseCachedSrb(dcd.bindings);
575 dcd.bindings = bindings;
576 srbChanged = true;
577 }
578
579 if (cubeFace == QSSGRenderTextureCubeFaceNone)
580 renderable.rhiRenderData.mainPass.srb = srb;
581 else
582 renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
583
584 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
585 if (dcd.pipeline
586 && !srbChanged
587 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash
588 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
589 && dcd.ps == *ps)
590 {
591 if (cubeFace == QSSGRenderTextureCubeFaceNone)
592 renderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
593 else
594 renderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
595 } else {
596 if (cubeFace == QSSGRenderTextureCubeFaceNone) {
597 renderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
598 renderPassDescriptor,
599 srb);
600 dcd.pipeline = renderable.rhiRenderData.mainPass.pipeline;
601 } else {
602 renderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
603 renderPassDescriptor,
604 srb);
605 dcd.pipeline = renderable.rhiRenderData.reflectionPass.pipeline;
606 }
607 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
608 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;
609 dcd.ps = *ps;
610 }
611}
612
613void QSSGParticleRenderer::prepareParticlesForModel(QSSGRhiShaderPipeline &shaderPipeline,
614 QSSGRhiContext *rhiCtx,
615 QSSGRhiShaderResourceBindingList &bindings,
616 const QSSGRenderModel *model)
617{
618 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
619 QSSGRhiParticleData &particleData = rhiCtxD->particleData(model);
620 const QSSGParticleBuffer &particleBuffer = *model->particleBuffer;
621 int particleCount = particleBuffer.particleCount();
622 bool update = particleBuffer.serial() != particleData.serial;
623 const bool needsConversion = !rhiCtx->rhi()->isTextureFormatSupported(QRhiTexture::RGBA32F);
624 if (particleData.texture == nullptr || particleData.particleCount != particleCount) {
625 QSize size(particleBuffer.size());
626 if (!particleData.texture) {
627 particleData.texture = rhiCtx->rhi()->newTexture(needsConversion ? QRhiTexture::RGBA16F : QRhiTexture::RGBA32F, size);
628 particleData.texture->create();
629 } else {
630 particleData.texture->setPixelSize(size);
631 particleData.texture->create();
632 }
633 particleData.particleCount = particleCount;
634 update = true;
635 }
636
637 if (update) {
638 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
639 QRhiTextureSubresourceUploadDescription upload;
640 upload.setData(convertParticleData(particleData.convertData, particleBuffer.data(), needsConversion));
641 QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, upload));
642 rub->uploadTexture(particleData.texture, uploadDesc);
643 rhiCtx->commandBuffer()->resourceUpdate(rub);
644 }
645 particleData.serial = particleBuffer.serial();
646 int samplerBinding = shaderPipeline.bindingForTexture("qt_particleTexture");
647 if (samplerBinding >= 0) {
648 QRhiTexture *texture = particleData.texture;
649 if (samplerBinding >= 0 && texture) {
650 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
651 QRhiSampler::Nearest,
652 QRhiSampler::None,
653 QRhiSampler::ClampToEdge,
654 QRhiSampler::ClampToEdge,
655 QRhiSampler::Repeat
656 });
657 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::VertexStage, texture, sampler);
658 }
659 }
660}
661
662static QByteArray particlesLogPrefix() { return QByteArrayLiteral("particle material pipeline-- "); }
663
665{
673 void addUniform(const QByteArray &name, const QByteArray &type)
674 {
675 vs->addUniform(name, type);
676 fs->addUniform(name, type);
677 }
678 void addUniformArray(const QByteArray &name, const QByteArray &type, quint32 size)
679 {
680 vs->addUniformArray(name, type, size);
681 fs->addUniformArray(name, type, size);
682 }
683 void addDefinition(const QByteArray &name, const QByteArray &value)
684 {
685 vs->addDefinition(name, value);
686 fs->addDefinition(name, value);
687 }
688 void addInterpolant(const QByteArray &name, const QByteArray &type)
689 {
690 vs->addOutgoing(name, type);
691 fs->addIncoming(name, type);
692 }
693 void addFlatInterpolant(const QByteArray &name, const QByteArray &type)
694 {
695 vs->addFlatOutgoing(name, type);
696 fs->addFlatIncoming(name, type);
697 }
698 void addPrefix(const QByteArray &prefix)
699 {
700 vs->addPrefix(prefix);
701 fs->addPrefix(prefix);
702 }
703};
704
706{
708 int indent = 0;
709 AutoFormatGenerator(QSSGStageGeneratorBase &g)
710 : gen(g)
711 {
712
713 }
714 AutoFormatGenerator &operator<<(const QByteArray &data)
715 {
716 for (int i = 0; i < indent; i++)
717 gen << " ";
718 gen << data << "\n";
719 return *this;
720 }
722 {
723 indent++;
724 }
726 {
727 indent--;
728 Q_ASSERT(indent >= 0);
729 }
730};
731
732static const char *s_lightPrefix = {
733 "struct ParticleLightData\n"
734 "{\n"
735 " vec4 qt_pointLightPosition[4];\n"
736 " vec4 qt_pointLightConstantAtt;\n"
737 " vec4 qt_pointLightLinearAtt;\n"
738 " vec4 qt_pointLightQuadAtt;\n"
739 " vec4 qt_pointLightColor[4];\n"
740 " vec4 qt_spotLightPosition[4];\n"
741 " vec4 qt_spotLightConstantAtt;\n"
742 " vec4 qt_spotLightLinearAtt;\n"
743 " vec4 qt_spotLightQuadAtt;\n"
744 " vec4 qt_spotLightColor[4];\n"
745 " vec4 qt_spotLightDirection[4];\n"
746 " vec4 qt_spotLightConeAngle;\n"
747 " vec4 qt_spotLightInnerConeAngle;\n"
748 "};\n"
749};
750
751QSSGShaderFeatures QSSGParticleRenderer::particleShaderFeatures(const QSSGShaderFeatures& features)
752{
753#define COPY_FEATURE(f) ret.set(f, features.isSet(f));
754 QSSGShaderFeatures ret;
755 COPY_FEATURE(QSSGShaderFeatures::Feature::LinearTonemapping);
756 COPY_FEATURE(QSSGShaderFeatures::Feature::AcesTonemapping);
757 COPY_FEATURE(QSSGShaderFeatures::Feature::HejlDawsonTonemapping);
758 COPY_FEATURE(QSSGShaderFeatures::Feature::FilmicTonemapping);
759 COPY_FEATURE(QSSGShaderFeatures::Feature::DisableMultiView);
760#undef COPY_FEATURE
761 return ret;
762}
763
764QSSGRhiShaderPipelinePtr QSSGParticleRenderer::generateRhiShaderPipeline(QSSGRenderer &renderer,
765 QSSGParticlesRenderable &inRenderable,
766 const QSSGShaderFeatures &inFeatureSet,
767 QByteArray &shaderString,
768 const QSSGShaderParticleMaterialKeyProperties &shaderKeyProperties)
769{
770 const auto &m_contextInterface = renderer.m_contextInterface;
771 const auto &shaderCache = m_contextInterface->shaderCache();
772 const auto &shaderProgramGenerator = m_contextInterface->shaderProgramGenerator();
773 const auto &shaderLibraryManager = m_contextInterface->shaderLibraryManager();
774
775 shaderString = particlesLogPrefix();
776 QSSGShaderParticleMaterialKey theKey(inRenderable.shaderDescription);
777
778 theKey.toString(shaderString, shaderKeyProperties);
779
780 if (s_shaderCacheEnabled) {
781 if (const auto &maybePipeline = shaderCache->tryGetRhiShaderPipeline(shaderString, inFeatureSet))
782 return maybePipeline;
783
784 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(inFeatureSet));
785 // Check if there's a pre-built (offline generated) shader for available.
786 if (renderer.m_currentLayer) {
787 QQsbCollection::EntryMap &pregenEntries = renderer.m_currentLayer->m_particleShaderEntries;
788 if (pregenEntries.isEmpty()) {
789 renderer.m_currentLayer->m_particleShaderEntries = shaderLibraryManager->getParticleShaderEntries();
790 pregenEntries = renderer.m_currentLayer->m_particleShaderEntries;
791 }
792 if (!pregenEntries.isEmpty()) {
793 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
794 if (foundIt != pregenEntries.cend())
795 return shaderCache->newPipelineFromPregenerated(shaderString, inFeatureSet, *foundIt, inRenderable.particles);
796 }
797 }
798
799 // Try the persistent (disk-based) cache then.
800 if (const auto &maybePipeline = shaderCache->tryNewPipelineFromPersistentCache(qsbcKey, shaderString, inFeatureSet))
801 return maybePipeline;
802 }
803
804 // generate shader code
805 shaderProgramGenerator->beginProgram();
806 auto &vertex = *shaderProgramGenerator->getStage(QSSGShaderGeneratorStage::Vertex);
807 auto &fragment = *shaderProgramGenerator->getStage(QSSGShaderGeneratorStage::Fragment);
808 ShaderGeneratorCommon common(*shaderProgramGenerator);
809 const bool isSpriteLinear = shaderKeyProperties.m_isSpriteLinear.getValue(theKey);
810 const bool isColorTableLinear = shaderKeyProperties.m_isColorTableLinear.getValue(theKey);
811 const bool lighting = shaderKeyProperties.m_hasLighting.getValue(theKey);
812 const bool isMapped = shaderKeyProperties.m_isMapped.getValue(theKey);
813 const bool isLineParticle = shaderKeyProperties.m_isLineParticle.getValue(theKey);
814 const bool isAnimated = shaderKeyProperties.m_isAnimated.getValue(theKey);
815 const int viewCount = inFeatureSet.isSet(QSSGShaderFeatures::Feature::DisableMultiView)
816 ? 1 : shaderKeyProperties.m_viewCount.getValue(theKey);
817 const int oit = shaderKeyProperties.m_orderIndependentTransparency.getValue(theKey);
818 const bool oitMSAA = shaderKeyProperties.m_oitMSAA.getValue(theKey);
819
820 if (isAnimated)
821 common.addDefinition("QSSG_PARTICLES_ENABLE_ANIMATED", "1");
822 if (isLineParticle)
823 common.addDefinition("QSSG_PARTICLES_ENABLE_LINE_PARTICLE", "1");
824 if (oit)
825 common.addDefinition("QSSG_OIT_METHOD", QStringLiteral("%1").arg(oit).toUtf8());
826 if (oitMSAA)
827 common.addDefinition("QSSG_MULTISAMPLE", "1");
828
829 common.addUniform("qt_modelMatrix", "mat4");
830 if (viewCount >= 2) {
831 common.addUniformArray("qt_viewMatrix", "mat4", viewCount);
832 common.addUniformArray("qt_projectionMatrix", "mat4", viewCount);
833 } else {
834 common.addUniform("qt_viewMatrix", "mat4");
835 common.addUniform("qt_projectionMatrix", "mat4");
836 }
837 if (lighting) {
838 common.addPrefix(s_lightPrefix);
839 common.addUniform("lightData", "ParticleLightData");
840 }
841 common.addUniform("qt_spriteConfig", "vec4");
842 common.addUniform("qt_light_ambient_total", "vec3");
843 common.addUniform("qt_oneOverParticleImageSize", "vec2");
844 common.addUniform("qt_countPerSlice", "uint");
845 if (isLineParticle)
846 common.addUniform("qt_lineSegmentCount", "uint");
847 common.addUniform("qt_billboard", "float");
848 common.addUniform("qt_opacity", "float");
849 if (isLineParticle) {
850 common.addUniform("qt_alphaFade", "float");
851 common.addUniform("qt_sizeModifier", "float");
852 common.addUniform("qt_texcoordScale", "float");
853 }
854 if (lighting) {
855 common.addUniform("qt_pointLights", "bool");
856 common.addUniform("qt_spotLights", "bool");
857 }
858 if (oit == (int)QSSGRenderLayer::OITMethod::WeightedBlended) {
859 common.addUniform("qt_cameraProperties", "vec2");
860 } else if (oit == (int)QSSGRenderLayer::OITMethod::LinkedList) {
861 common.addUniform("qt_viewSize", "ivec2");
862 common.addUniform("qt_ABufImageWidth", "uint");
863 common.addUniform("qt_listNodeCount", "uint");
864#ifdef QSSG_OIT_USE_BUFFERS
865 common.addUniform("qt_samples", "uint");
866#endif
867 }
868
869 if (lighting)
870 vertex.addInclude("particleLighting.glsllib");
871
872 vertex.addUniform("qt_particleTexture", "sampler2D");
873
874 vertex.addInclude("particleLoading.glsllib");
875
876 common.addInterpolant("color", "vec4");
877 common.addInterpolant("spriteData", "vec2");
878 common.addInterpolant("texcoord", "vec2");
879 common.addFlatInterpolant("instanceIndex", "uint");
880 if (oit == (int)QSSGRenderLayer::OITMethod::LinkedList && viewCount >= 2)
881 common.addFlatInterpolant("v_viewIndex", "uint");
882
883 AutoFormatGenerator vg(vertex);
884 if (!isLineParticle)
885 vg << "const vec2 corners[4] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}};";
886
887 // vertex shader main
888 vg << "out gl_PerVertex {"
889 << " vec4 gl_Position;"
890 << "};";
891
892 vg << "void main() {";
893 vg.incIndent();
894 if (isLineParticle) {
895 vg << "uint segmentIndex = gl_VertexIndex / 2;"
896 << "uint particleIndex = segmentIndex + gl_InstanceIndex * qt_lineSegmentCount;";
897 } else {
898 vg << "uint particleIndex = gl_InstanceIndex;"
899 << "uint cornerIndex = gl_VertexIndex;"
900 << "vec2 corner = corners[cornerIndex];";
901 }
902 vg << "instanceIndex = particleIndex;"
903 << "Particle p = qt_loadParticle(particleIndex);";
904
905 if (isLineParticle) {
906 vg << "float side = float(mod(gl_VertexIndex, 2)) - 0.5;"
907 << "float size = p.size * qt_calcSizeFactor(segmentIndex);"
908 << "vec3 o = p.binormal * side * size;"
909 << "vec4 worldPos = qt_modelMatrix * vec4(p.position + (1.0 - qt_billboard) * o, 1.0);";
910 if (viewCount >= 2) {
911 vg << "vec4 viewPos = qt_viewMatrix[gl_ViewIndex] * worldPos;"
912 << "vec3 viewBN = (transpose(inverse(qt_viewMatrix[gl_ViewIndex] * qt_modelMatrix)) * vec4(p.binormal, 0.0)).xyz;";
913 } else {
914 vg << "vec4 viewPos = qt_viewMatrix * worldPos;"
915 << "vec3 viewBN = (transpose(inverse(qt_viewMatrix * qt_modelMatrix)) * vec4(p.binormal, 0.0)).xyz;";
916 }
917 vg << "viewBN.z = 0;"
918 << "viewBN = normalize(viewBN) * side * size;"
919 << "viewPos = viewPos + vec4(viewBN, 0.0) * qt_billboard;"
920 << "texcoord.x = p.segmentLength * qt_texcoordScale;"
921 << "texcoord.y = float(mod(gl_VertexIndex, 2));"
922 << "color = p.color;"
923 << "color.a *= qt_opacity * qt_calcAlphaFade(segmentIndex);";
924 } else {
925 vg << "mat3 rotMat = qt_fromEulerRotation(p.rotation);"
926 << "vec4 worldPos = qt_modelMatrix * vec4(p.position, 1.0);"
927 << "vec4 viewPos = worldPos;"
928 << "vec3 offset = (p.size * vec3(corner - vec2(0.5), 0.0));"
929 << "viewPos.xyz += offset * rotMat * (1.0 - qt_billboard);";
930 if (viewCount >= 2)
931 vg << "viewPos = qt_viewMatrix[gl_ViewIndex] * viewPos;";
932 else
933 vg << "viewPos = qt_viewMatrix * viewPos;";
934 vg << "viewPos.xyz += rotMat * offset * qt_billboard;"
935 << "texcoord = corner;"
936 << "color = p.color;"
937 << "color.a *= qt_opacity;";
938 }
939 if (lighting)
940 vg << "color.rgb *= qt_calcLightColor(worldPos.xyz);";
941 if (viewCount >= 2) {
942 if (oit == int(QSSGRenderLayer::OITMethod::LinkedList))
943 vg << "v_viewIndex = gl_ViewIndex;";
944 vg << "gl_Position = qt_projectionMatrix[gl_ViewIndex] * viewPos;";
945 } else {
946 vg << "gl_Position = qt_projectionMatrix * viewPos;";
947 }
948 if (isMapped)
949 vg << "spriteData.x = qt_ageToSpriteFactor(p.age);";
950 if (isAnimated)
951 vg << "spriteData.y = p.animationFrame;";
952 vg.decIndent();
953 vg << "}";
954
955 // fragment shader main
956 fragment.addInclude("tonemapping.glsllib");
957 fragment.addInclude("particleSprite.glsllib");
958 fragment.addUniform("qt_sprite", "sampler2D");
959
960 AutoFormatGenerator fg(fragment);
961
962 if (oit == int(QSSGRenderLayer::OITMethod::WeightedBlended)) {
963 fragment.addInclude("oitweightedblended.glsllib");
964 fg << "layout(location = 1) out vec4 revealageOutput;";
965 } else if (oit == int(QSSGRenderLayer::OITMethod::LinkedList)) {
966#ifdef QSSG_OIT_USE_BUFFERS
967 fragment.addInclude("oitlinkedlist_buf.glsllib");
968 QSSGShaderResourceMergeContext::setAdditionalBufferAmount(3);
969#else
970 fragment.addInclude("oitlinkedlist.glsllib");
971#endif
972 }
973 if (isSpriteLinear)
974 fg << "#define spriteFunc";
975 else
976 fg << "#define spriteFunc qt_sRGBToLinear";
977 if (isMapped) {
978 fragment.addUniform("qt_colorTable", "sampler2D");
979 if (isColorTableLinear)
980 fg << "#define colorTableFunc";
981 else
982 fg << "#define colorTableFunc qt_sRGBToLinear";
983 }
984
985 fg << "vec4 qt_readSprite() {";
986 fg.incIndent();
987 if (isAnimated) {
988 fg << "vec2 coords[2];"
989 << "float factor = qt_spriteCoords(coords);"
990 << "return mix(spriteFunc(texture(qt_sprite, coords[0])), spriteFunc(texture(qt_sprite, coords[1])), factor);";
991 } else {
992 fg << "return spriteFunc(texture(qt_sprite, texcoord));";
993 }
994 fg.decIndent();
995 fg << "}";
996 fg << "vec4 qt_readColor() {";
997 fg.incIndent();
998 if (isMapped)
999 fg << "return color * colorTableFunc(texture(qt_colorTable, vec2(spriteData.x, qt_rand(vec2(instanceIndex, 0)))));";
1000 else
1001 fg << "return color;";
1002 fg.decIndent();
1003 fg << "}";
1004 fg << "void main() {";
1005 fg.incIndent();
1006 fg << "vec4 ret = qt_readColor() * qt_readSprite();";
1007 if (oit) {
1008 if (oit == int(QSSGRenderLayer::OITMethod::WeightedBlended)) {
1009 fg << "float z = abs(gl_FragCoord.z);"
1010 << "float distWeight = qt_transparencyWeight(z, ret.a, qt_cameraProperties.y);"
1011 << "fragOutput = distWeight * qt_tonemap(ret);"
1012 << "revealageOutput = vec4(ret.a);";
1013 } else if (oit == int(QSSGRenderLayer::OITMethod::LinkedList)) {
1014#ifdef QSSG_OIT_USE_BUFFERS
1015 if (viewCount >= 2)
1016 fg << "fragOutput = qt_oitLinkedList(ret, qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, v_viewIndex, qt_samples);";
1017 else
1018 fg << "fragOutput = qt_oitLinkedList(ret, qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, 0, qt_samples);";
1019#else
1020 if (viewCount >= 2)
1021 fg << "fragOutput = qt_oitLinkedList(ret, qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, v_viewIndex);";
1022 else
1023 fg << "fragOutput = qt_oitLinkedList(ret, qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, 0);";
1024#endif
1025 }
1026 } else {
1027 fg << "fragOutput = qt_tonemap(ret);";
1028 }
1029 fg.decIndent();
1030 fg << "}";
1031
1032
1033 return shaderProgramGenerator->compileGeneratedRhiShader(shaderString,
1034 inFeatureSet,
1035 *shaderLibraryManager,
1036 *shaderCache,
1037 QSSGRhiShaderPipeline::UsedWithoutIa,
1038 {},
1039 shaderKeyProperties.m_viewCount.getValue(inRenderable.shaderDescription),
1040 false);
1041}
1042
1043QSSGRhiShaderPipelinePtr QSSGParticleRenderer::getShaderPipelineParticles(QSSGRenderer &renderer,
1044 QSSGParticlesRenderable &inRenderable,
1045 const QSSGShaderFeatures &inFeatureSet)
1046{
1047 const auto features = particleShaderFeatures(inFeatureSet);
1048 auto *m_currentLayer = renderer.m_currentLayer;
1049 QSSG_ASSERT(m_currentLayer != nullptr, return {});
1050
1051 auto &shaderMap = m_currentLayer->particleShaderMap;
1052
1053 QElapsedTimer timer;
1054 timer.start();
1055
1056 QSSGRhiShaderPipelinePtr shaderPipeline;
1057
1058 // This just references inFeatureSet and inRenderable.shaderDescription -
1059 // cheap to construct and is good enough for the find()
1060 QSSGParticleShaderMapKey skey = QSSGParticleShaderMapKey(QByteArray(),
1061 features,
1062 inRenderable.shaderDescription);
1063 auto it = shaderMap.find(skey);
1064 if (it == shaderMap.end()) {
1065 Q_TRACE_SCOPE(QSSG_generateShader);
1066 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
1067 auto &shaderString = renderer.m_currentLayer->generatedShaderString;
1068 shaderPipeline = QSSGParticleRenderer::generateRhiShaderPipeline(renderer, inRenderable, features, shaderString, m_currentLayer->particleMaterialShaderKeyProperties);
1069 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inRenderable.particles.profilingId);
1070 // make skey useable as a key for the QHash (makes a copy of the materialKey, instead of just referencing)
1071 skey.detach();
1072 // insert it no matter what, no point in trying over and over again
1073 shaderMap.insert(skey, shaderPipeline);
1074 } else {
1075 shaderPipeline = it.value();
1076 }
1077
1078 if (shaderPipeline != nullptr) {
1079 if (m_currentLayer && !m_currentLayer->renderedCameras.isEmpty())
1080 m_currentLayer->ensureCachedCameraDatas();
1081 }
1082
1083 const auto &rhiContext = renderer.m_contextInterface->rhiContext();
1084 QSSGRhiContextStats::get(*rhiContext).registerMaterialShaderGenerationTime(timer.elapsed());
1085
1086 return shaderPipeline;
1087}
1088
1089void QSSGParticleRenderer::rhiRenderRenderable(QSSGRhiContext *rhiCtx,
1090 QSSGParticlesRenderable &renderable,
1091 bool *needsSetViewport,
1092 QSSGRenderTextureCubeFace cubeFace,
1093 const QSSGRhiGraphicsPipelineState &state)
1094{
1095 QRhiGraphicsPipeline *ps = renderable.rhiRenderData.mainPass.pipeline;
1096 QRhiShaderResourceBindings *srb = renderable.rhiRenderData.mainPass.srb;
1097
1098 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
1099 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
1100 ps = renderable.rhiRenderData.reflectionPass.pipeline;
1101 srb = renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx];
1102 }
1103
1104 if (!ps || !srb)
1105 return;
1106
1107 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
1108
1109 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1110 // QRhi optimizes out unnecessary binding of the same pipline
1111 cb->setGraphicsPipeline(ps);
1112 cb->setVertexInput(0, 0, nullptr);
1113 cb->setShaderResources(srb);
1114
1115 if (needsSetViewport && *needsSetViewport) {
1116 cb->setViewport(state.viewport);
1117 *needsSetViewport = false;
1118 }
1119 if (renderable.particles.m_featureLevel >= QSSGRenderParticles::FeatureLevel::Line) {
1120 // draw triangle strip with 2 * segmentCount vertices N times
1121 int S = renderable.particles.m_particleBuffer.segments();
1122 int N = renderable.particles.m_particleBuffer.particleCount() / S;
1123 cb->draw(2 * S, N);
1124 QSSGRHICTX_STAT(rhiCtx, draw(2 * S, N));
1125 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DRenderCall, (2 * S | quint64(N) << 32), renderable.particles.profilingId);
1126 } else {
1127 // draw triangle strip with 2 triangles N times
1128 cb->draw(4, renderable.particles.m_particleBuffer.particleCount());
1129 QSSGRHICTX_STAT(rhiCtx, draw(4, renderable.particles.m_particleBuffer.particleCount()));
1130 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DRenderCall, (4 | quint64(renderable.particles.m_particleBuffer.particleCount()) << 32), renderable.particles.profilingId);
1131 }
1132}
1133
1134QT_END_NAMESPACE
\keyword 16-bit Floating Point Support\inmodule QtCore \inheaderfile QFloat16
Definition qfloat16.h:56
Combined button and popup list for selecting options.
Q_CORE_EXPORT void qFloatToFloat16(qfloat16 *, const float *, qsizetype length) noexcept
#define QSSGRHICTX_STAT(ctx, f)
#define QSSG_MAX_NUM_LIGHTS
static QByteArray particlesLogPrefix()
static const char * s_lightPrefix
static QByteArray convertParticleData(QByteArray &dest, const QByteArray &data, bool convert)
static void fillTargetBlend(QRhiGraphicsPipeline::TargetBlend &targetBlend, QSSGRenderParticles::BlendMode mode)
static bool s_shaderCacheEnabled
static void sortParticles(const QSSGLayerRenderData &renderData, QByteArray &result, QList< QSSGRhiSortData > &sortData, const QSSGParticleBuffer &buffer, const QSSGRenderParticles &particles, const QVector3D &cameraDirection, bool animatedParticles)
static QT_BEGIN_NAMESPACE const QRhiShaderResourceBinding::StageFlags VISIBILITY_ALL
#define COPY_FEATURE(f)
AutoFormatGenerator(QSSGStageGeneratorBase &g)
QSSGStageGeneratorBase & gen
QVector4D spotLightPos[4]
QVector4D spotLightColor[4]
QVector4D pointLightColor[4]
QVector4D pointLightPos[4]
QVector4D spotLightDir[4]
void addUniformArray(const QByteArray &name, const QByteArray &type, quint32 size)
void addInterpolant(const QByteArray &name, const QByteArray &type)
ShaderGeneratorCommon(QSSGProgramGenerator &gen)
void addUniform(const QByteArray &name, const QByteArray &type)
void addFlatInterpolant(const QByteArray &name, const QByteArray &type)
QSSGStageGeneratorBase * vs
QSSGStageGeneratorBase * fs
void addPrefix(const QByteArray &prefix)
void addDefinition(const QByteArray &name, const QByteArray &value)