| 1 | // Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "shader_p.h" |
| 5 | #include "renderlogging_p.h" |
| 6 | |
| 7 | #include <QFile> |
| 8 | #include <QOpenGLContext> |
| 9 | #include <QOpenGLShaderProgram> |
| 10 | #include <QMutexLocker> |
| 11 | #include <qshaderprogram.h> |
| 12 | #include <Qt3DRender/private/attachmentpack_p.h> |
| 13 | #include <Qt3DRender/private/qshaderprogram_p.h> |
| 14 | #include <Qt3DRender/private/stringtoint_p.h> |
| 15 | #include <Qt3DRender/private/managers_p.h> |
| 16 | |
| 17 | QT_BEGIN_NAMESPACE |
| 18 | |
| 19 | namespace Qt3DRender { |
| 20 | namespace Render { |
| 21 | |
| 22 | using namespace Qt3DCore; |
| 23 | const int Shader::modelMatrixNameId = StringToInt::lookupId(str: QLatin1String("modelMatrix" )); |
| 24 | const int Shader::viewMatrixNameId = StringToInt::lookupId(str: QLatin1String("viewMatrix" )); |
| 25 | const int Shader::projectionMatrixNameId = StringToInt::lookupId(str: QLatin1String("projectionMatrix" )); |
| 26 | const int Shader::modelViewMatrixNameId = StringToInt::lookupId(str: QLatin1String("modelView" )); |
| 27 | const int Shader::viewProjectionMatrixNameId = StringToInt::lookupId(str: QLatin1String("viewProjectionMatrix" )); |
| 28 | const int Shader::modelViewProjectionNameId = StringToInt::lookupId(str: QLatin1String("modelViewProjection" )); |
| 29 | const int Shader::mvpNameId = StringToInt::lookupId(str: QLatin1String("mvp" )); |
| 30 | const int Shader::inverseModelMatrixNameId = StringToInt::lookupId(str: QLatin1String("inverseModelMatrix" )); |
| 31 | const int Shader::inverseViewMatrixNameId = StringToInt::lookupId(str: QLatin1String("inverseViewMatrix" )); |
| 32 | const int Shader::inverseProjectionMatrixNameId = StringToInt::lookupId(str: QLatin1String("inverseProjectionMatrix" )); |
| 33 | const int Shader::inverseModelViewNameId = StringToInt::lookupId(str: QLatin1String("inverseModelView" )); |
| 34 | const int Shader::inverseViewProjectionMatrixNameId = StringToInt::lookupId(str: QLatin1String("inverseViewProjectionMatrix" )); |
| 35 | const int Shader::inverseModelViewProjectionNameId = StringToInt::lookupId(str: QLatin1String("inverseModelViewProjection" )); |
| 36 | const int Shader::modelNormalMatrixNameId = StringToInt::lookupId(str: QLatin1String("modelNormalMatrix" )); |
| 37 | const int Shader::modelViewNormalNameId = StringToInt::lookupId(str: QLatin1String("modelViewNormal" )); |
| 38 | const int Shader::viewportMatrixNameId = StringToInt::lookupId(str: QLatin1String("viewportMatrix" )); |
| 39 | const int Shader::inverseViewportMatrixNameId = StringToInt::lookupId(str: QLatin1String("inverseViewportMatrix" )); |
| 40 | const int Shader::textureTransformMatrixNameId = StringToInt::lookupId(str: QLatin1String("textureTransformMatrix" )); |
| 41 | const int Shader::aspectRatioNameId = StringToInt::lookupId(str: QLatin1String("aspectRatio" )); |
| 42 | const int Shader::exposureNameId = StringToInt::lookupId(str: QLatin1String("exposure" )); |
| 43 | const int Shader::gammaNameId = StringToInt::lookupId(str: QLatin1String("gamma" )); |
| 44 | const int Shader::timeNameId = StringToInt::lookupId(str: QLatin1String("time" )); |
| 45 | const int Shader::eyePositionNameId = StringToInt::lookupId(str: QLatin1String("eyePosition" )); |
| 46 | const int Shader::skinningPaletteNameId = StringToInt::lookupId(str: QLatin1String("skinningPalette[0]" )); |
| 47 | const int Shader::yUpInFBOId = StringToInt::lookupId(str: QLatin1String("yUpInFBO" )); |
| 48 | const int Shader::yUpInNDCId = StringToInt::lookupId(str: QLatin1String("yUpInNDC" )); |
| 49 | |
| 50 | Shader::Shader() |
| 51 | : BackendNode(ReadWrite) |
| 52 | , m_requiresFrontendSync(false) |
| 53 | , m_status(QShaderProgram::NotReady) |
| 54 | , m_format(QShaderProgram::GLSL) |
| 55 | , m_dirty(false) |
| 56 | { |
| 57 | m_shaderCode.resize(new_size: static_cast<int>(QShaderProgram::Compute) + 1); |
| 58 | } |
| 59 | |
| 60 | Shader::~Shader() |
| 61 | { |
| 62 | } |
| 63 | |
| 64 | void Shader::cleanup() |
| 65 | { |
| 66 | QBackendNode::setEnabled(false); |
| 67 | m_status = QShaderProgram::NotReady; |
| 68 | m_format = QShaderProgram::GLSL; |
| 69 | m_log.clear(); |
| 70 | m_requiresFrontendSync = false; |
| 71 | m_dirty = false; |
| 72 | } |
| 73 | |
| 74 | void Shader::syncFromFrontEnd(const QNode *frontEnd, bool firstTime) |
| 75 | { |
| 76 | const QShaderProgram *node = qobject_cast<const QShaderProgram *>(object: frontEnd); |
| 77 | if (!node) |
| 78 | return; |
| 79 | |
| 80 | BackendNode::syncFromFrontEnd(frontEnd, firstTime); |
| 81 | |
| 82 | if (firstTime) |
| 83 | for (int i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) |
| 84 | m_shaderCode[i].clear(); |
| 85 | |
| 86 | for (int i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) { |
| 87 | const QShaderProgram::ShaderType shaderType = static_cast<QShaderProgram::ShaderType>(i); |
| 88 | const QByteArray code = node->shaderCode(type: shaderType); |
| 89 | if (code != m_shaderCode[shaderType]) |
| 90 | setShaderCode(type: shaderType, code); |
| 91 | } |
| 92 | setFormat(node->format()); |
| 93 | } |
| 94 | |
| 95 | void Shader::setShaderCode(QShaderProgram::ShaderType type, const QByteArray &code) |
| 96 | { |
| 97 | if (code == m_shaderCode[type]) |
| 98 | return; |
| 99 | |
| 100 | m_shaderCode[type] = code; |
| 101 | m_requiresFrontendSync = true; |
| 102 | m_dirty = true; |
| 103 | setStatus(QShaderProgram::NotReady); |
| 104 | markDirty(changes: AbstractRenderer::ShadersDirty); |
| 105 | } |
| 106 | |
| 107 | void Shader::setFormat(QShaderProgram::Format format) |
| 108 | { |
| 109 | if (format == m_format) |
| 110 | return; |
| 111 | m_format = format; |
| 112 | m_dirty = true; |
| 113 | setStatus(QShaderProgram::NotReady); |
| 114 | markDirty(changes: AbstractRenderer::ShadersDirty); |
| 115 | } |
| 116 | |
| 117 | const std::vector<QByteArray> &Shader::shaderCode() const |
| 118 | { |
| 119 | return m_shaderCode; |
| 120 | } |
| 121 | |
| 122 | /*! |
| 123 | \internal |
| 124 | Initializes this Shader's state relating to attributes, global block uniforms and |
| 125 | and named uniform blocks by copying these details from \a other. |
| 126 | */ |
| 127 | void Shader::initializeFromReference(const Shader &other) |
| 128 | { |
| 129 | m_status = other.m_status; |
| 130 | m_log = other.m_log; |
| 131 | m_requiresFrontendSync = true; |
| 132 | setStatus(other.status()); |
| 133 | setLog(other.log()); |
| 134 | } |
| 135 | |
| 136 | // Called by renderer plugin when loading a GL Shader plugins |
| 137 | void Shader::requestCacheRebuild() |
| 138 | { |
| 139 | markDirty(changes: AbstractRenderer::MaterialDirty); |
| 140 | } |
| 141 | |
| 142 | void Shader::setLog(const QString &log) |
| 143 | { |
| 144 | m_log = log; |
| 145 | m_requiresFrontendSync = true; |
| 146 | } |
| 147 | |
| 148 | void Shader::setStatus(QShaderProgram::Status status) |
| 149 | { |
| 150 | m_status = status; |
| 151 | m_requiresFrontendSync = true; |
| 152 | } |
| 153 | |
| 154 | ShaderFunctor::ShaderFunctor(AbstractRenderer *renderer, ShaderManager *manager) |
| 155 | : m_renderer(renderer) |
| 156 | , m_shaderManager(manager) |
| 157 | { |
| 158 | } |
| 159 | |
| 160 | QBackendNode *ShaderFunctor::create(QNodeId id) const |
| 161 | { |
| 162 | Shader *backend = m_shaderManager->getOrCreateResource(id); |
| 163 | // Remove from the list of ids to destroy in case we were added to it |
| 164 | m_shaderManager->removeShaderIdFromIdsToCleanup(id); |
| 165 | backend->setRenderer(m_renderer); |
| 166 | return backend; |
| 167 | } |
| 168 | |
| 169 | QBackendNode *ShaderFunctor::get(QNodeId id) const |
| 170 | { |
| 171 | // If we are marked for destruction, return nullptr so that |
| 172 | // if we were to be recreated, create would be called again |
| 173 | if (m_shaderManager->hasShaderIdToCleanup(id)) |
| 174 | return nullptr; |
| 175 | return m_shaderManager->lookupResource(id); |
| 176 | } |
| 177 | |
| 178 | void ShaderFunctor::destroy(QNodeId id) const |
| 179 | { |
| 180 | // We only add ourselves to the dirty list |
| 181 | // The actual removal needs to be performed after we have |
| 182 | // destroyed the associated APIShader in the RenderThread |
| 183 | if (m_shaderManager->lookupResource(id)) |
| 184 | m_shaderManager->addShaderIdToCleanup(id); |
| 185 | } |
| 186 | |
| 187 | } // namespace Render |
| 188 | } // namespace Qt3DRender |
| 189 | |
| 190 | QT_END_NAMESPACE |
| 191 | |