blob: 55a711921d2d3e0d90580a23de2645428ccbc3b4 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "glshader_p.h"
#include <QMutexLocker>
#include <Qt3DRender/private/stringtoint_p.h>
#include <graphicscontext_p.h>
#include <logging_p.h>
#include <gllights_p.h>
QT_BEGIN_NAMESPACE
namespace Qt3DRender {
namespace Render {
namespace OpenGL {
namespace {
QVector<int> getLightUniformNameIds()
{
QVector<int> names;
names.reserve(MAX_LIGHTS * 18 + 1);
names << GLLights::LIGHT_COUNT_NAME_ID;
for (int i = 0; i < MAX_LIGHTS; ++i) {
names << GLLights::LIGHT_TYPE_NAMES[i]
<< GLLights::LIGHT_COLOR_NAMES[i]
<< GLLights::LIGHT_POSITION_NAMES[i]
<< GLLights::LIGHT_INTENSITY_NAMES[i]
<< GLLights::LIGHT_DIRECTION_NAMES[i]
<< GLLights::LIGHT_LINEAR_ATTENUATION_NAMES[i]
<< GLLights::LIGHT_QUADRATIC_ATTENUATION_NAMES[i]
<< GLLights::LIGHT_CONSTANT_ATTENUATION_NAMES[i]
<< GLLights::LIGHT_CUT_OFF_ANGLE_NAMES[i]
<< GLLights::LIGHT_TYPE_UNROLL_NAMES[i]
<< GLLights::LIGHT_COLOR_UNROLL_NAMES[i]
<< GLLights::LIGHT_POSITION_UNROLL_NAMES[i]
<< GLLights::LIGHT_INTENSITY_UNROLL_NAMES[i]
<< GLLights::LIGHT_DIRECTION_UNROLL_NAMES[i]
<< GLLights::LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[i]
<< GLLights::LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[i]
<< GLLights::LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[i]
<< GLLights::LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[i];
}
return names;
}
template<typename Vector>
bool fastContains(const Vector &v, int value)
{
return std::binary_search(v.cbegin(), v.cend(), value);
}
}
GLShader::GLShader()
: m_isLoaded(false)
, m_graphicsContext(nullptr)
, m_parameterPackSize(0)
, m_hasActiveVariables(false)
{
m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1);
}
GLShader::~GLShader()
{
if (m_contextConnection)
QObject::disconnect(m_contextConnection);
}
void GLShader::setGraphicsContext(GraphicsContext *context)
{
QMutexLocker lock(&m_mutex);
m_graphicsContext = context;
if (m_graphicsContext) {
m_contextConnection = QObject::connect(m_graphicsContext->openGLContext(),
&QOpenGLContext::aboutToBeDestroyed,
[this] { setGraphicsContext(nullptr); });
}
}
GraphicsContext *GLShader::graphicsContext()
{
QMutexLocker lock(&m_mutex);
return m_graphicsContext;
}
QVector<QString> GLShader::uniformsNames() const
{
return m_uniformsNames;
}
QVector<QString> GLShader::attributesNames() const
{
return m_attributesNames;
}
QVector<QString> GLShader::uniformBlockNames() const
{
return m_uniformBlockNames;
}
QVector<QString> GLShader::storageBlockNames() const
{
return m_shaderStorageBlockNames;
}
QVector<QByteArray> GLShader::shaderCode() const
{
return m_shaderCode;
}
QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockIndex) const
{
return m_uniformBlockIndexToShaderUniforms.value(blockIndex);
}
ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_index == blockIndex) {
return m_uniformBlocks[i];
}
}
return ShaderUniformBlock();
}
ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_nameId == blockNameId) {
return m_uniformBlocks[i];
}
}
return ShaderUniformBlock();
}
ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_name == blockName) {
return m_uniformBlocks[i];
}
}
return ShaderUniformBlock();
}
ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_index == blockIndex)
return m_shaderStorageBlocks[i];
}
return ShaderStorageBlock();
}
ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_nameId == blockNameId)
return m_shaderStorageBlocks[i];
}
return ShaderStorageBlock();
}
ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_name == blockName)
return m_shaderStorageBlocks[i];
}
return ShaderStorageBlock();
}
GLShader::ParameterKind GLShader::categorizeVariable(int nameId) const noexcept
{
if (fastContains(m_uniformsNamesIds, nameId))
return ParameterKind::Uniform;
if (fastContains(m_uniformBlockNamesIds, nameId))
return ParameterKind::UBO;
if (fastContains(m_shaderStorageBlockNamesIds, nameId))
return ParameterKind::SSBO;
return ParameterKind::Struct;
}
bool GLShader::hasUniform(int nameId) const noexcept
{
return m_uniformsNamesIds.contains(nameId);
}
void GLShader::prepareUniforms(ShaderParameterPack &pack)
{
const PackUniformHash &values = pack.uniforms();
auto it = values.keys.cbegin();
const auto end = values.keys.cend();
const int shaderUniformsCount = m_uniforms.size();
const auto uIt = m_uniforms.cbegin();
while (it != end) {
// Find if there's a uniform with the same name id
int i = 0;
const int targetNameId = *it;
while (i < shaderUniformsCount && (uIt + i)->m_nameId < targetNameId)
++i;
if (i < shaderUniformsCount && (uIt + i)->m_nameId == targetNameId)
pack.setSubmissionUniformIndex(i);
++it;
}
}
void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
{
{
QMutexLocker lock(&m_mutex);
m_fragOutputs = fragOutputs;
}
}
const QHash<QString, int> GLShader::fragOutputs() const
{
QMutexLocker lock(&m_mutex);
return m_fragOutputs;
}
void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescription)
{
m_uniforms = uniformsDescription;
m_uniformsNames.resize(uniformsDescription.size());
m_uniformsNamesIds.reserve(uniformsDescription.size());
m_standardUniformNamesIds.reserve(5);
m_lightUniformsNamesIds.reserve(MAX_LIGHTS * 8 + 1);
QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
static const QVector<int> standardUniformNameIds = {
Shader::modelMatrixNameId,
Shader::viewMatrixNameId,
Shader::projectionMatrixNameId,
Shader::modelViewMatrixNameId,
Shader::viewProjectionMatrixNameId,
Shader::modelViewProjectionNameId,
Shader::mvpNameId,
Shader::inverseModelMatrixNameId,
Shader::inverseViewMatrixNameId,
Shader::inverseProjectionMatrixNameId,
Shader::inverseModelViewNameId,
Shader::inverseViewProjectionMatrixNameId,
Shader::inverseModelViewProjectionNameId,
Shader::modelNormalMatrixNameId,
Shader::modelViewNormalNameId,
Shader::viewportMatrixNameId,
Shader::inverseViewportMatrixNameId,
Shader::aspectRatioNameId,
Shader::exposureNameId,
Shader::gammaNameId,
Shader::timeNameId,
Shader::eyePositionNameId,
Shader::skinningPaletteNameId,
};
static const QVector<int> lightUniformNameIds = getLightUniformNameIds();
for (int i = 0, m = uniformsDescription.size(); i < m; i++) {
m_uniformsNames[i] = m_uniforms[i].m_name;
const int nameId = StringToInt::lookupId(m_uniformsNames[i]);
m_uniforms[i].m_nameId = nameId;
// Is the uniform a Qt3D "Standard" uniform, a light uniform or a user defined one?
if (standardUniformNameIds.contains(nameId))
m_standardUniformNamesIds.push_back(nameId);
else if (lightUniformNameIds.contains(nameId))
m_lightUniformsNamesIds.push_back(nameId);
else
m_uniformsNamesIds.push_back(nameId);
if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block
qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex;
activeUniformsInDefaultBlock.insert(uniformsDescription[i].m_name, uniformsDescription[i]);
}
}
m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock);
m_parameterPackSize += m_standardUniformNamesIds.size() + m_lightUniformsNamesIds.size() + m_uniformsNamesIds.size();
m_hasActiveVariables |= (m_parameterPackSize > 0);
// Sort by ascending order to make contains check faster
std::sort(m_uniformsNamesIds.begin(), m_uniformsNamesIds.end());
std::sort(m_lightUniformsNamesIds.begin(), m_lightUniformsNamesIds.end());
std::sort(m_standardUniformNamesIds.begin(), m_standardUniformNamesIds.end());
std::sort(m_uniforms.begin(), m_uniforms.end(),
[] (const ShaderUniform &a, const ShaderUniform &b) {
return a.m_nameId < b.m_nameId;
});
}
void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription)
{
m_attributes = attributesDescription;
m_attributesNames.resize(attributesDescription.size());
m_attributeNamesIds.resize(attributesDescription.size());
for (int i = 0, m = attributesDescription.size(); i < m; i++) {
m_attributesNames[i] = attributesDescription[i].m_name;
m_attributes[i].m_nameId = StringToInt::lookupId(m_attributesNames[i]);
m_attributeNamesIds[i] = m_attributes[i].m_nameId;
qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name;
}
m_hasActiveVariables |= (m_attributeNamesIds.size() > 0);
}
void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription)
{
m_uniformBlocks = uniformBlockDescription;
m_uniformBlockNames.resize(uniformBlockDescription.size());
m_uniformBlockNamesIds.resize(uniformBlockDescription.size());
for (int i = 0, m = uniformBlockDescription.size(); i < m; ++i) {
m_uniformBlockNames[i] = m_uniformBlocks[i].m_name;
m_uniformBlockNamesIds[i] = StringToInt::lookupId(m_uniformBlockNames[i]);
m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i];
qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}";
// Find all active uniforms for the shader block
QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin();
const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend();
QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin();
const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend();
QHash<QString, ShaderUniform> activeUniformsInBlock;
while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) {
if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) {
QString uniformName = *uniformNamesIt;
if (!m_uniformBlockNames[i].isEmpty() && !uniformName.startsWith(m_uniformBlockNames[i]))
uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt;
activeUniformsInBlock.insert(uniformName, *uniformsIt);
qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block " << m_uniformBlockNames[i] << " at index " << uniformsIt->m_blockIndex;
}
++uniformsIt;
++uniformNamesIt;
}
m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock);
}
m_parameterPackSize += m_uniformsNamesIds.size();
m_hasActiveVariables |= (m_parameterPackSize > 0);
// Sort by ascending order to make contains check faster
std::sort(m_uniformBlockNamesIds.begin(), m_uniformBlockNamesIds.end());
}
void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription)
{
m_shaderStorageBlocks = shaderStorageBlockDescription;
m_shaderStorageBlockNames.resize(shaderStorageBlockDescription.size());
m_shaderStorageBlockNamesIds.resize(shaderStorageBlockDescription.size());
for (int i = 0, m = shaderStorageBlockDescription.size(); i < m; ++i) {
m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name;
m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(m_shaderStorageBlockNames[i]);
m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i];
qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}";
}
m_parameterPackSize += m_shaderStorageBlockNamesIds.size();
m_hasActiveVariables |= (m_parameterPackSize > 0);
// Sort by ascending order to make contains check faster
std::sort(m_shaderStorageBlockNamesIds.begin(), m_shaderStorageBlockNamesIds.end());
}
} // OpenGL
} // Render
} // Qt3DRender
QT_END_NAMESPACE