blob: 1aa087bffb82ae1c770389832320dc6e277bbcfa [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2008-2012 NVIDIA Corporation.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick 3D.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qssgrendercustommaterialsystem_p.h"
#include <QtQuick3DUtils/private/qssgutils_p.h>
#include <QtQuick3DRender/private/qssgrendercontext_p.h>
#include <QtQuick3DRender/private/qssgrendershaderprogram_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercustommaterialrendercontext_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
//#include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderdynamicobjectsystemcommands_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderresourcemanager_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderdynamicobjectsystemutil_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderableimage_p.h>
#include <QtQuick3DRuntimeRender/private/qssgvertexpipelineimpl_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendererimpllayerrenderdata_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderprefiltertexture_p.h>
QT_BEGIN_NAMESPACE
QSSGCustomMaterialVertexPipeline::QSSGCustomMaterialVertexPipeline(QSSGRenderContextInterface *inContext,
TessellationModeValues inTessMode)
: QSSGVertexPipelineImpl(inContext->customMaterialShaderGenerator(), inContext->shaderProgramGenerator(), false)
, m_context(inContext)
, m_tessMode(TessellationModeValues::NoTessellation)
{
if (m_context->renderContext()->supportsTessellation()) {
m_tessMode = inTessMode;
}
if (m_context->renderContext()->supportsGeometryStage() && m_tessMode != TessellationModeValues::NoTessellation) {
m_wireframe = inContext->wireframeMode();
}
}
void QSSGCustomMaterialVertexPipeline::initializeTessControlShader()
{
if (m_tessMode == TessellationModeValues::NoTessellation
|| programGenerator()->getStage(QSSGShaderGeneratorStage::TessControl) == nullptr) {
return;
}
QSSGShaderStageGeneratorInterface &tessCtrlShader(*programGenerator()->getStage(QSSGShaderGeneratorStage::TessControl));
tessCtrlShader.addUniform("tessLevelInner", "float");
tessCtrlShader.addUniform("tessLevelOuter", "float");
setupTessIncludes(QSSGShaderGeneratorStage::TessControl, m_tessMode);
tessCtrlShader.append("void main() {\n");
tessCtrlShader.append("\tctWorldPos[0] = varWorldPos[0];");
tessCtrlShader.append("\tctWorldPos[1] = varWorldPos[1];");
tessCtrlShader.append("\tctWorldPos[2] = varWorldPos[2];");
if (m_tessMode == TessellationModeValues::Phong || m_tessMode == TessellationModeValues::NPatch) {
tessCtrlShader.append("\tctNorm[0] = varObjectNormal[0];");
tessCtrlShader.append("\tctNorm[1] = varObjectNormal[1];");
tessCtrlShader.append("\tctNorm[2] = varObjectNormal[2];");
}
if (m_tessMode == TessellationModeValues::NPatch) {
tessCtrlShader.append("\tctTangent[0] = varObjTangent[0];");
tessCtrlShader.append("\tctTangent[1] = varObjTangent[1];");
tessCtrlShader.append("\tctTangent[2] = varObjTangent[2];");
}
tessCtrlShader.append("\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;");
tessCtrlShader.append("\ttessShader( tessLevelOuter, tessLevelInner);\n");
}
void QSSGCustomMaterialVertexPipeline::initializeTessEvaluationShader()
{
if (m_tessMode == TessellationModeValues::NoTessellation
|| programGenerator()->getStage(QSSGShaderGeneratorStage::TessEval) == nullptr) {
return;
}
QSSGShaderStageGeneratorInterface &tessEvalShader(*programGenerator()->getStage(QSSGShaderGeneratorStage::TessEval));
tessEvalShader.addUniform("modelViewProjection", "mat4");
tessEvalShader.addUniform("normalMatrix", "mat3");
setupTessIncludes(QSSGShaderGeneratorStage::TessEval, m_tessMode);
if (m_tessMode == TessellationModeValues::Linear && m_displacementImage) {
tessEvalShader.addInclude("defaultMaterialFileDisplacementTexture.glsllib");
tessEvalShader.addUniform("modelMatrix", "mat4");
tessEvalShader.addUniform("displace_tiling", "vec3");
tessEvalShader.addUniform("displaceAmount", "float");
tessEvalShader.addUniform(m_displacementImage->m_image.m_imageShaderName.toUtf8(), "sampler2D");
}
tessEvalShader.append("void main() {");
if (m_tessMode == TessellationModeValues::NPatch) {
tessEvalShader.append("\tctNorm[0] = varObjectNormalTC[0];");
tessEvalShader.append("\tctNorm[1] = varObjectNormalTC[1];");
tessEvalShader.append("\tctNorm[2] = varObjectNormalTC[2];");
tessEvalShader.append("\tctTangent[0] = varTangentTC[0];");
tessEvalShader.append("\tctTangent[1] = varTangentTC[1];");
tessEvalShader.append("\tctTangent[2] = varTangentTC[2];");
}
tessEvalShader.append("\tvec4 pos = tessShader( );\n");
}
void QSSGCustomMaterialVertexPipeline::finalizeTessControlShader()
{
QSSGShaderStageGeneratorInterface &tessCtrlShader(*programGenerator()->getStage(QSSGShaderGeneratorStage::TessControl));
// add varyings we must pass through
typedef TStrTableStrMap::const_iterator TParamIter;
for (TParamIter iter = m_interpolationParameters.begin(), end = m_interpolationParameters.end(); iter != end; ++iter) {
tessCtrlShader << "\t" << iter.key() << "TC[gl_InvocationID] = " << iter.key() << "[gl_InvocationID];\n";
}
}
void QSSGCustomMaterialVertexPipeline::finalizeTessEvaluationShader()
{
QSSGShaderStageGeneratorInterface &tessEvalShader(*programGenerator()->getStage(QSSGShaderGeneratorStage::TessEval));
QByteArray outExt;
if (programGenerator()->getEnabledStages() & QSSGShaderGeneratorStage::Geometry)
outExt = "TE";
// add varyings we must pass through
typedef TStrTableStrMap::const_iterator TParamIter;
if (m_tessMode == TessellationModeValues::NPatch) {
for (TParamIter iter = m_interpolationParameters.begin(), end = m_interpolationParameters.end(); iter != end; ++iter) {
tessEvalShader << "\t" << iter.key() << outExt << " = gl_TessCoord.z * " << iter.key() << "TC[0] + ";
tessEvalShader << "gl_TessCoord.x * " << iter.key() << "TC[1] + ";
tessEvalShader << "gl_TessCoord.y * " << iter.key() << "TC[2];\n";
}
// transform the normal
if (m_generationFlags & GenerationFlag::WorldNormal)
tessEvalShader << "\n\tvarNormal" << outExt << " = normalize(normalMatrix * teNorm);\n";
// transform the tangent
if (m_generationFlags & GenerationFlag::TangentBinormal) {
tessEvalShader << "\n\tvarTangent" << outExt << " = normalize(normalMatrix * teTangent);\n";
// transform the binormal
tessEvalShader << "\n\tvarBinormal" << outExt << " = normalize(normalMatrix * teBinormal);\n";
}
} else {
for (TParamIter iter = m_interpolationParameters.begin(), end = m_interpolationParameters.end(); iter != end; ++iter) {
tessEvalShader << "\t" << iter.key() << outExt << " = gl_TessCoord.x * " << iter.key() << "TC[0] + ";
tessEvalShader << "gl_TessCoord.y * " << iter.key() << "TC[1] + ";
tessEvalShader << "gl_TessCoord.z * " << iter.key() << "TC[2];\n";
}
// displacement mapping makes only sense with linear tessellation
if (m_tessMode == TessellationModeValues::Linear && m_displacementImage) {
tessEvalShader << "\ttexture_coordinate_info tmp = textureCoordinateInfo( varTexCoord0" << outExt
<< ", varTangent" << outExt << ", varBinormal" << outExt << " );"
<< "\n";
tessEvalShader << "\ttmp = transformCoordinate( rotationTranslationScale( vec3( "
"0.000000, 0.000000, 0.000000 ), vec3( 0.000000, 0.000000, "
"0.000000 ), displace_tiling ), tmp);"
<< "\n";
tessEvalShader << "\tpos.xyz = defaultMaterialFileDisplacementTexture( "
<< m_displacementImage->m_image.m_imageShaderName.toUtf8() << ", displaceAmount, "
<< "tmp.position.xy";
tessEvalShader << ", varObjectNormal" << outExt << ", pos.xyz );"
<< "\n";
tessEvalShader << "\tvarWorldPos" << outExt << "= (modelMatrix * pos).xyz;"
<< "\n";
}
// transform the normal
tessEvalShader << "\n\tvarNormal" << outExt << " = normalize(normalMatrix * varObjectNormal" << outExt << ");\n";
}
tessEvalShader.append("\tgl_Position = modelViewProjection * pos;\n");
}
// Responsible for beginning all vertex and fragment generation (void main() { etc).
void QSSGCustomMaterialVertexPipeline::beginVertexGeneration(quint32 displacementImageIdx, QSSGRenderableImage *displacementImage)
{
m_displacementIdx = displacementImageIdx;
m_displacementImage = displacementImage;
QSSGShaderGeneratorStageFlags theStages(QSSGShaderProgramGeneratorInterface::defaultFlags());
if (m_tessMode != TessellationModeValues::NoTessellation) {
theStages |= QSSGShaderGeneratorStage::TessControl;
theStages |= QSSGShaderGeneratorStage::TessEval;
}
if (m_wireframe) {
theStages |= QSSGShaderGeneratorStage::Geometry;
}
programGenerator()->beginProgram(theStages);
if (m_tessMode != TessellationModeValues::NoTessellation) {
initializeTessControlShader();
initializeTessEvaluationShader();
}
if (m_wireframe) {
initializeWireframeGeometryShader();
}
QSSGShaderStageGeneratorInterface &vertexShader(vertex());
// thinks we need
vertexShader.addInclude("viewProperties.glsllib");
vertexShader.addInclude("customMaterial.glsllib");
vertexShader.addIncoming("attr_pos", "vec3");
vertexShader << "void main()"
<< "\n"
<< "{"
<< "\n";
if (displacementImage) {
generateUVCoords(0);
if (!hasTessellation()) {
vertexShader.addUniform("displaceAmount", "float");
vertexShader.addUniform("displace_tiling", "vec3");
// we create the world position setup here
// because it will be replaced with the displaced position
setCode(GenerationFlag::WorldPosition);
vertexShader.addUniform("modelMatrix", "mat4");
vertexShader.addInclude("defaultMaterialFileDisplacementTexture.glsllib");
vertexShader.addUniform(displacementImage->m_image.m_imageShaderName.toUtf8(), "sampler2D");
vertexShader << "\ttexture_coordinate_info tmp = textureCoordinateInfo( texCoord0, "
"varTangent, varBinormal );"
<< "\n";
vertexShader << "\ttmp = transformCoordinate( rotationTranslationScale( vec3( "
"0.000000, 0.000000, 0.000000 ), vec3( 0.000000, 0.000000, "
"0.000000 ), displace_tiling ), tmp);"
<< "\n";
vertexShader << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( "
<< displacementImage->m_image.m_imageShaderName.toUtf8() << ", displaceAmount, "
<< "tmp.position.xy"
<< ", attr_norm, attr_pos );"
<< "\n";
addInterpolationParameter("varWorldPos", "vec3");
vertexShader.append("\tvec3 local_model_world_position = (modelMatrix * "
"vec4(displacedPos, 1.0)).xyz;");
assignOutput("varWorldPos", "local_model_world_position");
}
}
if (hasTessellation()) {
vertexShader.append("\tgl_Position = vec4(attr_pos, 1.0);");
} else {
vertexShader.addUniform("modelViewProjection", "mat4");
if (displacementImage)
vertexShader.append("\tgl_Position = modelViewProjection * vec4(displacedPos, 1.0);");
else
vertexShader.append("\tgl_Position = modelViewProjection * vec4(attr_pos, 1.0);");
}
if (hasTessellation()) {
generateWorldPosition();
generateWorldNormal();
generateObjectNormal();
generateVarTangentAndBinormal();
}
}
void QSSGCustomMaterialVertexPipeline::beginFragmentGeneration()
{
fragment().addUniform("objectOpacity", "float");
fragment() << "void main()"
<< "\n"
<< "{"
<< "\n";
}
void QSSGCustomMaterialVertexPipeline::assignOutput(const QByteArray &inVarName, const QByteArray &inVarValue)
{
vertex() << "\t" << inVarName << " = " << inVarValue << ";\n";
}
void QSSGCustomMaterialVertexPipeline::generateUVCoords(quint32 inUVSet)
{
if (inUVSet == 0 && setCode(GenerationFlag::UVCoords))
return;
if (inUVSet == 1 && setCode(GenerationFlag::UVCoords1))
return;
Q_ASSERT(inUVSet == 0 || inUVSet == 1);
if (inUVSet == 0)
addInterpolationParameter("varTexCoord0", "vec3");
else if (inUVSet == 1)
addInterpolationParameter("varTexCoord1", "vec3");
doGenerateUVCoords(inUVSet);
}
void QSSGCustomMaterialVertexPipeline::generateWorldNormal()
{
if (setCode(GenerationFlag::WorldNormal))
return;
addInterpolationParameter("varNormal", "vec3");
doGenerateWorldNormal();
}
void QSSGCustomMaterialVertexPipeline::generateObjectNormal()
{
if (setCode(GenerationFlag::ObjectNormal))
return;
doGenerateObjectNormal();
}
void QSSGCustomMaterialVertexPipeline::generateVarTangentAndBinormal()
{
if (setCode(GenerationFlag::TangentBinormal))
return;
addInterpolationParameter("varTangent", "vec3");
addInterpolationParameter("varBinormal", "vec3");
addInterpolationParameter("varObjTangent", "vec3");
addInterpolationParameter("varObjBinormal", "vec3");
doGenerateVarTangentAndBinormal();
}
void QSSGCustomMaterialVertexPipeline::generateWorldPosition()
{
if (setCode(GenerationFlag::WorldPosition))
return;
activeStage().addUniform("modelMatrix", "mat4");
addInterpolationParameter("varWorldPos", "vec3");
addInterpolationParameter("varObjPos", "vec3");
doGenerateWorldPosition();
}
// responsible for closing all vertex and fragment generation
void QSSGCustomMaterialVertexPipeline::endVertexGeneration(bool customShader)
{
if (hasTessellation()) {
// finalize tess control shader
finalizeTessControlShader();
// finalize tess evaluation shader
finalizeTessEvaluationShader();
tessControl().append("}");
tessEval().append("}");
if (m_wireframe) {
// finalize geometry shader
finalizeWireframeGeometryShader();
geometry().append("}");
}
}
if (!customShader)
vertex().append("}");
}
void QSSGCustomMaterialVertexPipeline::endFragmentGeneration(bool customShader)
{
if (!customShader)
fragment().append("}");
}
QSSGShaderStageGeneratorInterface &QSSGCustomMaterialVertexPipeline::activeStage()
{
return vertex();
}
void QSSGCustomMaterialVertexPipeline::addInterpolationParameter(const QByteArray &inName, const QByteArray &inType)
{
m_interpolationParameters.insert(inName, inType);
vertex().addOutgoing(inName, inType);
fragment().addIncoming(inName, inType);
if (hasTessellation()) {
QByteArray nameBuilder(inName);
nameBuilder.append("TC");
tessControl().addOutgoing(nameBuilder, inType);
nameBuilder = inName;
if (programGenerator()->getEnabledStages() & QSSGShaderGeneratorStage::Geometry) {
nameBuilder.append("TE");
geometry().addOutgoing(inName, inType);
}
tessEval().addOutgoing(nameBuilder, inType);
}
}
void QSSGCustomMaterialVertexPipeline::doGenerateUVCoords(quint32 inUVSet)
{
Q_ASSERT(inUVSet == 0 || inUVSet == 1);
if (inUVSet == 0) {
vertex().addIncoming("attr_uv0", "vec2");
vertex() << "\tvec3 texCoord0 = vec3( attr_uv0, 0.0 );"
<< "\n";
assignOutput("varTexCoord0", "texCoord0");
} else if (inUVSet == 1) {
vertex().addIncoming("attr_uv1", "vec2");
vertex() << "\tvec3 texCoord1 = vec3( attr_uv1, 1.0 );"
<< "\n";
assignOutput("varTexCoord1", "texCoord1");
}
}
void QSSGCustomMaterialVertexPipeline::doGenerateWorldNormal()
{
QSSGShaderStageGeneratorInterface &vertexGenerator(vertex());
vertexGenerator.addIncoming("attr_norm", "vec3");
vertexGenerator.addUniform("normalMatrix", "mat3");
if (hasTessellation() == false) {
vertex().append("\tvarNormal = normalize( normalMatrix * attr_norm );");
}
}
void QSSGCustomMaterialVertexPipeline::doGenerateObjectNormal()
{
addInterpolationParameter("varObjectNormal", "vec3");
vertex().append("\tvarObjectNormal = attr_norm;");
}
void QSSGCustomMaterialVertexPipeline::doGenerateWorldPosition()
{
vertex().append("\tvarObjPos = attr_pos;");
vertex().append("\tvec4 worldPos = (modelMatrix * vec4(attr_pos, 1.0));");
assignOutput("varWorldPos", "worldPos.xyz");
}
void QSSGCustomMaterialVertexPipeline::doGenerateVarTangentAndBinormal()
{
vertex().addIncoming("attr_textan", "vec3");
vertex().addIncoming("attr_binormal", "vec3");
vertex() << "\tvarTangent = normalMatrix * attr_textan;"
<< "\n"
<< "\tvarBinormal = normalMatrix * attr_binormal;"
<< "\n";
vertex() << "\tvarObjTangent = attr_textan;"
<< "\n"
<< "\tvarObjBinormal = attr_binormal;"
<< "\n";
}
void QSSGCustomMaterialVertexPipeline::doGenerateVertexColor()
{
vertex().addIncoming("attr_color", "vec3");
vertex().append("\tvarColor = attr_color;");
}
struct QSSGShaderMapKey
{
TStrStrPair m_name;
ShaderFeatureSetList m_features;
TessellationModeValues m_tessMode;
bool m_wireframeMode;
QSSGShaderDefaultMaterialKey m_materialKey;
uint m_hashCode;
QSSGShaderMapKey(const TStrStrPair &inName,
const ShaderFeatureSetList &inFeatures,
TessellationModeValues inTessMode,
bool inWireframeMode,
QSSGShaderDefaultMaterialKey inMaterialKey)
: m_name(inName), m_features(inFeatures), m_tessMode(inTessMode), m_wireframeMode(inWireframeMode), m_materialKey(inMaterialKey)
{
m_hashCode = qHash(m_name) ^ hashShaderFeatureSet(m_features) ^ qHash(m_tessMode) ^ qHash(m_wireframeMode)
^ qHash(inMaterialKey.hash());
}
bool operator==(const QSSGShaderMapKey &inKey) const
{
return m_name == inKey.m_name && m_features == inKey.m_features && m_tessMode == inKey.m_tessMode
&& m_wireframeMode == inKey.m_wireframeMode && m_materialKey == inKey.m_materialKey;
}
};
uint qHash(const QSSGShaderMapKey &key)
{
return key.m_hashCode;
}
struct QSSGCustomMaterialTextureData
{
QAtomicInt ref;
QSSGRef<QSSGRenderShaderProgram> shader;
QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> sampler;
QSSGRef<QSSGRenderTexture2D> texture;
bool needsMips;
QSSGCustomMaterialTextureData(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGRef<QSSGRenderTexture2D> &inTexture,
const QByteArray &inTexName,
bool inNeedMips)
: shader(inShader), sampler(inTexName, inShader), texture(inTexture), needsMips(inNeedMips)
{
}
void set(const QSSGRenderCustomMaterial::TextureProperty *inDefinition)
{
if (texture && inDefinition) {
texture->setMagFilter(inDefinition->magFilterType);
texture->setMinFilter(inDefinition->minFilterType);
texture->setTextureWrapS(inDefinition->clampType);
texture->setTextureWrapT(inDefinition->clampType);
} else if (texture) {
// set some defaults
texture->setMinFilter(QSSGRenderTextureMinifyingOp::Linear);
texture->setTextureWrapS(QSSGRenderTextureCoordOp::ClampToEdge);
texture->setTextureWrapT(QSSGRenderTextureCoordOp::ClampToEdge);
}
if ((texture->numMipmaps() == 0) && needsMips)
texture->generateMipmaps();
sampler.set(texture.data());
}
static QSSGCustomMaterialTextureData createTextureEntry(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGRef<QSSGRenderTexture2D> &inTexture,
const QByteArray &inTexName,
bool needMips)
{
return QSSGCustomMaterialTextureData(inShader, inTexture, inTexName, needMips);
}
};
/**
* Cached tessellation property lookups this is on a per mesh base
*/
struct QSSGCustomMaterialsTessellationProperties
{
QSSGRenderCachedShaderProperty<float> m_edgeTessLevel; ///< tesselation value for the edges
QSSGRenderCachedShaderProperty<float> m_insideTessLevel; ///< tesselation value for the inside
QSSGRenderCachedShaderProperty<float> m_phongBlend; ///< blending between linear and phong component
QSSGRenderCachedShaderProperty<QVector2D> m_distanceRange; ///< distance range for min and max tess level
QSSGRenderCachedShaderProperty<float> m_disableCulling; ///< if set to 1.0 this disables backface
/// culling optimization in the tess shader
QSSGCustomMaterialsTessellationProperties() = default;
explicit QSSGCustomMaterialsTessellationProperties(const QSSGRef<QSSGRenderShaderProgram> &inShader)
: m_edgeTessLevel("tessLevelOuter", inShader)
, m_insideTessLevel("tessLevelInner", inShader)
, m_phongBlend("phongBlend", inShader)
, m_distanceRange("distanceRange", inShader)
, m_disableCulling("disableCulling", inShader)
{
}
};
/* We setup some shared state on the custom material shaders */
struct QSSGRenderCustomMaterialShader
{
QAtomicInt ref;
QSSGRef<QSSGRenderShaderProgram> shader;
QSSGRenderCachedShaderProperty<QMatrix4x4> modelMatrix;
QSSGRenderCachedShaderProperty<QMatrix4x4> viewProjMatrix;
QSSGRenderCachedShaderProperty<QMatrix4x4> viewMatrix;
QSSGRenderCachedShaderProperty<QMatrix3x3> normalMatrix;
QSSGRenderCachedShaderProperty<QVector3D> cameraPos;
QSSGRenderCachedShaderProperty<QMatrix4x4> projMatrix;
QSSGRenderCachedShaderProperty<QMatrix4x4> viewportMatrix;
QSSGRenderCachedShaderProperty<QVector2D> camProperties;
QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> depthTexture;
QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> aoTexture;
QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> lightProbe;
QSSGRenderCachedShaderProperty<QVector4D> lightProbeProps;
QSSGRenderCachedShaderProperty<QVector4D> lightProbeOpts;
QSSGRenderCachedShaderProperty<QVector4D> lightProbeRot;
QSSGRenderCachedShaderProperty<QVector4D> lightProbeOfs;
QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> lightProbe2;
QSSGRenderCachedShaderProperty<QVector4D> lightProbe2Props;
QSSGRenderCachedShaderProperty<qint32> lightCount;
QSSGRenderCachedShaderProperty<qint32> areaLightCount;
QSSGRenderCachedShaderBuffer<QSSGRenderShaderConstantBuffer> aoShadowParams;
QSSGCustomMaterialsTessellationProperties tessellation;
dynamic::QSSGDynamicShaderProgramFlags programFlags;
QSSGRenderCustomMaterialShader(const QSSGRef<QSSGRenderShaderProgram> &inShader, dynamic::QSSGDynamicShaderProgramFlags inFlags)
: shader(inShader)
, modelMatrix("modelMatrix", inShader)
, viewProjMatrix("modelViewProjection", inShader)
, viewMatrix("viewMatrix", inShader)
, normalMatrix("normalMatrix", inShader)
, cameraPos("cameraPosition", inShader)
, projMatrix("viewProjectionMatrix", inShader)
, viewportMatrix("viewportMatrix", inShader)
, camProperties("cameraProperties", inShader)
, depthTexture("depthTexture", inShader)
, aoTexture("aoTexture", inShader)
, lightProbe("lightProbe", inShader)
, lightProbeProps("lightProbeProperties", inShader)
, lightProbeOpts("lightProbeOptions", inShader)
, lightProbeRot("lightProbeRotation", inShader)
, lightProbeOfs("lightProbeOffset", inShader)
, lightProbe2("lightProbe2", inShader)
, lightProbe2Props("lightProbe2Properties", inShader)
, lightCount("lightCount", inShader)
, areaLightCount("areaLightCount", inShader)
, aoShadowParams("aoShadow", inShader)
, tessellation(inShader)
, programFlags(inFlags)
{
}
};
struct QSSGMaterialOrComputeShader
{
// TODO: struct/class?
const QSSGRef<QSSGRenderCustomMaterialShader> m_materialShader;
const QSSGRef<QSSGRenderShaderProgram> m_computeShader;
QSSGMaterialOrComputeShader() = default;
explicit QSSGMaterialOrComputeShader(const QSSGRef<QSSGRenderCustomMaterialShader> &inMaterialShader)
: m_materialShader(inMaterialShader)
{
}
explicit QSSGMaterialOrComputeShader(const QSSGRef<QSSGRenderShaderProgram> &inComputeShader)
: m_computeShader(inComputeShader)
{
Q_ASSERT(inComputeShader->programType() == QSSGRenderShaderProgram::ProgramType::Compute);
}
bool isValid() const { return m_materialShader || m_computeShader; }
bool isComputeShader() const { return m_computeShader != nullptr; }
bool isMaterialShader() const { return m_materialShader != nullptr; }
const QSSGRef<QSSGRenderCustomMaterialShader> &materialShader()
{
Q_ASSERT(isMaterialShader());
return m_materialShader;
}
const QSSGRef<QSSGRenderShaderProgram> &computeShader()
{
Q_ASSERT(isComputeShader());
return m_computeShader;
}
};
struct QSSGRenderCustomMaterialBuffer
{
QByteArray name;
QSSGRef<QSSGRenderFrameBuffer> frameBuffer;
QSSGRef<QSSGRenderTexture2D> texture;
dynamic::QSSGAllocateBufferFlags flags;
QSSGRenderCustomMaterialBuffer(const QByteArray &inName,
const QSSGRef<QSSGRenderFrameBuffer> &inFb,
const QSSGRef<QSSGRenderTexture2D> &inTexture,
dynamic::QSSGAllocateBufferFlags inFlags)
: name(inName), frameBuffer(inFb), texture(inTexture), flags(inFlags)
{
}
QSSGRenderCustomMaterialBuffer() = default;
};
struct QSSGStringMemoryBarrierFlagMap
{
const char *name;
QSSGRenderBufferBarrierValues value;
constexpr QSSGStringMemoryBarrierFlagMap(const char *nm, QSSGRenderBufferBarrierValues val) : name(nm), value(val)
{
}
};
// TODO:
//const QSSGStringMemoryBarrierFlagMap g_StringMemoryFlagMap[] = {
// QSSGStringMemoryBarrierFlagMap("vertex_attribute", QSSGRenderBufferBarrierValues::VertexAttribArray),
// QSSGStringMemoryBarrierFlagMap("element_array", QSSGRenderBufferBarrierValues::ElementArray),
// QSSGStringMemoryBarrierFlagMap("uniform_buffer", QSSGRenderBufferBarrierValues::UniformBuffer),
// QSSGStringMemoryBarrierFlagMap("texture_fetch", QSSGRenderBufferBarrierValues::TextureFetch),
// QSSGStringMemoryBarrierFlagMap("shader_image_access", QSSGRenderBufferBarrierValues::ShaderImageAccess),
// QSSGStringMemoryBarrierFlagMap("command_buffer", QSSGRenderBufferBarrierValues::CommandBuffer),
// QSSGStringMemoryBarrierFlagMap("pixel_buffer", QSSGRenderBufferBarrierValues::PixelBuffer),
// QSSGStringMemoryBarrierFlagMap("texture_update", QSSGRenderBufferBarrierValues::TextureUpdate),
// QSSGStringMemoryBarrierFlagMap("buffer_update", QSSGRenderBufferBarrierValues::BufferUpdate),
// QSSGStringMemoryBarrierFlagMap("frame_buffer", QSSGRenderBufferBarrierValues::Framebuffer),
// QSSGStringMemoryBarrierFlagMap("transform_feedback", QSSGRenderBufferBarrierValues::TransformFeedback),
// QSSGStringMemoryBarrierFlagMap("atomic_counter", QSSGRenderBufferBarrierValues::AtomicCounter),
// QSSGStringMemoryBarrierFlagMap("shader_storage", QSSGRenderBufferBarrierValues::ShaderStorage),
//};
struct QSSGStringBlendFuncMap
{
const char *name;
QSSGRenderSrcBlendFunc value;
constexpr QSSGStringBlendFuncMap(const char *nm, QSSGRenderSrcBlendFunc val) : name(nm), value(val) {}
};
// TODO:
//const QSSGStringBlendFuncMap g_BlendFuncMap[] = {
// QSSGStringBlendFuncMap("Unknown", QSSGRenderSrcBlendFunc::Unknown),
// QSSGStringBlendFuncMap("Zero", QSSGRenderSrcBlendFunc::Zero),
// QSSGStringBlendFuncMap("One", QSSGRenderSrcBlendFunc::One),
// QSSGStringBlendFuncMap("SrcColor", QSSGRenderSrcBlendFunc::SrcColor),
// QSSGStringBlendFuncMap("OneMinusSrcColor", QSSGRenderSrcBlendFunc::OneMinusSrcColor),
// QSSGStringBlendFuncMap("DstColor", QSSGRenderSrcBlendFunc::DstColor),
// QSSGStringBlendFuncMap("OneMinusDstColor", QSSGRenderSrcBlendFunc::OneMinusDstColor),
// QSSGStringBlendFuncMap("SrcAlpha", QSSGRenderSrcBlendFunc::SrcAlpha),
// QSSGStringBlendFuncMap("OneMinusSrcAlpha", QSSGRenderSrcBlendFunc::OneMinusSrcAlpha),
// QSSGStringBlendFuncMap("DstAlpha", QSSGRenderSrcBlendFunc::DstAlpha),
// QSSGStringBlendFuncMap("OneMinusDstAlpha", QSSGRenderSrcBlendFunc::OneMinusDstAlpha),
// QSSGStringBlendFuncMap("ConstantColor", QSSGRenderSrcBlendFunc::ConstantColor),
// QSSGStringBlendFuncMap("OneMinusConstantColor", QSSGRenderSrcBlendFunc::OneMinusConstantColor),
// QSSGStringBlendFuncMap("ConstantAlpha", QSSGRenderSrcBlendFunc::ConstantAlpha),
// QSSGStringBlendFuncMap("OneMinusConstantAlpha", QSSGRenderSrcBlendFunc::OneMinusConstantAlpha),
// QSSGStringBlendFuncMap("SrcAlphaSaturate", QSSGRenderSrcBlendFunc::SrcAlphaSaturate)
//};
QSSGMaterialSystem::QSSGMaterialSystem(QSSGRenderContextInterface *ct)
: context(ct)
{
}
QSSGMaterialSystem::~QSSGMaterialSystem()
{
while (allocatedBuffers.size()) { // replace_with_last
allocatedBuffers[0] = allocatedBuffers.back();
allocatedBuffers.pop_back();
}
}
void QSSGMaterialSystem::releaseBuffer(qint32 inIdx)
{
// Don't call this on MaterialSystem destroy.
// This causes issues for scene liftime buffers
// because the resource manager is destroyed before
const QSSGRef<QSSGResourceManager> &theManager(context->resourceManager());
QSSGRenderCustomMaterialBuffer &theEntry(allocatedBuffers[inIdx]);
theEntry.frameBuffer->attach(QSSGRenderFrameBufferAttachment::Color0, QSSGRenderTextureOrRenderBuffer());
theManager->release(theEntry.frameBuffer);
theManager->release(theEntry.texture);
{ // replace_with_last
allocatedBuffers[inIdx] = allocatedBuffers.back();
allocatedBuffers.pop_back();
}
}
qint32 QSSGMaterialSystem::findBuffer(const QByteArray &inName) const
{
for (qint32 idx = 0, end = allocatedBuffers.size(); idx < end; ++idx) {
if (allocatedBuffers.at(idx).name == inName)
return idx;
}
return allocatedBuffers.size();
}
bool QSSGMaterialSystem::textureNeedsMips(const QSSGRenderCustomMaterial::TextureProperty *inPropDec, QSSGRenderTexture2D *inTexture)
{
if (inPropDec && inTexture) {
return bool((inPropDec->minFilterType == QSSGRenderTextureMinifyingOp::LinearMipmapLinear)
&& (inTexture->numMipmaps() == 0));
}
return false;
}
void QSSGMaterialSystem::setTexture(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QByteArray &inPropName,
const QSSGRef<QSSGRenderTexture2D> &inTexture,
const QSSGRenderCustomMaterial::TextureProperty *inPropDec,
bool needMips)
{
QSSGRef<QSSGCustomMaterialTextureData> theTextureEntry;
auto it = textureEntries.cbegin();
const auto end = textureEntries.cend();
for (; it != end && theTextureEntry == nullptr; ++it) {
if (it->first == inPropName && it->second->shader == inShader
&& it->second->texture == inTexture) {
theTextureEntry = it->second;
break;
}
}
if (theTextureEntry == nullptr) {
QSSGRef<QSSGCustomMaterialTextureData> theNewEntry(new QSSGCustomMaterialTextureData(
QSSGCustomMaterialTextureData::createTextureEntry(inShader, inTexture, inPropName, needMips)));
textureEntries.push_back(QPair<QByteArray, QSSGRef<QSSGCustomMaterialTextureData>>(inPropName, theNewEntry));
theTextureEntry = theNewEntry;
}
// TODO: Already set?
theTextureEntry->set(inPropDec);
}
// TODO: Use an enum for the shader type?
// Remove and call the setShaderData func directly?
void QSSGMaterialSystem::setMaterialClassShader(const QByteArray &inName, const QByteArray &inShaderType, const QByteArray &inShaderVersion,
const QByteArray &inShaderData, bool inHasGeomShader, bool inIsComputeShader)
{
context->dynamicObjectSystem()->setShaderData(inName, inShaderData, inShaderType, inShaderVersion, inHasGeomShader, inIsComputeShader);
}
QSSGRef<QSSGRenderShaderProgram> QSSGMaterialSystem::getShader(QSSGCustomMaterialRenderContext &inRenderContext,
const QSSGRenderCustomMaterial &inMaterial,
const dynamic::QSSGBindShader &inCommand,
const ShaderFeatureSetList &inFeatureSet,
const dynamic::QSSGDynamicShaderProgramFlags &inFlags)
{
Q_UNUSED(inFlags);
const QSSGRef<QSSGMaterialShaderGeneratorInterface> &theMaterialGenerator(context->customMaterialShaderGenerator());
// generate key
// QString theKey = getShaderCacheKey(theShaderKeyBuffer, inCommand.m_shaderPath,
// inCommand.m_shaderDefine, inFlags);
// ### TODO: Enable caching?
QSSGCustomMaterialVertexPipeline thePipeline(context, inRenderContext.model.tessellationMode);
const QSSGRef<QSSGRenderShaderProgram> &theProgram = theMaterialGenerator->generateShader(inMaterial,
inRenderContext.materialKey,
thePipeline,
inFeatureSet,
inRenderContext.lights,
inRenderContext.firstImage,
(inMaterial.m_hasTransparency || inMaterial.m_hasRefraction),
"custom material pipeline-- ",
inCommand.m_shaderPath);
return theProgram;
}
QSSGMaterialOrComputeShader QSSGMaterialSystem::bindShader(QSSGCustomMaterialRenderContext &inRenderContext, const QSSGRenderCustomMaterial &inMaterial, const dynamic::QSSGBindShader &inCommand, const ShaderFeatureSetList &inFeatureSet)
{
QSSGRef<QSSGRenderShaderProgram> theProgram;
dynamic::QSSGDynamicShaderProgramFlags theFlags(inRenderContext.model.tessellationMode, inRenderContext.subset.wireframeMode);
if (inRenderContext.model.tessellationMode != TessellationModeValues::NoTessellation)
theFlags |= ShaderCacheProgramFlagValues::TessellationEnabled;
if (inRenderContext.subset.wireframeMode)
theFlags |= ShaderCacheProgramFlagValues::GeometryShaderEnabled;
QSSGShaderMapKey skey = QSSGShaderMapKey(TStrStrPair(inCommand.m_shaderPath, inCommand.m_shaderDefine),
inFeatureSet,
theFlags.tessMode,
theFlags.wireframeMode,
inRenderContext.materialKey);
auto theInsertResult = shaderMap.find(skey);
// QPair<TShaderMap::iterator, bool> theInsertResult(m_ShaderMap.insert(skey, QSSGRef<SCustomMaterialShader>(nullptr)));
if (theInsertResult == shaderMap.end()) {
theProgram = getShader(inRenderContext, inMaterial, inCommand, inFeatureSet, theFlags);
if (theProgram) {
theInsertResult = shaderMap.insert(skey,
QSSGRef<QSSGRenderCustomMaterialShader>(
new QSSGRenderCustomMaterialShader(theProgram, theFlags)));
}
} else if (theInsertResult.value()) {
theProgram = theInsertResult.value()->shader;
}
if (theProgram) {
if (theProgram->programType() == QSSGRenderShaderProgram::ProgramType::Graphics) {
if (theInsertResult.value()) {
const QSSGRef<QSSGRenderContext> &theContext(context->renderContext());
theContext->setActiveShader(theInsertResult.value()->shader);
}
return QSSGMaterialOrComputeShader(theInsertResult.value());
} else {
const QSSGRef<QSSGRenderContext> &theContext(context->renderContext());
theContext->setActiveShader(theProgram);
return QSSGMaterialOrComputeShader(theProgram);
}
}
return QSSGMaterialOrComputeShader();
}
void QSSGMaterialSystem::doApplyInstanceValue(QSSGRenderCustomMaterial &inMaterial,
const QByteArray &inPropertyName,
const QVariant &propertyValue,
QSSGRenderShaderDataType inPropertyType,
const QSSGRef<QSSGRenderShaderProgram> &inShader)
{
Q_UNUSED(inMaterial)
const QSSGRef<QSSGRenderShaderConstantBase> &theConstant = inShader->shaderConstant(inPropertyName);
if (Q_LIKELY(theConstant)) {
if (theConstant->isCompatibleType(inPropertyType)) {
if (inPropertyType == QSSGRenderShaderDataType::Texture2D) {
// StaticAssert<sizeof(QString) == sizeof(QSSGRenderTexture2DPtr)>::valid_expression();
QSSGRenderCustomMaterial::TextureProperty *textureProperty = reinterpret_cast<QSSGRenderCustomMaterial::TextureProperty *>(propertyValue.value<void *>());
QSSGRenderImage *image = textureProperty->texImage;
if (image) {
const QString &imageSource = image->m_imagePath;
const QSSGRef<QSSGBufferManager> &theBufferManager(context->bufferManager());
QSSGRef<QSSGRenderTexture2D> theTexture;
if (!imageSource.isEmpty()) {
QSSGRenderImageTextureData theTextureData = theBufferManager->loadRenderImage(imageSource);
if (theTextureData.m_texture) {
theTexture = theTextureData.m_texture;
setTexture(inShader,
inPropertyName,
theTexture,
textureProperty, // TODO: Should not be null!
textureNeedsMips(textureProperty /* TODO: Should not be null! */, theTexture.data()));
}
}
}
} else {
// TODO:
switch (inPropertyType) {
case QSSGRenderShaderDataType::Integer:
inShader->setPropertyValue(theConstant.data(), propertyValue.toInt());
break;
case QSSGRenderShaderDataType::IntegerVec2:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<qint32_2>());
break;
case QSSGRenderShaderDataType::IntegerVec3:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<qint32_3>());
break;
case QSSGRenderShaderDataType::IntegerVec4:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<qint32_4>());
break;
case QSSGRenderShaderDataType::Boolean:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<bool>());
break;
case QSSGRenderShaderDataType::BooleanVec2:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<bool_2>());
break;
case QSSGRenderShaderDataType::BooleanVec3:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<bool_3>());
break;
case QSSGRenderShaderDataType::BooleanVec4:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<bool_4>());
break;
case QSSGRenderShaderDataType::Float:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<float>());
break;
case QSSGRenderShaderDataType::Vec2:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<QVector2D>());
break;
case QSSGRenderShaderDataType::Vec3:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<QVector3D>());
break;
case QSSGRenderShaderDataType::Vec4:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<QVector4D>());
break;
case QSSGRenderShaderDataType::Rgba:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<QColor>());
break;
case QSSGRenderShaderDataType::UnsignedInteger:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<quint32>());
break;
case QSSGRenderShaderDataType::UnsignedIntegerVec2:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<quint32_2>());
break;
case QSSGRenderShaderDataType::UnsignedIntegerVec3:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<quint32_3>());
break;
case QSSGRenderShaderDataType::UnsignedIntegerVec4:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<quint32_4>());
break;
case QSSGRenderShaderDataType::Matrix3x3:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<QMatrix3x3>());
break;
case QSSGRenderShaderDataType::Matrix4x4:
inShader->setPropertyValue(theConstant.data(), propertyValue.value<QMatrix4x4>());
break;
case QSSGRenderShaderDataType::Texture2D:
inShader->setPropertyValue(theConstant.data(), *(reinterpret_cast<QSSGRenderTexture2D **>(propertyValue.value<void *>())));
break;
case QSSGRenderShaderDataType::Texture2DHandle:
inShader->setPropertyValue(theConstant.data(),
*(reinterpret_cast<QSSGRenderTexture2D ***>(propertyValue.value<void *>())));
break;
case QSSGRenderShaderDataType::TextureCube:
inShader->setPropertyValue(theConstant.data(), *(reinterpret_cast<QSSGRenderTextureCube **>(propertyValue.value<void *>())));
break;
case QSSGRenderShaderDataType::TextureCubeHandle:
inShader->setPropertyValue(theConstant.data(),
*(reinterpret_cast<QSSGRenderTextureCube ***>(propertyValue.value<void *>())));
break;
case QSSGRenderShaderDataType::Image2D:
inShader->setPropertyValue(theConstant.data(), *(reinterpret_cast<QSSGRenderImage2D **>(propertyValue.value<void *>())));
break;
case QSSGRenderShaderDataType::DataBuffer:
inShader->setPropertyValue(theConstant.data(), *(reinterpret_cast<QSSGRenderDataBuffer **>(propertyValue.value<void *>())));
break;
default:
Q_UNREACHABLE();
}
}
} else {
qCCritical(INVALID_OPERATION,
"CustomMaterial ApplyInstanceValue command datatype and "
"shader datatypes differ for property %s",
inPropertyName.constData());
Q_ASSERT(false);
}
}
}
void QSSGMaterialSystem::applyInstanceValue(QSSGRenderCustomMaterial &inMaterial,
const QSSGRef<QSSGRenderShaderProgram> &inShader,
const dynamic::QSSGApplyInstanceValue &inCommand)
{
// sanity check
if (!inCommand.m_propertyName.isNull()) {
const auto &properties = inMaterial.properties;
const auto foundIt = std::find_if(properties.cbegin(), properties.cend(), [&inCommand](const QSSGRenderCustomMaterial::Property &prop) { return (prop.name == inCommand.m_propertyName); });
if (foundIt != properties.cend())
doApplyInstanceValue(inMaterial, foundIt->name, foundIt->value, foundIt->shaderDataType, inShader);
} else {
const auto &properties = inMaterial.properties;
for (const auto &prop : properties)
doApplyInstanceValue(inMaterial, prop.name, prop.value, prop.shaderDataType, inShader);
const auto textProps = inMaterial.textureProperties;
for (const auto &prop : textProps)
doApplyInstanceValue(inMaterial, prop.name, QVariant::fromValue((void *)&prop), prop.shaderDataType, inShader);
}
}
void QSSGMaterialSystem::applyBlending(const dynamic::QSSGApplyBlending &inCommand)
{
const QSSGRef<QSSGRenderContext> &theContext(context->renderContext());
theContext->setBlendingEnabled(true);
QSSGRenderBlendFunctionArgument blendFunc = QSSGRenderBlendFunctionArgument(inCommand.m_srcBlendFunc,
inCommand.m_dstBlendFunc,
inCommand.m_srcBlendFunc,
inCommand.m_dstBlendFunc);
QSSGRenderBlendEquationArgument blendEqu(QSSGRenderBlendEquation::Add, QSSGRenderBlendEquation::Add);
theContext->setBlendFunction(blendFunc);
theContext->setBlendEquation(blendEqu);
}
void QSSGMaterialSystem::applyRenderStateValue(const dynamic::QSSGApplyRenderState &inCommand)
{
const QSSGRef<QSSGRenderContext> &theContext(context->renderContext());
switch (inCommand.m_renderState) {
case QSSGRenderState::Blend:
theContext->setBlendingEnabled(inCommand.m_enabled);
break;
case QSSGRenderState::DepthTest:
theContext->setDepthTestEnabled(inCommand.m_enabled);
break;
case QSSGRenderState::StencilTest:
theContext->setStencilTestEnabled(inCommand.m_enabled);
break;
case QSSGRenderState::ScissorTest:
theContext->setScissorTestEnabled(inCommand.m_enabled);
break;
case QSSGRenderState::DepthWrite:
theContext->setDepthWriteEnabled(inCommand.m_enabled);
break;
case QSSGRenderState::Multisample:
theContext->setMultisampleEnabled(inCommand.m_enabled);
break;
case QSSGRenderState::CullFace:
// CullFace is configured by Model.cullingMode and not by CustomMaterial
case QSSGRenderState::Unknown:
Q_ASSERT(false);
break;
}
}
QSSGRef<QSSGRenderTexture2D> QSSGMaterialSystem::applyBufferValue(const QSSGRenderCustomMaterial &inMaterial,
const QSSGRef<QSSGRenderShaderProgram> &inShader,
const dynamic::QSSGApplyBufferValue &inCommand,
const QSSGRef<QSSGRenderTexture2D> &inSourceTexture)
{
QSSGRef<QSSGRenderTexture2D> theTexture = nullptr;
if (!inCommand.m_bufferName.isNull()) {
qint32 bufferIdx = findBuffer(inCommand.m_bufferName);
if (bufferIdx < allocatedBuffers.size()) {
const QSSGRenderCustomMaterialBuffer &theEntry = allocatedBuffers.at(bufferIdx);
theTexture = theEntry.texture;
} else {
// we must have allocated the read target before
qCCritical(INTERNAL_ERROR, "CustomMaterial: ApplyBufferValue: Failed to setup read target");
Q_ASSERT(false);
}
} else {
theTexture = inSourceTexture;
}
if (!inCommand.m_paramName.isNull()) {
QSSGRef<QSSGRenderShaderConstantBase> theConstant = inShader->shaderConstant(inCommand.m_paramName);
if (theConstant) {
if (theConstant->getShaderConstantType() != QSSGRenderShaderDataType::Texture2D) {
qCCritical(INVALID_OPERATION,
"CustomMaterial %s: Binding buffer to parameter %s that is not a texture",
inMaterial.className,
inCommand.m_paramName.constData());
Q_ASSERT(false);
} else {
setTexture(inShader, inCommand.m_paramName, theTexture);
}
}
}
return (theTexture != nullptr) ? theTexture : nullptr;
}
void QSSGMaterialSystem::allocateBuffer(const dynamic::QSSGAllocateBuffer &inCommand, const QSSGRef<QSSGRenderFrameBuffer> &inTarget)
{
QSSGTextureDetails theSourceTextureDetails;
QSSGRef<QSSGRenderTexture2D> theTexture;
// get color attachment we always assume at location 0
if (inTarget) {
QSSGRenderTextureOrRenderBuffer theSourceTexture = inTarget->attachment(QSSGRenderFrameBufferAttachment::Color0);
// we need a texture
if (theSourceTexture.hasTexture2D()) {
theSourceTextureDetails = theSourceTexture.texture2D()->textureDetails();
} else {
qCCritical(INVALID_OPERATION, "CustomMaterial %s: Invalid source texture", inCommand.m_name.constData());
Q_ASSERT(false);
return;
}
} else {
QSSGRef<QSSGRenderContext> theContext = context->renderContext();
// if we allocate a buffer based on the default target use viewport to get the dimension
QRect theViewport(theContext->viewport());
theSourceTextureDetails.height = theViewport.height();
theSourceTextureDetails.width = theViewport.width();
}
const qint32 theWidth = qint32(theSourceTextureDetails.width * inCommand.m_sizeMultiplier);
const qint32 theHeight = qint32(theSourceTextureDetails.height * inCommand.m_sizeMultiplier);
QSSGRenderTextureFormat theFormat = inCommand.m_format;
if (theFormat == QSSGRenderTextureFormat::Unknown
&& theSourceTextureDetails.format != QSSGRenderTextureFormat::Unknown) {
theFormat = theSourceTextureDetails.format;
} else {
theFormat = QSSGRenderTextureFormat::RGBA8;
}
const QSSGRef<QSSGResourceManager> &theResourceManager(context->resourceManager());
// size intentionally requiried every loop;
qint32 bufferIdx = findBuffer(inCommand.m_name);
if (bufferIdx < allocatedBuffers.size()) {
const QSSGRenderCustomMaterialBuffer &theEntry = allocatedBuffers.at(bufferIdx);
QSSGTextureDetails theDetails = theEntry.texture->textureDetails();
if (theDetails.width == theWidth && theDetails.height == theHeight && theDetails.format == theFormat) {
theTexture = theEntry.texture;
} else {
releaseBuffer(bufferIdx);
}
}
if (theTexture == nullptr) {
QSSGRef<QSSGRenderFrameBuffer> theFB(theResourceManager->allocateFrameBuffer());
QSSGRef<QSSGRenderTexture2D> theTexture(theResourceManager->allocateTexture2D(theWidth, theHeight, theFormat));
theTexture->setMagFilter(inCommand.m_filterOp);
theTexture->setMinFilter(static_cast<QSSGRenderTextureMinifyingOp>(inCommand.m_filterOp));
theTexture->setTextureWrapS(inCommand.m_texCoordOp);
theTexture->setTextureWrapT(inCommand.m_texCoordOp);
theFB->attach(QSSGRenderFrameBufferAttachment::Color0, theTexture);
allocatedBuffers.push_back(QSSGRenderCustomMaterialBuffer(inCommand.m_name, theFB, theTexture, inCommand.m_bufferFlags));
}
}
QSSGRef<QSSGRenderFrameBuffer> QSSGMaterialSystem::bindBuffer(const QSSGRenderCustomMaterial &inMaterial, const dynamic::QSSGBindBuffer &inCommand, bool &outClearTarget, QVector2D &outDestSize)
{
QSSGRef<QSSGRenderFrameBuffer> theBuffer;
QSSGRef<QSSGRenderTexture2D> theTexture;
// search for the buffer
qint32 bufferIdx = findBuffer(inCommand.m_bufferName);
if (bufferIdx < allocatedBuffers.size()) {
theBuffer = allocatedBuffers[bufferIdx].frameBuffer;
theTexture = allocatedBuffers[bufferIdx].texture;
}
if (theBuffer == nullptr) {
qCCritical(INVALID_OPERATION,
"Material %s: Failed to find buffer %s for bind",
inMaterial.className,
inCommand.m_bufferName.constData());
Q_ASSERT(false);
return nullptr;
}
if (theTexture) {
QSSGTextureDetails theDetails(theTexture->textureDetails());
context->renderContext()->setViewport(QRect(0, 0, theDetails.width, theDetails.height));
outDestSize = QVector2D(float(theDetails.width), float(theDetails.height));
outClearTarget = inCommand.m_needsClear;
}
return theBuffer;
}
void QSSGMaterialSystem::computeScreenCoverage(QSSGCustomMaterialRenderContext &inRenderContext, qint32 *xMin, qint32 *yMin, qint32 *xMax, qint32 *yMax)
{
const QSSGRef<QSSGRenderContext> &theContext(context->renderContext());
QSSGBounds2BoxPoints outPoints;
const float MaxFloat = std::numeric_limits<float>::max();
QVector4D projMin(MaxFloat, MaxFloat, MaxFloat, MaxFloat);
QVector4D projMax(-MaxFloat, -MaxFloat, -MaxFloat, -MaxFloat);
// get points
inRenderContext.subset.bounds.expand(outPoints);
for (quint32 idx = 0; idx < 8; ++idx) {
QVector4D homPoint(outPoints[idx], 1.0);
QVector4D projPoint = mat44::transform(inRenderContext.modelViewProjection, homPoint);
projPoint /= projPoint.w();
if (projMin.x() > projPoint.x())
projMin.setX(projPoint.x());
if (projMin.y() > projPoint.y())
projMin.setY(projPoint.y());
if (projMin.z() > projPoint.z())
projMin.setZ(projPoint.z());
if (projMax.x() < projPoint.x())
projMax.setX(projPoint.x());
if (projMax.y() < projPoint.y())
projMax.setY(projPoint.y());
if (projMax.z() < projPoint.z())
projMax.setZ(projPoint.z());
}
QRect theViewport(theContext->viewport());
qint32 x1 = qint32(projMax.x() * (theViewport.width() / 2) + (theViewport.x() + (theViewport.width() / 2)));
qint32 y1 = qint32(projMax.y() * (theViewport.height() / 2) + (theViewport.y() + (theViewport.height() / 2)));
qint32 x2 = qint32(projMin.x() * (theViewport.width() / 2) + (theViewport.x() + (theViewport.width() / 2)));
qint32 y2 = qint32(projMin.y() * (theViewport.height() / 2) + (theViewport.y() + (theViewport.height() / 2)));
if (x1 > x2) {
*xMin = x2;
*xMax = x1;
} else {
*xMin = x1;
*xMax = x2;
}
if (y1 > y2) {
*yMin = y2;
*yMax = y1;
} else {
*yMin = y1;
*yMax = y2;
}
}
void QSSGMaterialSystem::blitFramebuffer(QSSGCustomMaterialRenderContext &inRenderContext, const dynamic::QSSGApplyBlitFramebuffer &inCommand, const QSSGRef<QSSGRenderFrameBuffer> &inTarget)
{
const QSSGRef<QSSGRenderContext> &theContext(context->renderContext());
// we change the read/render targets here
QSSGRenderContextScopedProperty<const QSSGRef<QSSGRenderFrameBuffer> &> __framebuffer(*theContext,
&QSSGRenderContext::renderTarget,
&QSSGRenderContext::setRenderTarget);
// we may alter scissor
QSSGRenderContextScopedProperty<bool> theScissorEnabled(*theContext,
&QSSGRenderContext::isScissorTestEnabled,
&QSSGRenderContext::setScissorTestEnabled);
if (!inCommand.m_destBufferName.isNull()) {
qint32 bufferIdx = findBuffer(inCommand.m_destBufferName);
if (bufferIdx < allocatedBuffers.size()) {
QSSGRenderCustomMaterialBuffer &theEntry(allocatedBuffers[bufferIdx]);
theContext->setRenderTarget(theEntry.frameBuffer);
} else {
// we must have allocated the read target before
qCCritical(INTERNAL_ERROR, "CustomMaterial: BlitFramebuffer: Failed to setup render target");
Q_ASSERT(false);
}
} else {
// our dest is the default render target
theContext->setRenderTarget(inTarget);
}
if (!inCommand.m_sourceBufferName.isNull()) {
qint32 bufferIdx = findBuffer(inCommand.m_sourceBufferName);
if (bufferIdx < allocatedBuffers.size()) {
QSSGRenderCustomMaterialBuffer &theEntry(allocatedBuffers[bufferIdx]);
theContext->setReadTarget(theEntry.frameBuffer);
theContext->setReadBuffer(QSSGReadFace::Color0);
} else {
// we must have allocated the read target before
qCCritical(INTERNAL_ERROR, "CustomMaterial: BlitFramebuffer: Failed to setup read target");
Q_ASSERT(false);
}
} else {
// our source is the default read target
// depending on what we render we assume color0 or back
theContext->setReadTarget(inTarget);
QSSGReadFace value = (inTarget) ? QSSGReadFace::Color0 : QSSGReadFace::Back;
theContext->setReadBuffer(value);
}
QRect theViewport(theContext->viewport());
theContext->setScissorTestEnabled(false);
if (!useFastBlits) {
// only copy sreen amount of pixels
qint32 xMin, yMin, xMax, yMax;
computeScreenCoverage(inRenderContext, &xMin, &yMin, &xMax, &yMax);
// same dimension
theContext->blitFramebuffer(xMin, yMin, xMax, yMax, xMin, yMin, xMax, yMax, QSSGRenderClearValues::Color, QSSGRenderTextureMagnifyingOp::Nearest);
} else {
// same dimension
theContext->blitFramebuffer(theViewport.x(),
theViewport.y(),
theViewport.x() + theViewport.width(),
theViewport.y() + theViewport.height(),
theViewport.x(),
theViewport.y(),
theViewport.x() + theViewport.width(),
theViewport.y() + theViewport.height(),
QSSGRenderClearValues::Color,
QSSGRenderTextureMagnifyingOp::Nearest);
}
}
QSSGLayerGlobalRenderProperties QSSGMaterialSystem::getLayerGlobalRenderProperties(QSSGCustomMaterialRenderContext &inRenderContext)
{
const QSSGRenderLayer &theLayer = inRenderContext.layer;
const QSSGLayerRenderData &theData = inRenderContext.layerData;
QVector<QVector3D> tempDirection;
return QSSGLayerGlobalRenderProperties{ theLayer,
const_cast<QSSGRenderCamera &>(inRenderContext.camera),
theData.cameraDirection,
const_cast<QVector<QSSGRenderLight *> &>(inRenderContext.lights),
tempDirection,
theData.shadowMapManager,
inRenderContext.depthTexture,
inRenderContext.aoTexture,
theLayer.lightProbe,
theLayer.lightProbe2,
theLayer.probeHorizon,
theLayer.probeBright,
theLayer.probe2Window,
theLayer.probe2Pos,
theLayer.probe2Fade,
theLayer.probeFov };
}
void QSSGMaterialSystem::renderPass(QSSGCustomMaterialRenderContext &inRenderContext, const QSSGRef<QSSGRenderCustomMaterialShader> &inShader, const QSSGRef<QSSGRenderTexture2D> &, const QSSGRef<QSSGRenderFrameBuffer> &inFrameBuffer, bool inRenderTargetNeedsClear, const QSSGRef<QSSGRenderInputAssembler> &inAssembler, quint32 inCount, quint32 inOffset)
{
const QSSGRef<QSSGRenderContext> &theContext(context->renderContext());
theContext->setRenderTarget(inFrameBuffer);
QVector4D clearColor(0.0, 0.0, 0.0, 0.0);
QSSGRenderContextScopedProperty<QVector4D> __clearColor(*theContext,
&QSSGRenderContext::clearColor,
&QSSGRenderContext::setClearColor,
clearColor);
if (inRenderTargetNeedsClear) {
theContext->clear(QSSGRenderClearValues::Color);
}
const QSSGRef<QSSGMaterialShaderGeneratorInterface> &theMaterialGenerator(context->customMaterialShaderGenerator());
theMaterialGenerator->setMaterialProperties(inShader->shader,
inRenderContext.material,
QVector2D(1.0, 1.0),
inRenderContext.modelViewProjection,
inRenderContext.normalMatrix,
inRenderContext.modelMatrix,
inRenderContext.firstImage,
inRenderContext.opacity,
getLayerGlobalRenderProperties(inRenderContext));
// I think the prim type should always be fetched from the
// current mesh subset setup because there you get the actual draw mode
// for this frame
QSSGRenderDrawMode theDrawMode = inAssembler->drawMode();
// tesselation
if (inRenderContext.subset.primitiveType == QSSGRenderDrawMode::Patches) {
QVector2D camProps(inRenderContext.camera.clipNear, inRenderContext.camera.clipFar);
theDrawMode = inRenderContext.subset.primitiveType;
inShader->tessellation.m_edgeTessLevel.set(inRenderContext.subset.edgeTessFactor);
inShader->tessellation.m_insideTessLevel.set(inRenderContext.subset.innerTessFactor);
// the blend value is hardcoded
inShader->tessellation.m_phongBlend.set(0.75);
// this should finally be based on some user input
inShader->tessellation.m_distanceRange.set(camProps);
// enable culling
inShader->tessellation.m_disableCulling.set(0.0);
}
if (inRenderContext.subset.wireframeMode) {
QRect theViewport(theContext->viewport());
QMatrix4x4 vpMatrix = { (float)theViewport.width() / 2.0f,
0.0,
0.0,
0.0,
0.0,
(float)theViewport.height() / 2.0f,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
(float)theViewport.width() / 2.0f + (float)theViewport.x(),
(float)theViewport.height() / 2.0f + (float)theViewport.y(),
0.0,
1.0 };
inShader->viewportMatrix.set(vpMatrix);
}
theContext->setInputAssembler(inAssembler);
theContext->solveCullingOptions(inRenderContext.material.cullingMode);
quint32 count = inCount;
quint32 offset = inOffset;
theContext->draw(theDrawMode, count, offset);
}
void QSSGMaterialSystem::doRenderCustomMaterial(QSSGCustomMaterialRenderContext &inRenderContext,
const QSSGRenderCustomMaterial &inMaterial,
const ShaderFeatureSetList &inFeatureSet)
{
const QSSGRef<QSSGRenderContext> &theContext = context->renderContext();
QSSGRef<QSSGRenderCustomMaterialShader> theCurrentShader(nullptr);
QRect theOriginalViewport(theContext->viewport());
QSSGRef<QSSGRenderTexture2D> theCurrentSourceTexture;
// for refrative materials we come from the transparent render path
// but we do not want to do blending
bool wasBlendingEnabled = theContext->isBlendingEnabled();
if (inMaterial.m_hasRefraction)
theContext->setBlendingEnabled(false);
QSSGRenderContextScopedProperty<const QSSGRef<QSSGRenderFrameBuffer> &> __framebuffer(*theContext,
&QSSGRenderContext::renderTarget,
&QSSGRenderContext::setRenderTarget);
const auto &originalTarget = __framebuffer.m_initialValue;
QSSGRef<QSSGRenderFrameBuffer> theCurrentRenderTarget(originalTarget);
QSSGRenderContextScopedProperty<QRect> __viewport(*theContext, &QSSGRenderContext::viewport, &QSSGRenderContext::setViewport);
QVector2D theDestSize;
bool theRenderTargetNeedsClear = false;
const auto &commands = inMaterial.commands;
for (const auto &command : commands) {
switch (command->m_type) {
case dynamic::CommandType::AllocateBuffer:
allocateBuffer(static_cast<const dynamic::QSSGAllocateBuffer &>(*command), originalTarget);
break;
case dynamic::CommandType::BindBuffer:
theCurrentRenderTarget = bindBuffer(inMaterial,
static_cast<const dynamic::QSSGBindBuffer &>(*command),
theRenderTargetNeedsClear,
theDestSize);
break;
case dynamic::CommandType::BindTarget:
// Restore the previous render target and info.
theCurrentRenderTarget = originalTarget;
theContext->setViewport(theOriginalViewport);
break;
case dynamic::CommandType::BindShader: {
theCurrentShader = nullptr;
QSSGMaterialOrComputeShader theBindResult = bindShader(inRenderContext,
inMaterial,
static_cast<const dynamic::QSSGBindShader &>(*command),
inFeatureSet);
if (theBindResult.isMaterialShader())
theCurrentShader = theBindResult.materialShader();
} break;
case dynamic::CommandType::ApplyInstanceValue:
// we apply the property update explicitly at the render pass
break;
case dynamic::CommandType::Render:
if (theCurrentShader) {
renderPass(inRenderContext,
theCurrentShader,
theCurrentSourceTexture,
theCurrentRenderTarget,
theRenderTargetNeedsClear,
inRenderContext.subset.inputAssembler,
inRenderContext.subset.count,
inRenderContext.subset.offset);
}
// reset
theRenderTargetNeedsClear = false;
break;
case dynamic::CommandType::ApplyBlending:
applyBlending(static_cast<const dynamic::QSSGApplyBlending &>(*command));
break;
case dynamic::CommandType::ApplyBufferValue:
if (theCurrentShader)
applyBufferValue(inMaterial,
theCurrentShader->shader,
static_cast<const dynamic::QSSGApplyBufferValue &>(*command),
theCurrentSourceTexture);
break;
case dynamic::CommandType::ApplyBlitFramebuffer:
blitFramebuffer(inRenderContext, static_cast<const dynamic::QSSGApplyBlitFramebuffer &>(*command), originalTarget);
break;
case dynamic::CommandType::ApplyRenderState:
// TODO: The applyRenderStateValue() function is a very naive implementation
applyRenderStateValue(static_cast<const dynamic::QSSGApplyRenderState &>(*command));
break;
default:
Q_ASSERT(false);
break;
}
}
if (inMaterial.m_hasRefraction)
theContext->setBlendingEnabled(wasBlendingEnabled);
// Release any per-frame buffers
for (qint32 idx = 0; idx < allocatedBuffers.size(); ++idx) {
if (allocatedBuffers[idx].flags.isSceneLifetime() == false) {
releaseBuffer(idx);
--idx;
}
}
}
QByteArray QSSGMaterialSystem::getShaderName(const QSSGRenderCustomMaterial &inMaterial)
{
auto it = inMaterial.commands.cbegin();
const auto end = inMaterial.commands.cend();
for (; it != end; ++it) {
if ((*it)->m_type == dynamic::CommandType::BindShader) {
dynamic::QSSGBindShader *bindCommand = static_cast<dynamic::QSSGBindShader *>(*it);
return bindCommand->m_shaderPath;
}
}
Q_UNREACHABLE();
return QByteArray();
}
void QSSGMaterialSystem::applyShaderPropertyValues(const QSSGRenderCustomMaterial &inMaterial, const QSSGRef<QSSGRenderShaderProgram> &inProgram)
{
dynamic::QSSGApplyInstanceValue applier;
applyInstanceValue(const_cast<QSSGRenderCustomMaterial &>(inMaterial), inProgram, applier);
}
void QSSGMaterialSystem::prepareDisplacementForRender(QSSGRenderCustomMaterial &inMaterial)
{
// TODO: Shouldn't be needed anymore, as there's only one place where the values are updated
if (inMaterial.m_displacementMap == nullptr)
return;
// our displacement mappin in MDL has fixed naming
const auto &props = inMaterial.properties;
for (const auto &prop : props) {
if (prop.shaderDataType == QSSGRenderShaderDataType::Float && prop.name == QByteArrayLiteral("displaceAmount")) {
bool ok = false;
const float theValue = prop.value.toFloat(&ok); //*reinterpret_cast<const float *>(inMaterial.getDataSectionBegin() + thePropDefs[idx].offset);
if (ok)
inMaterial.m_displaceAmount = theValue;
} else if (prop.shaderDataType == QSSGRenderShaderDataType::Vec3 && prop.name == QByteArrayLiteral("displace_tiling")) {
const QVector3D theValue = prop.value.value<QVector3D>(); // = *reinterpret_cast<const QVector3D *>(inMaterial.getDataSectionBegin() + thePropDefs[idx].offset);
if (theValue.x() != inMaterial.m_displacementMap->m_scale.x()
|| theValue.y() != inMaterial.m_displacementMap->m_scale.y()) {
inMaterial.m_displacementMap->m_scale = QVector2D(theValue.x(), theValue.y());
inMaterial.m_displacementMap->m_flags.setFlag(QSSGRenderImage::Flag::TransformDirty);
}
}
}
}
void QSSGMaterialSystem::prepareMaterialForRender(QSSGRenderCustomMaterial &inMaterial)
{
if (inMaterial.m_displacementMap) // inClass->m_hasDisplacement
prepareDisplacementForRender(inMaterial);
}
// Returns true if the material is dirty and thus will produce a different render result
// than previously. This effects things like progressive AA.
// TODO - return more information, specifically about transparency (object is transparent,
// object is completely transparent
bool QSSGMaterialSystem::prepareForRender(const QSSGRenderModel &, const QSSGRenderSubset &, QSSGRenderCustomMaterial &inMaterial)
{
prepareMaterialForRender(inMaterial);
const bool wasDirty = inMaterial.isDirty(); // TODO: Always dirty flag?
return wasDirty;
}
// TODO - handle UIC specific features such as vertex offsets for prog-aa and opacity.
void QSSGMaterialSystem::renderSubset(QSSGCustomMaterialRenderContext &inRenderContext, const ShaderFeatureSetList &inFeatureSet)
{
// Ensure that our overall render context comes back no matter what the client does.
QSSGRenderContextScopedProperty<QSSGRenderBlendFunctionArgument> __blendFunction(*context->renderContext(),
&QSSGRenderContext::blendFunction,
&QSSGRenderContext::setBlendFunction,
QSSGRenderBlendFunctionArgument());
QSSGRenderContextScopedProperty<QSSGRenderBlendEquationArgument> __blendEquation(*context->renderContext(),
&QSSGRenderContext::blendEquation,
&QSSGRenderContext::setBlendEquation,
QSSGRenderBlendEquationArgument());
QSSGRenderContextScopedProperty<bool> theBlendEnabled(*context->renderContext(),
&QSSGRenderContext::isBlendingEnabled,
&QSSGRenderContext::setBlendingEnabled);
doRenderCustomMaterial(inRenderContext, inRenderContext.material, inFeatureSet);
}
bool QSSGMaterialSystem::renderDepthPrepass(const QMatrix4x4 &inMVP, const QSSGRenderCustomMaterial &inMaterial, const QSSGRenderSubset &inSubset)
{
const auto &commands = inMaterial.commands;
auto it = commands.cbegin();
const auto end = commands.cend();
TShaderAndFlags thePrepassShader;
for (; it != end && thePrepassShader.first == nullptr; ++it) {
if ((*it)->m_type == dynamic::CommandType::BindShader) {
const dynamic::QSSGBindShader &theBindCommand = static_cast<const dynamic::QSSGBindShader &>(*(*it));
thePrepassShader = context->dynamicObjectSystem()->getDepthPrepassShader(theBindCommand.m_shaderPath,
QByteArray(),
ShaderFeatureSetList());
}
}
if (thePrepassShader.first == nullptr)
return false;
const QSSGRef<QSSGRenderContext> &theContext = context->renderContext();
const QSSGRef<QSSGRenderShaderProgram> &theProgram = thePrepassShader.first;
theContext->setActiveShader(theProgram);
theProgram->setPropertyValue("modelViewProjection", inMVP);
theContext->setInputAssembler(inSubset.inputAssemblerPoints);
theContext->draw(QSSGRenderDrawMode::Lines, inSubset.posVertexBuffer->numVertexes(), 0);
return true;
}
void QSSGMaterialSystem::endFrame()
{
#ifdef QQ3D_UNUSED_TIMER
if (lastFrameTime.elapsed() != 0)
msSinceLastFrame = lastFrameTime.elapsed()/1000000.0f;
lastFrameTime.restart();
#endif
}
void QSSGMaterialSystem::setRenderContextInterface(QSSGRenderContextInterface *inContext)
{
context = inContext;
// check for fast blits
const QSSGRef<QSSGRenderContext> &theContext = context->renderContext();
useFastBlits = theContext->renderBackendCap(QSSGRenderBackend::QSSGRenderBackendCaps::FastBlits);
}
QT_END_NAMESPACE