Revision control

Copy as Markdown

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#ifndef QSSGRHICONTEXT_P_H
#define QSSGRHICONTEXT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtGui/rhi/qrhi.h>
#include <QtQuick3DRuntimeRender/qtquick3druntimerenderexports.h>
#include <QtQuick3DUtils/private/qssgrenderbasetypes_p.h>
#include <QtQuick3DUtils/private/qssgutils_p.h>
#include <ssg/qssgrhicontext.h>
QT_BEGIN_NAMESPACE
struct QSSGRenderLayer;
struct QSSGRenderSkyMaterial;
struct QSSGRenderInstanceTable;
struct QSSGRenderModel;
struct QSSGRenderMesh;
class QSSGRenderGraphObject;
class QSSGBufferManager;
class QSSGUserRenderPassManager;
struct QSSGRhiInputAssemblerStatePrivate
{
using InputAssemblerState = QSSGRhiGraphicsPipelineState::InputAssemblerState;
static const InputAssemblerState &get(const QSSGRhiGraphicsPipelineState &ps) { return ps.ia; }
static InputAssemblerState &get(QSSGRhiGraphicsPipelineState &ps) { return ps.ia; }
};
using QSSGRhiInputAssemblerState = QSSGRhiInputAssemblerStatePrivate::InputAssemblerState;
struct QSSGRhiGraphicsPipelineStatePrivate
{
static void setShaderPipeline(QSSGRhiGraphicsPipelineState &ps, const QSSGRhiShaderPipeline *pipeline)
{
ps.shaderPipeline = pipeline;
}
static constexpr const QSSGRhiShaderPipeline *getShaderPipeline(const QSSGRhiGraphicsPipelineState &ps)
{
return ps.shaderPipeline;
}
};
namespace QSSGRhiHelpers
{
inline QRhiSampler::Filter toRhi(QSSGRenderTextureFilterOp op)
{
switch (op) {
case QSSGRenderTextureFilterOp::Nearest:
return QRhiSampler::Nearest;
case QSSGRenderTextureFilterOp::Linear:
return QRhiSampler::Linear;
case QSSGRenderTextureFilterOp::None:
return QRhiSampler::Linear;
}
Q_UNREACHABLE_RETURN(QRhiSampler::Linear);
}
inline QRhiSampler::AddressMode toRhi(QSSGRenderTextureCoordOp tiling)
{
switch (tiling) {
case QSSGRenderTextureCoordOp::Repeat:
return QRhiSampler::Repeat;
case QSSGRenderTextureCoordOp::MirroredRepeat:
return QRhiSampler::Mirror;
case QSSGRenderTextureCoordOp::ClampToEdge:
return QRhiSampler::ClampToEdge;
case QSSGRenderTextureCoordOp::Unknown:
return QRhiSampler::ClampToEdge;
}
Q_UNREACHABLE_RETURN(QRhiSampler::ClampToEdge);
}
inline QRhiGraphicsPipeline::CullMode toCullMode(QSSGCullFaceMode cullFaceMode)
{
switch (cullFaceMode) {
case QSSGCullFaceMode::Back:
return QRhiGraphicsPipeline::Back;
case QSSGCullFaceMode::Front:
return QRhiGraphicsPipeline::Front;
case QSSGCullFaceMode::Disabled:
return QRhiGraphicsPipeline::None;
case QSSGCullFaceMode::FrontAndBack:
qWarning("FrontAndBack cull mode not supported");
return QRhiGraphicsPipeline::None;
case QSSGCullFaceMode::Unknown:
return QRhiGraphicsPipeline::None;
}
Q_UNREACHABLE_RETURN(QRhiGraphicsPipeline::None);
}
QRhiVertexInputAttribute::Format toVertexInputFormat(QSSGRenderComponentType compType, quint32 numComps);
QRhiGraphicsPipeline::Topology toTopology(QSSGRenderDrawMode drawMode);
// Fills out inputLayout.attributes[].location based on
// inputLayoutInputNames and the provided shader reflection info.
void bakeVertexInputLocations(QSSGRhiInputAssemblerState *ia, const QSSGRhiShaderPipeline &shaders, int instanceBufferBinding = 0);
} // namespace QSSGRhiHelpers
inline bool operator==(const QSSGRhiGraphicsPipelineState &a, const QSSGRhiGraphicsPipelineState &b) Q_DECL_NOTHROW
{
const auto &ia_a = QSSGRhiInputAssemblerStatePrivate::get(a);
const auto &ia_b = QSSGRhiInputAssemblerStatePrivate::get(b);
const auto compareTargetBlend = [](const std::array<QRhiGraphicsPipeline::TargetBlend, 8> &a,
const std::array<QRhiGraphicsPipeline::TargetBlend, 8> &b,
const int count) -> bool {
for (int i = 0; i < count; i++) {
if (a[i].colorWrite != b[i].colorWrite
|| a[i].srcColor != b[i].srcColor
|| a[i].dstColor != b[i].dstColor
|| a[i].opColor != b[i].opColor
|| a[i].srcAlpha != b[i].srcAlpha
|| a[i].dstAlpha != b[i].dstAlpha
|| a[i].opAlpha != b[i].opAlpha) {
return false;
}
}
return true;
};
// The viewport and scissor rects are intentionally not compared: they are
// dynamic state, never baked into a QRhiGraphicsPipeline, and comparing
// them made the pipeline cache key size-dependent, growing the cache with
// a new set of pipelines for every window size!
return QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(a) == QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(b)
&& a.samples == b.samples
&& a.flags == b.flags
&& a.stencilRef == b.stencilRef
&& (std::memcmp(&a.stencilOpFrontState, &b.stencilOpFrontState, sizeof(QRhiGraphicsPipeline::StencilOpState)) == 0)
&& a.stencilWriteMask == b.stencilWriteMask
&& a.depthFunc == b.depthFunc
&& a.cullMode == b.cullMode
&& a.depthBias == b.depthBias
&& a.slopeScaledDepthBias == b.slopeScaledDepthBias
&& ia_a.topology == ia_b.topology
&& ia_a.inputLayout == ia_b.inputLayout
&& a.colorAttachmentCount == b.colorAttachmentCount
&& compareTargetBlend(a.targetBlend, b.targetBlend, a.colorAttachmentCount)
&& a.lineWidth == b.lineWidth
&& a.polygonMode == b.polygonMode
&& a.viewCount == b.viewCount;
}
inline bool operator!=(const QSSGRhiGraphicsPipelineState &a, const QSSGRhiGraphicsPipelineState &b) Q_DECL_NOTHROW
{
return !(a == b);
}
inline size_t qHash(const QSSGRhiGraphicsPipelineState &s, size_t seed) Q_DECL_NOTHROW
{
// do not bother with all fields
return qHash(QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(s), seed)
^ qHash(s.samples)
^ qHash(s.viewCount)
^ qHash(s.targetBlend[0].dstColor)
^ qHash(s.depthFunc)
^ qHash(s.cullMode)
^ qHash(s.colorAttachmentCount)
^ qHash(s.lineWidth)
^ qHash(s.polygonMode)
^ qHashBits(&s.stencilOpFrontState, sizeof(QRhiGraphicsPipeline::StencilOpState))
^ (s.flags)
^ (s.stencilRef << 6)
^ (s.stencilWriteMask << 7);
}
// The lookup keys can be somewhat complicated due to having to handle cases
// like "render a model in a shared scene between multiple View3Ds" (here both
// the View3D ('layer/cid') and the model ('model') act as the lookup key since
// while the model is the same, we still want different uniform buffers per
// View3D). 'resource' is the resource the model is drawn with, typically a
// material; many resources can map to one model. When 'resource' refers to a
// material it must be the material pointer and nothing else, since
// cleanupDrawCallDataForResource() finds the entries to release for a
// to-be-destroyed material by matching on 'resource'. 'mapEntry' is the
// per-light shadow map entry or per-probe reflection map entry being rendered
// into, when the draw is part of a shadow or reflection pass, otherwise null.
// 'resourceIdx' disambiguates multiple entries for the same resource and map
// entry, e.g. the cube map face and the subset offset.
//
struct QSSGRhiDrawCallDataKey
{
const void *cid = nullptr; // Usually the sub-pass (see usage of QSSGPassKey)
const void *model = nullptr;
const void *resource = nullptr;
const void *mapEntry = nullptr;
quintptr resourceIdx = 0;
};
class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiBuffer
{
Q_DISABLE_COPY(QSSGRhiBuffer)
public:
QSSGRhiBuffer(QSSGRhiContext &context,
QRhiBuffer::Type type,
QRhiBuffer::UsageFlags usageMask,
quint32 stride,
qsizetype size,
QRhiCommandBuffer::IndexFormat indexFormat = QRhiCommandBuffer::IndexUInt16);
virtual ~QSSGRhiBuffer();
QRhiBuffer *buffer() const { return m_buffer; }
quint32 stride() const { return m_stride; }
quint32 numVertices() const {
const quint32 sz = quint32(m_buffer->size());
Q_ASSERT((sz % m_stride) == 0);
return sz / m_stride;
}
QRhiCommandBuffer::IndexFormat indexFormat() const { return m_indexFormat; }
private:
QSSGRhiContext &m_context;
QRhiBuffer *m_buffer = nullptr;
quint32 m_stride;
QRhiCommandBuffer::IndexFormat m_indexFormat;
};
struct QSSGRhiShaderUniform
{
char name[64];
size_t size = 0;
private:
size_t offset = SIZE_MAX;
bool maybeExists = true;
friend class QSSGRhiShaderPipeline;
};
struct QSSGRhiShaderUniformArray
{
char name[64];
size_t typeSize = 0;
size_t itemCount = 0;
private:
size_t offset = SIZE_MAX;
size_t size = 0;
bool maybeExists = true;
friend class QSSGRhiShaderPipeline;
};
using QSSGRhiBufferPtr = std::shared_ptr<QSSGRhiBuffer>;
inline bool operator==(const QSSGRhiSamplerDescription &a, const QSSGRhiSamplerDescription &b) Q_DECL_NOTHROW
{
return a.hTiling == b.hTiling && a.vTiling == b.vTiling && a.zTiling == b.zTiling
&& a.minFilter == b.minFilter && a.magFilter == b.magFilter
&& a.mipmap == b.mipmap;
}
inline bool operator!=(const QSSGRhiSamplerDescription &a, const QSSGRhiSamplerDescription &b) Q_DECL_NOTHROW
{
return !(a == b);
}
struct QSSGRhiTexture
{
QByteArray name;
QRhiTexture *texture = nullptr;
QSSGRhiSamplerDescription samplerDesc;
};
enum class QSSGRhiSamplerBindingHints
{
LightProbe = 64, // must be larger than the largest value in SSGRenderableImage::Type
ScreenTexture,
DepthTexture,
AoTexture,
LightmapTexture,
DepthTextureArray,
ScreenTextureArray,
AoTextureArray,
NormalTexture,
MotionVectorTexture,
BindingMapSize
};
// these are our current shader limits
#define QSSG_MAX_NUM_LIGHTS 16
#define QSSG_MAX_NUM_DIRECTIONAL_LIGHTS 4
#define QSSG_REDUCED_MAX_NUM_LIGHTS 4
#define QSSG_REDUCED_MAX_NUM_DIRECTIONAL_LIGHTS 1
struct QSSGShaderLightData {
float position[3];
float coneAngle;
float direction[3];
float innerConeAngle;
float diffuseColor[3];
float constantAttenuation;
float specularColor[3];
float linearAttenuation;
quint32 lightmapState;
quint32 shadowPcfSamples;
float quadraticAttenuation;
float shadowTextureSize;
float shadowAtlasUV0[2];
float shadowAtlasUV1[2];
float shadowAtlasLayer0;
float shadowAtlasLayer1;
float shadowPcfFactor;
float enableShadows;
float shadowBias;
float shadowFactor;
float shadowClipNear;
float shadowMapFar;
float shadowMatrix[16];
};
struct QSSGShaderDirectionalLightData {
float direction[3];
float shadowBias;
float diffuseColor[3];
float shadowFactor;
float specularColor[3];
float enableShadows;
float matrices[4][16];
float dimensionsInverted[4][4];
float csmSplits[4];
float csmActive[4];
float atlasLocations[4][4]; // (u, v, size, layer)
quint32 lightmapState;
quint32 shadowPcfSamples;
quint32 csmNumSplits;
float shadowPcfFactor;
float shadowMapFar;
float csmBlendRatio;
float padding[2];
};
struct QSSGShaderLightsUniformData {
QSSGShaderLightData lightData[QSSG_MAX_NUM_LIGHTS];
};
struct QSSGShaderDirectionalLightsUniformData {
QSSGShaderDirectionalLightData directionalLightData[QSSG_MAX_NUM_DIRECTIONAL_LIGHTS];
};
class QSSGRhiShaderResourceBindingList;
class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiShaderPipeline
{
Q_DISABLE_COPY(QSSGRhiShaderPipeline)
public:
explicit QSSGRhiShaderPipeline(QSSGRhiContext &context) : m_context(context) { }
QSSGRhiContext &context() const { return m_context; }
bool isNull() const { return m_stages.isEmpty(); }
enum StageFlag {
// Indicates that this shaderpipeline object is not going to be used with
// a QSSGRhiInputAssemblerState, i.e. bakeVertexInputLocations() will
// not be called.
UsedWithoutIa = 0x01
};
Q_DECLARE_FLAGS(StageFlags, StageFlag)
void addStage(const QRhiShaderStage &stage, StageFlags flags = {});
const QRhiShaderStage *cbeginStages() const { return m_stages.cbegin(); }
const QRhiShaderStage *cendStages() const { return m_stages.cend(); }
const QRhiShaderStage *vertexStage() const {
for (const QRhiShaderStage &s : m_stages) {
if (s.type() == QRhiShaderStage::Vertex)
return &s;
}
return nullptr;
}
const QRhiShaderStage *fragmentStage() const {
for (const QRhiShaderStage &s : m_stages) {
if (s.type() == QRhiShaderStage::Fragment)
return &s;
}
return nullptr;
}
int ub0Size() const { return m_ub0Size; }
int ub0LightDataOffset() const { return m_ub0NextUBufOffset; }
int ub0DirectionalLightDataOffset() const;
const QHash<QSSGRhiInputAssemblerState::InputSemantic, QShaderDescription::InOutVariable> &vertexInputs() const { return m_vertexInputs; }
// This struct is used purely for performance. It is used to quickly store
// and index common uniform names using the storeIndex argument in the
// setUniform method.
struct CommonUniformIndices
{
int cameraPositionIdx = -1;
int cameraDirectionIdx = -1;
int viewProjectionMatrixIdx = -1;
int projectionMatrixIdx = -1;
int inverseProjectionMatrixIdx = -1;
int viewMatrixIdx = -1;
int modelViewProjectionIdx = -1;
int normalMatrixIdx = -1;
int modelMatrixIdx = -1;
int lightProbeOrientationIdx = -1;
int lightProbePropertiesIdx = -1;
int material_emissiveColorIdx = -1;
int material_baseColorIdx = -1;
int material_specularIdx = -1;
int cameraPropertiesIdx = -1;
int light_ambient_totalIdx = -1;
int material_propertiesIdx = -1;
int material_properties2Idx = -1;
int material_properties3Idx = -1;
int material_properties4Idx = -1;
int material_properties5Idx = -1;
int material_attenuationIdx = -1;
int thicknessFactorIdx = -1;
int clearcoatNormalStrengthIdx = -1;
int sheenColorIdx = -1;
int sheenRoughnessIdx = -1;
int anisotropyIdx = -1;
int iridescenceIdx = -1;
int dispersionIdx = -1;
int clearcoatFresnelPowerIdx = -1;
int rhiPropertiesIdx = -1;
int displaceAmountIdx = -1;
int boneTransformsIdx = -1;
int boneNormalTransformsIdx = -1;
int shadowDepthAdjustIdx = -1;
int pointSizeIdx = -1;
int morphWeightsIdx = -1;
int reflectionProbeCubeMapCenter = -1;
int reflectionProbeBoxMax = -1;
int reflectionProbeBoxMin = -1;
int reflectionProbeCorrection = -1;
int specularAAIdx = -1;
int fogColorIdx = -1;
int fogSunColorIdx = -1;
int fogDepthPropertiesIdx = -1;
int fogHeightPropertiesIdx = -1;
int fogTransmitPropertiesIdx = -1;
int lightAndShadowCountsIdx = -1;
int abufImageWidth = -1;
int listNodeCount = -1;
int viewSize = -1;
int samples = -1;
struct ImageIndices
{
int imageRotationsUniformIndex = -1;
int imageOffsetsUniformIndex = -1;
};
QVarLengthArray<ImageIndices, 16> imageIndices;
} commonUniformIndices;
struct InstanceLocations {
int transform0 = -1;
int transform1 = -1;
int transform2 = -1;
int color = -1;
int data = -1;
} instanceLocations;
enum class UniformFlag {
Mat3 = 0x01
};
Q_DECLARE_FLAGS(UniformFlags, UniformFlag)
void setUniformValue(char *ubufData, const char *name, const QVariant &value, QSSGRenderShaderValue::Type type);
void setUniform(char *ubufData, const char *name, const void *data, size_t size, int *storeIndex = nullptr, UniformFlags flags = {});
void setUniformArray(char *ubufData, const char *name, const void *data, size_t itemCount, QSSGRenderShaderValue::Type type, int *storeIndex = nullptr);
int bindingForTexture(const char *name, int hint = -1);
int bindingForStorageImage(QByteArrayView name);
int bindingForStorageBuffer(QByteArrayView name);
void setShaderResources(char *ubufData,
QSSGBufferManager &theBufferManager,
const QByteArray &inPropertyName,
const QVariant &propertyValue,
QSSGRenderShaderValue::Type inPropertyType);
void setLightsEnabled(bool enable) { m_lightsEnabled = enable; }
bool isLightingEnabled() const { return m_lightsEnabled; }
void ensureCombinedUniformBuffer(QRhiBuffer **ubuf);
void ensureUniformBuffer(QRhiBuffer **ubuf);
void setLightProbeTexture(QRhiTexture *texture,
QSSGRenderTextureCoordOp hTile = QSSGRenderTextureCoordOp::ClampToEdge,
QSSGRenderTextureCoordOp vTile = QSSGRenderTextureCoordOp::ClampToEdge)
{
m_lightProbeTexture = texture; m_lightProbeHorzTile = hTile; m_lightProbeVertTile = vTile;
}
QRhiTexture *lightProbeTexture() const { return m_lightProbeTexture; }
QPair<QSSGRenderTextureCoordOp, QSSGRenderTextureCoordOp> lightProbeTiling() const
{
return {m_lightProbeHorzTile, m_lightProbeVertTile};
}
void setScreenTexture(QRhiTexture *texture) { m_screenTexture = texture; }
QRhiTexture *screenTexture() const { return m_screenTexture; }
void setDepthTexture(QRhiTexture *texture) { m_depthTexture = texture; }
QRhiTexture *depthTexture() const { return m_depthTexture; }
void setNormalTexture(QRhiTexture *texture) { m_normalTexture = texture; }
QRhiTexture *normalTexture() const { return m_normalTexture; }
void setSsaoTexture(QRhiTexture *texture) { m_ssaoTexture = texture; }
QRhiTexture *ssaoTexture() const { return m_ssaoTexture; }
void setLightmapTexture(QRhiTexture *texture) { m_lightmapTexture = texture; }
QRhiTexture *lightmapTexture() const { return m_lightmapTexture; }
void setMotionVectorTexture(QRhiTexture *texture) { m_motionVectorTexture = texture; }
QRhiTexture *MotionVectorTexture() const { return m_motionVectorTexture; }
void setShadowMapAtlasTexture(QRhiTexture *texture) { m_shadowMapAtlasTexture = texture; }
QRhiTexture *shadowMapAtlasTexture() const { return m_shadowMapAtlasTexture; }
void setShadowMapBlueNoiseTexture(QRhiTexture *texture) { m_shadowMapBlueNoiseTexture = texture; }
QRhiTexture *shadowMapBlueNoiseTexture() const { return m_shadowMapBlueNoiseTexture; }
void setOITImages(QRhiTexture *accumulator, QRhiTexture *auxiliary, QRhiTexture *counter)
{
m_oitImages[0] = accumulator;
m_oitImages[1] = auxiliary;
m_oitImages[2] = counter;
}
QRhiTexture **oitImages() { return m_oitImages; }
void resetExtraTextures() { m_extraTextures.clear(); }
void addExtraTexture(const QSSGRhiTexture &t) { m_extraTextures.append(t); }
int extraTextureCount() const { return m_extraTextures.size(); }
const QSSGRhiTexture &extraTextureAt(int index) const { return m_extraTextures[index]; }
QSSGRhiTexture &extraTextureAt(int index) { return m_extraTextures[index]; }
struct StorageResourceBinding
{
int binding = 0;
QByteArray name;
QRhiShaderResourceBinding::Type type = QRhiShaderResourceBinding::BufferLoadStore;
QRhiBuffer *buffer = nullptr;
QRhiTexture *texture = nullptr;
StorageResourceBinding() = default;
StorageResourceBinding(int binding, const QByteArray &name, QRhiShaderResourceBinding::Type type, QRhiBuffer *buffer)
: binding(binding), name(name), type(type), buffer(buffer), texture(nullptr) {}
StorageResourceBinding(int binding, const QByteArray &name, QRhiShaderResourceBinding::Type type, QRhiTexture *texture)
: binding(binding), name(name), type(type), buffer(nullptr), texture(texture) {}
};
using StorageBindingMap = QMap<int, StorageResourceBinding>;
void addStorageBufferBinding(const QByteArray &name, QRhiShaderResourceBinding::Type type, QRhiBuffer *buffer, int binding);
void addStorageImageBinding(const QByteArray &name, QRhiShaderResourceBinding::Type type, QRhiTexture *texture, int binding);
void applyAndClearStorageBindings(QRhiShaderResourceBinding::StageFlags stage, QSSGRhiShaderResourceBindingList &bindings);
const StorageBindingMap &storageBufferBindings() const { return m_storageBufferBindings; }
const StorageBindingMap &storageImageBindings() const { return m_storageImageBindings; }
QSSGShaderLightsUniformData &lightsUniformData() { return m_lightsUniformData; }
QSSGShaderDirectionalLightsUniformData &directionalLightsUniformData() { return m_directionalLightsUniformData; }
InstanceLocations instanceBufferLocations() const { return instanceLocations; }
int offsetOfUniform(const QByteArray &name);
private:
QSSGRhiContext &m_context;
QVarLengthArray<QRhiShaderStage, 2> m_stages;
int m_ub0Size = 0;
int m_ub0NextUBufOffset = 0;
QHash<QByteArray, QShaderDescription::BlockVariable> m_ub0;
QHash<QSSGRhiInputAssemblerState::InputSemantic, QShaderDescription::InOutVariable> m_vertexInputs;
QHash<QByteArray, QShaderDescription::InOutVariable> m_combinedImageSamplers;
QHash<QByteArray, QShaderDescription::InOutVariable> m_storageImages;
QHash<QByteArray, QShaderDescription::StorageBlock> m_storageBuffers;
int m_materialImageSamplerBindings[size_t(QSSGRhiSamplerBindingHints::BindingMapSize)];
QVarLengthArray<QSSGRhiShaderUniform, 32> m_uniforms; // members of the main (binding 0) uniform buffer
QVarLengthArray<QSSGRhiShaderUniformArray, 8> m_uniformArrays;
QHash<QByteArray, size_t> m_uniformIndex; // Maps uniform name to index in m_uniforms and m_uniformArrays
// transient (per-object) data; pointers are all non-owning
bool m_lightsEnabled = false;
QSSGShaderLightsUniformData m_lightsUniformData;
QSSGShaderDirectionalLightsUniformData m_directionalLightsUniformData;
QRhiTexture *m_shadowMapAtlasTexture = nullptr;
QRhiTexture *m_shadowMapBlueNoiseTexture = nullptr;
QRhiTexture *m_lightProbeTexture = nullptr;
QSSGRenderTextureCoordOp m_lightProbeHorzTile = QSSGRenderTextureCoordOp::ClampToEdge;
QSSGRenderTextureCoordOp m_lightProbeVertTile = QSSGRenderTextureCoordOp::ClampToEdge;
QRhiTexture *m_screenTexture = nullptr;
QRhiTexture *m_depthTexture = nullptr;
QRhiTexture *m_normalTexture = nullptr;
QRhiTexture *m_ssaoTexture = nullptr;
QRhiTexture *m_lightmapTexture = nullptr;
QRhiTexture *m_oitImages[3] = {nullptr};
QRhiTexture *m_motionVectorTexture = nullptr;
QVarLengthArray<QSSGRhiTexture, 8> m_extraTextures;
QMap<int, StorageResourceBinding> m_storageImageBindings;
QMap<int, StorageResourceBinding> m_storageBufferBindings;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSSGRhiShaderPipeline::StageFlags)
Q_DECLARE_OPERATORS_FOR_FLAGS(QSSGRhiShaderPipeline::UniformFlags)
using QSSGRhiShaderPipelinePtr = std::shared_ptr<QSSGRhiShaderPipeline>;
class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiShaderResourceBindingList
{
public:
static const int MAX_SIZE = 32;
int p = 0;
size_t h = 0;
QRhiShaderResourceBinding v[MAX_SIZE];
void clear() { p = 0; h = 0; }
QSSGRhiShaderResourceBindingList() { }
QSSGRhiShaderResourceBindingList(const QSSGRhiShaderResourceBindingList &other)
: p(other.p),
h(other.h)
{
for (int i = 0; i < p; ++i)
v[i] = other.v[i];
}
QSSGRhiShaderResourceBindingList &operator=(const QSSGRhiShaderResourceBindingList &other) Q_DECL_NOTHROW
{
if (this != &other) {
p = other.p;
h = other.h;
for (int i = 0; i < p; ++i)
v[i] = other.v[i];
}
return *this;
}
void addUniformBuffer(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset = 0 , int size = 0, bool hasDynamicOffset = false);
void addTexture(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler);
void addImageLoad(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level);
void addImageStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level);
void addImageLoadStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level);
void addStorageBufferLoad(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset = 0 , int size = 0);
void addStorageBufferStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset = 0 , int size = 0);
void addStorageBufferLoadStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset = 0 , int size = 0);
private:
void fillImage(QRhiShaderResourceBinding::Data *d, QRhiShaderResourceBinding::Type t, int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level);
void fillStorageBuffer(QRhiShaderResourceBinding::Data *d, QRhiShaderResourceBinding::Type t, int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size);
QRhiShaderResourceBinding::Data *newBinding();
};
inline bool operator==(const QSSGRhiShaderResourceBindingList &a, const QSSGRhiShaderResourceBindingList &b) Q_DECL_NOTHROW
{
if (a.h != b.h)
return false;
if (a.p != b.p)
return false;
for (int i = 0; i < a.p; ++i) {
if (a.v[i] != b.v[i])
return false;
}
return true;
}
inline bool operator!=(const QSSGRhiShaderResourceBindingList &a, const QSSGRhiShaderResourceBindingList &b) Q_DECL_NOTHROW
{
return !(a == b);
}
inline size_t qHash(const QSSGRhiShaderResourceBindingList &bl, size_t seed) Q_DECL_NOTHROW
{
return bl.h ^ seed;
}
struct QSSGRhiDrawCallData
{
QRhiBuffer *ubuf = nullptr; // owned
QRhiShaderResourceBindings *srb = nullptr; // not owned
QSSGRhiShaderResourceBindingList bindings;
QRhiGraphicsPipeline *pipeline = nullptr; // not owned
size_t renderTargetDescriptionHash = 0;
QVector<quint32> renderTargetDescription;
QSSGRhiGraphicsPipelineState ps;
void reset()
{
delete ubuf;
ubuf = nullptr;
srb = nullptr;
pipeline = nullptr;
}
};
class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGManagedRhiTexture
{
enum class Private { Initialize };
Q_DISABLE_COPY(QSSGManagedRhiTexture)
public:
using Ptr = std::unique_ptr<QSSGManagedRhiTexture>;
const std::unique_ptr<QRhiTexture> &texture() const { return m_texture; }
[[nodiscard]] bool isValid() const { return m_texture != nullptr; }
QSSGManagedRhiTexture() = default;
QSSGManagedRhiTexture(const std::shared_ptr<QSSGUserRenderPassManager> &manager, QRhiTexture *texture, Private);
QSSGManagedRhiTexture(const std::shared_ptr<QSSGUserRenderPassManager> &manager, std::unique_ptr<QRhiTexture> texture);
~QSSGManagedRhiTexture();
[[nodiscard]] static Ptr make_copy(const Ptr &other)
{
return std::make_unique<QSSGManagedRhiTexture>(other->m_manager, other->m_texture.get(), Private::Initialize);
}
private:
friend class QSSGRhiRenderableTextureV2;
friend class QSSGUserRenderPassManager;
void invalidate();
std::shared_ptr<QSSGUserRenderPassManager> m_manager;
std::unique_ptr<QRhiTexture> m_texture;
};
using QSSGManagedRhiTexturePtr = QSSGManagedRhiTexture::Ptr;
class QSSGRhiRenderableTextureV2
{
enum class Private { Initialize };
public:
QSSGRhiRenderableTextureV2(const std::shared_ptr<QSSGUserRenderPassManager> &manager, Private)
: m_manager(manager)
{
}
~QSSGRhiRenderableTextureV2();
void setDescription(QRhi *rhi, QRhiTextureRenderTargetDescription rtDesc, QRhiTextureRenderTarget::Flags = {});
void setName(const QByteArray &name) { rtName = name; }
const QByteArray &getName() const { return rtName; }
const QSSGManagedRhiTexturePtr &getDepthTexture() const { return depthTexture; }
const std::unique_ptr<QRhiRenderBuffer> &getDepthStencil() const { return depthStencil; }
const std::unique_ptr<QRhiRenderPassDescriptor> &getRenderPassDescriptor() const { return rpDesc; }
const std::unique_ptr<QRhiTextureRenderTarget> &getRenderTarget() const { return rt; }
size_t colorAttachmentCount() const { return textures.size(); }
const QSSGManagedRhiTexturePtr &getColorTexture(int index) const { return textures[index]; }
bool isValid() const { return (textures.size() > 0) && rt && rpDesc; }
void finialize(QRhi *rhi);
void resetRenderTarget();
void reset();
private:
friend class QSSGUserRenderPassManager;
enum Dirty : quint32
{
ColorTextureDirty = 0x1,
DepthStencilDirty = 0x2,
DepthTextureDirty = 0x4,
};
void invalidate();
using DirtyT = std::underlying_type_t<Dirty>;
std::shared_ptr<QSSGUserRenderPassManager> m_manager;
QVarLengthArray<QSSGManagedRhiTexturePtr, 4> textures {};
std::unique_ptr<QRhiRenderBuffer> depthStencil;
QSSGManagedRhiTexturePtr depthTexture; // either depthStencil or depthTexture are valid, never both
std::unique_ptr<QRhiRenderPassDescriptor> rpDesc;
std::unique_ptr<QRhiTextureRenderTarget> rt;
QByteArray rtName;
DirtyT dirty = 0;
};
using QSSGRhiRenderableTextureV2Ptr = std::shared_ptr<QSSGRhiRenderableTextureV2>;
struct QSSGRhiRenderableTexture
{
QRhiTexture *texture = nullptr;
QRhiRenderBuffer *depthStencil = nullptr;
QRhiTexture *depthTexture = nullptr; // either depthStencil or depthTexture are valid, never both
QRhiRenderPassDescriptor *rpDesc = nullptr;
QRhiTextureRenderTarget *rt = nullptr;
bool isValid() const { return texture && rpDesc && rt; }
void resetRenderTarget() {
delete rt;
rt = nullptr;
delete rpDesc;
rpDesc = nullptr;
}
void reset() {
resetRenderTarget();
delete texture;
delete depthStencil;
delete depthTexture;
*this = QSSGRhiRenderableTexture();
}
};
struct QSSGRhiSortData
{
float d = 0.0f;
int indexOrOffset = -1;
};
struct QSSGRhiInstanceBufferData
{
QRhiBuffer *buffer = nullptr;
QByteArray sortedData;
QList<QSSGRhiSortData> sortData;
QVector3D sortedCameraDirection;
QVector3D cameraPosition;
QByteArray lodData;
int serial = -1;
bool owned = true;
bool sorting = false;
};
struct QSSGRhiParticleData
{
QRhiTexture *texture = nullptr;
QByteArray sortedData;
QByteArray convertData;
QList<QSSGRhiSortData> sortData;
int particleCount = 0;
int serial = -1;
bool sorting = false;
};
class QSSGComputePipelineStateKey
{
public:
QShader shader;
QVector<quint32> srbLayoutDescription;
struct {
size_t srbLayoutDescriptionHash;
} extra;
static QSSGComputePipelineStateKey create(const QShader &shader,
const QRhiShaderResourceBindings *srb)
{
const QVector<quint32> srbDesc = srb->serializedLayoutDescription();
return { shader, srbDesc, { qHash(srbDesc) } };
}
};
inline bool operator==(const QSSGComputePipelineStateKey &a, const QSSGComputePipelineStateKey &b) Q_DECL_NOTHROW
{
return a.shader == b.shader && a.srbLayoutDescription == b.srbLayoutDescription;
}
inline bool operator!=(const QSSGComputePipelineStateKey &a, const QSSGComputePipelineStateKey &b) Q_DECL_NOTHROW
{
return !(a == b);
}
inline size_t qHash(const QSSGComputePipelineStateKey &k, size_t seed = 0) Q_DECL_NOTHROW
{
return qHash(k.shader, seed) ^ k.extra.srbLayoutDescriptionHash;
}
struct QSSGRhiDummyTextureKey
{
QRhiTexture::Flags flags;
QSize size;
QColor color;
int arraySize;
};
inline size_t qHash(const QSSGRhiDummyTextureKey &k, size_t seed) Q_DECL_NOTHROW
{
return qHash(k.flags, seed)
^ qHash(k.size.width() ^ k.size.height() ^ k.color.red() ^ k.color.green()
^ k.color.blue() ^ k.color.alpha() ^ k.arraySize);
}
inline bool operator==(const QSSGRhiDummyTextureKey &a, const QSSGRhiDummyTextureKey &b) Q_DECL_NOTHROW
{
return a.flags == b.flags && a.size == b.size && a.color == b.color && a.arraySize == b.arraySize;
}
inline bool operator!=(const QSSGRhiDummyTextureKey &a, const QSSGRhiDummyTextureKey &b) Q_DECL_NOTHROW
{
return !(a == b);
}
class QSSGGraphicsPipelineStateKey
{
public:
QSSGRhiGraphicsPipelineState state;
QVector<quint32> renderTargetDescription;
QVector<quint32> srbLayoutDescription;
struct {
size_t renderTargetDescriptionHash;
size_t srbLayoutDescriptionHash;
} extra;
static QSSGGraphicsPipelineStateKey create(const QSSGRhiGraphicsPipelineState &state,
const QRhiRenderPassDescriptor *rpDesc,
const QRhiShaderResourceBindings *srb)
{
const QVector<quint32> rtDesc = rpDesc->serializedFormat();
const QVector<quint32> srbDesc = srb->serializedLayoutDescription();
return { state, rtDesc, srbDesc, { qHash(rtDesc), qHash(srbDesc) } };
}
};
inline bool operator==(const QSSGGraphicsPipelineStateKey &a, const QSSGGraphicsPipelineStateKey &b) Q_DECL_NOTHROW
{
return a.state == b.state
&& a.renderTargetDescription == b.renderTargetDescription
&& a.srbLayoutDescription == b.srbLayoutDescription;
}
inline bool operator!=(const QSSGGraphicsPipelineStateKey &a, const QSSGGraphicsPipelineStateKey &b) Q_DECL_NOTHROW
{
return !(a == b);
}
inline size_t qHash(const QSSGGraphicsPipelineStateKey &k, size_t seed) Q_DECL_NOTHROW
{
return qHash(k.state, seed)
^ k.extra.renderTargetDescriptionHash
^ k.extra.srbLayoutDescriptionHash;
}
#define QSSGRHICTX_STAT(ctx, f) \
for (bool qssgrhictxlog_enabled = QSSGRhiContextStats::get(*ctx).isEnabled(); qssgrhictxlog_enabled; qssgrhictxlog_enabled = false) \
QSSGRhiContextStats::get(*ctx).f
class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiContextStats
{
public:
[[nodiscard]] static QSSGRhiContextStats &get(QSSGRhiContext &rhiCtx);
[[nodiscard]] static const QSSGRhiContextStats &get(const QSSGRhiContext &rhiCtx);
struct DrawInfo {
quint64 callCount = 0;
quint64 vertexOrIndexCount = 0;
};
struct InstancedDrawInfo {
quint64 callCount = 0;
quint64 vertexOrIndexCount = 0;
quint64 instanceCount = 0;
};
struct RenderPassInfo {
QByteArray rtName;
QSize pixelSize;
DrawInfo indexedDraws;
DrawInfo draws;
InstancedDrawInfo instancedIndexedDraws;
InstancedDrawInfo instancedDraws;
};
struct PerLayerInfo {
PerLayerInfo()
{
externalRenderPass.rtName = QByteArrayLiteral("Qt Quick");
}
// The main render pass if renderMode==Offscreen, plus render passes
// for shadow maps, postprocessing effects, etc.
QVector<RenderPassInfo> renderPasses;
// An Underlay/Overlay/Inline renderMode will make the View3D add stuff
// to a render pass managed by Qt Quick. (external == not under the
// control of Qt Quick 3D)
RenderPassInfo externalRenderPass;
int currentRenderPassIndex = -1;
};
struct GlobalInfo { // global as in per QSSGRhiContext which is per-QQuickWindow
quint64 meshDataSize = 0;
quint64 imageDataSize = 0;
qint64 materialGenerationTime = 0;
qint64 effectGenerationTime = 0;
};
QHash<QSSGRenderLayer *, PerLayerInfo> perLayerInfo;
GlobalInfo globalInfo;
QSSGRhiContextStats(QSSGRhiContext &context)
: rhiCtx(&context)
{
}
// The data collected have four consumers:
//
// - Printed on debug output when QSG_RENDERER_DEBUG has the relevant key.
// (this way the debug output from the 2D scenegraph renderer and these 3D
// statistics appear nicely intermixed)
// - Passed on to the QML profiler when profiling is enabled.
// - DebugView via QQuick3DRenderStats.
// - When tracing is enabled
//
// The first two are enabled globally, but DebugView needs a dynamic
// enable/disable since we want to collect data when a DebugView item
// becomes visible, but not otherwise.
static bool profilingEnabled();
static bool rendererDebugEnabled();
bool isEnabled() const;
void drawIndexed(quint32 indexCount, quint32 instanceCount);
void draw(quint32 vertexCount, quint32 instanceCount);
void meshDataSizeChanges(quint64 newSize) // can be called outside start-stop
{
globalInfo.meshDataSize = newSize;
}
void imageDataSizeChanges(quint64 newSize) // can be called outside start-stop
{
globalInfo.imageDataSize = newSize;
}
void registerMaterialShaderGenerationTime(qint64 ms)
{
globalInfo.materialGenerationTime += ms;
}
void registerEffectShaderGenerationTime(qint64 ms)
{
globalInfo.effectGenerationTime += ms;
}
static quint64 totalDrawCallCountForPass(const QSSGRhiContextStats::RenderPassInfo &pass)
{
return pass.draws.callCount
+ pass.indexedDraws.callCount
+ pass.instancedDraws.callCount
+ pass.instancedIndexedDraws.callCount;
}
static quint64 totalVertexCountForPass(const QSSGRhiContextStats::RenderPassInfo &pass)
{
return pass.draws.vertexOrIndexCount
+ pass.indexedDraws.vertexOrIndexCount
+ pass.instancedDraws.vertexOrIndexCount
+ pass.instancedIndexedDraws.vertexOrIndexCount;
}
void start(QSSGRenderLayer *layer);
void stop(QSSGRenderLayer *layer);
void beginRenderPass(QRhiTextureRenderTarget *rt);
void endRenderPass();
void printRenderPass(const RenderPassInfo &rp);
void cleanupLayerInfo(QSSGRenderLayer *layer);
QSSGRhiContext *rhiCtx;
QSSGRenderLayer *layerKey = nullptr;
QSet<QSSGRenderLayer *> dynamicDataSources;
};
class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiContextPrivate
{
Q_DECLARE_PUBLIC(QSSGRhiContext)
explicit QSSGRhiContextPrivate(QSSGRhiContext &rhiCtx, QRhi *rhi_)
: q_ptr(&rhiCtx)
, m_rhi(rhi_)
, m_stats(rhiCtx)
{}
public:
using Textures = QSet<QRhiTexture *>;
using Meshes = QSet<QSSGRenderMesh *>;
[[nodiscard]] static QSSGRhiContextPrivate *get(QSSGRhiContext *q) { return q->d_ptr.get(); }
[[nodiscard]] static const QSSGRhiContextPrivate *get(const QSSGRhiContext *q) { return q->d_ptr.get(); }
[[nodiscard]] static bool shaderDebuggingEnabled();
[[nodiscard]] static bool editorMode();
void setMainRenderPassDescriptor(QRhiRenderPassDescriptor *rpDesc);
void setCommandBuffer(QRhiCommandBuffer *cb);
void setRenderTarget(QRhiRenderTarget *rt);
void setMainPassSampleCount(int samples);
void setMainPassViewCount(int viewCount);
void releaseCachedResources();
void registerTexture(QRhiTexture *texture);
void releaseTexture(QRhiTexture *texture);
void registerMesh(QSSGRenderMesh *mesh);
void releaseMesh(QSSGRenderMesh *mesh);
QRhiShaderResourceBindings *srb(const QSSGRhiShaderResourceBindingList &bindings);
void releaseCachedSrb(QSSGRhiShaderResourceBindingList &bindings);
QRhiGraphicsPipeline *pipeline(const QSSGRhiGraphicsPipelineState &ps,
QRhiRenderPassDescriptor *rpDesc,
QRhiShaderResourceBindings *srb);
QRhiGraphicsPipeline *pipeline(const QSSGGraphicsPipelineStateKey &key,
QRhiRenderPassDescriptor *rpDesc,
QRhiShaderResourceBindings *srb);
QRhiComputePipeline *computePipeline(const QShader &shader,
QRhiShaderResourceBindings *srb);
QRhiComputePipeline *computePipeline(const QSSGComputePipelineStateKey &key,
QRhiShaderResourceBindings *srb);
QSSGRhiDrawCallData &drawCallData(const QSSGRhiDrawCallDataKey &key);
void releaseDrawCallData(QSSGRhiDrawCallData &dcd);
void cleanupDrawCallData(const QSSGRenderModel *model);
void cleanupDrawCallDataForResource(const void *resource);
void cleanupDrawCallDataForCid(const void *cid);
void cleanupDrawCallDataForSkyMaterial(QSSGRenderSkyMaterial *skyMaterial);
QSSGRhiInstanceBufferData &instanceBufferData(QSSGRenderInstanceTable *instanceTable);
void releaseInstanceBuffer(QSSGRenderInstanceTable *instanceTable);
QSSGRhiInstanceBufferData &instanceBufferData(const QSSGRenderModel *model);
QSSGRhiParticleData &particleData(const QSSGRenderGraphObject *particlesOrModel);
QSSGRhiContext *q_ptr = nullptr;
QRhi *m_rhi = nullptr;
QRhiRenderPassDescriptor *m_mainRpDesc = nullptr;
QRhiCommandBuffer *m_cb = nullptr;
QRhiRenderTarget *m_rt = nullptr;
Textures m_textures;
Meshes m_meshes;
int m_mainSamples = 1;
int m_mainViewCount = 1;
QVector<QPair<QSSGRhiSamplerDescription, QRhiSampler*>> m_samplers;
QHash<QSSGRhiDrawCallDataKey, QSSGRhiDrawCallData> m_drawCallData;
QHash<QSSGRhiShaderResourceBindingList, QRhiShaderResourceBindings *> m_srbCache;
QHash<QSSGGraphicsPipelineStateKey, QRhiGraphicsPipeline *> m_pipelines;
QHash<QSSGComputePipelineStateKey, QRhiComputePipeline *> m_computePipelines;
QHash<QSSGRhiDummyTextureKey, QRhiTexture *> m_dummyTextures;
QHash<QSSGRenderInstanceTable *, QSSGRhiInstanceBufferData> m_instanceBuffers;
QHash<const QSSGRenderModel *, QSSGRhiInstanceBufferData> m_instanceBuffersLod;
QHash<const QSSGRenderGraphObject *, QSSGRhiParticleData> m_particleData;
QSSGRhiContextStats m_stats;
};
inline bool operator==(const QSSGRhiDrawCallDataKey &a, const QSSGRhiDrawCallDataKey &b) noexcept
{
return a.cid == b.cid && a.model == b.model && a.resource == b.resource && a.mapEntry == b.mapEntry
&& a.resourceIdx == b.resourceIdx;
}
inline bool operator!=(const QSSGRhiDrawCallDataKey &a, const QSSGRhiDrawCallDataKey &b) noexcept
{
return !(a == b);
}
inline size_t qHash(const QSSGRhiDrawCallDataKey &k, size_t seed = 0) noexcept
{
return qHashMulti(seed, k.cid, k.model, k.resource, k.mapEntry, k.resourceIdx);
}
QT_END_NAMESPACE
#endif // QSSGRHICONTEXT_P_H