blob: 0c9e3c89a3858140b486915189df0002d8ea47f5 [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 "qssgrenderdynamicobjectsystem_p.h"
#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderinputstreamfactory_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderdynamicobjectsystemcommands_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderdynamicobjectsystemutil_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h>
#include <QtQuick3DRender/private/qssgrendershaderconstant_p.h>
#include <QtQuick3DRender/private/qssgrendershaderprogram_p.h>
#include <QtQuick3DUtils/private/qssgutils_p.h>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
QT_BEGIN_NAMESPACE
uint qHash(const TStrStrPair &item)
{
return qHash(item.first) ^ qHash(item.second);
}
namespace dynamic {
uint qHash(const QSSGDynamicShaderMapKey &inKey)
{
return inKey.m_hashCode;
}
//quint32 QSSGCommand::getSizeofCommand(const QSSGCommand &inCommand)
//{
// switch (inCommand.m_type) {
// case CommandType::AllocateBuffer:
// return sizeof(QSSGAllocateBuffer);
// case CommandType::BindBuffer:
// return sizeof(QSSGBindBuffer);
// case CommandType::BindTarget:
// return sizeof(QSSGBindTarget);
// case CommandType::BindShader:
// return sizeof(QSSGBindShader);
// case CommandType::Render:
// return sizeof(QSSGRender);
// case CommandType::ApplyBufferValue:
// return sizeof(QSSGApplyBufferValue);
// case CommandType::ApplyDepthValue:
// return sizeof(QSSGApplyDepthValue);
// case CommandType::ApplyInstanceValue:
// return sizeof(QSSGApplyInstanceValue);
// case CommandType::ApplyBlending:
// return sizeof(QSSGApplyBlending);
// case CommandType::ApplyRenderState:
// return sizeof(QSSGApplyRenderState);
// case CommandType::ApplyBlitFramebuffer:
// return sizeof(QSSGApplyBlitFramebuffer);
// case CommandType::ApplyValue:
// return sizeof(QSSGApplyValue) + static_cast<const QSSGApplyValue &>(inCommand).m_value.mSize;
// case CommandType::DepthStencil:
// return sizeof(QSSGDepthStencil);
// case CommandType::AllocateImage:
// return sizeof(QSSGAllocateImage);
// case CommandType::ApplyImageValue:
// return sizeof(QSSGApplyImageValue);
// case CommandType::AllocateDataBuffer:
// return sizeof(QSSGAllocateDataBuffer);
// case CommandType::ApplyDataBufferValue:
// return sizeof(QSSGApplyDataBufferValue);
// default:
// break;
// }
// Q_ASSERT(false);
// return 0;
//}
//template<typename TCommandType>
//inline void CopyConstructCommandT(quint8 *inDataBuffer, const QSSGCommand &inCommand)
//{
// TCommandType *theCommand = (TCommandType *)inDataBuffer;
// theCommand = new (theCommand) TCommandType(static_cast<const TCommandType &>(inCommand));
//}
//void QSSGCommand::copyConstructCommand(quint8 *inDataBuffer, const QSSGCommand &inCommand)
//{
// switch (inCommand.m_type) {
// case CommandType::AllocateBuffer:
// CopyConstructCommandT<QSSGAllocateBuffer>(inDataBuffer, inCommand);
// break;
// case CommandType::BindBuffer:
// CopyConstructCommandT<QSSGBindBuffer>(inDataBuffer, inCommand);
// break;
// case CommandType::BindTarget:
// CopyConstructCommandT<QSSGBindTarget>(inDataBuffer, inCommand);
// break;
// case CommandType::BindShader:
// CopyConstructCommandT<QSSGBindShader>(inDataBuffer, inCommand);
// break;
// case CommandType::Render:
// CopyConstructCommandT<QSSGRender>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyBufferValue:
// CopyConstructCommandT<QSSGApplyBufferValue>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyDepthValue:
// CopyConstructCommandT<QSSGApplyDepthValue>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyInstanceValue:
// CopyConstructCommandT<QSSGApplyInstanceValue>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyBlending:
// CopyConstructCommandT<QSSGApplyBlending>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyRenderState:
// CopyConstructCommandT<QSSGApplyRenderState>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyBlitFramebuffer:
// CopyConstructCommandT<QSSGApplyBlitFramebuffer>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyValue: {
// CopyConstructCommandT<QSSGApplyValue>(inDataBuffer, inCommand);
// QSSGApplyValue &dest = *reinterpret_cast<QSSGApplyValue *>(inDataBuffer);
// quint8 *destMem = inDataBuffer + sizeof(QSSGApplyValue);
// const QSSGApplyValue &src = static_cast<const QSSGApplyValue &>(inCommand);
// memcpy(destMem, src.m_value.mData, src.m_value.mSize);
// dest.m_value.mData = destMem;
// break;
// }
// case CommandType::DepthStencil:
// CopyConstructCommandT<QSSGDepthStencil>(inDataBuffer, inCommand);
// break;
// case CommandType::AllocateImage:
// CopyConstructCommandT<QSSGAllocateImage>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyImageValue:
// CopyConstructCommandT<QSSGApplyImageValue>(inDataBuffer, inCommand);
// break;
// case CommandType::AllocateDataBuffer:
// CopyConstructCommandT<QSSGAllocateDataBuffer>(inDataBuffer, inCommand);
// break;
// case CommandType::ApplyDataBufferValue:
// CopyConstructCommandT<QSSGApplyDataBufferValue>(inDataBuffer, inCommand);
// break;
// default:
// Q_ASSERT(false);
// break;
// }
//}
}
QString QSSGDynamicObjectSystem::getShaderCodeLibraryDirectory()
{
return QStringLiteral("res/effectlib");
}
static QByteArray includeSearch() { return QByteArrayLiteral("#include \""); };
static QByteArray copyrightHeaderStart() { return QByteArrayLiteral("/****************************************************************************"); }
static QByteArray copyrightHeaderEnd() { return QByteArrayLiteral("****************************************************************************/"); }
QSSGDynamicObjectSystem::QSSGDynamicObjectSystem(QSSGRenderContextInterface *ctx)
: m_context(ctx), m_propertyLoadMutex()
{
}
QSSGDynamicObjectSystem::~QSSGDynamicObjectSystem() {}
void QSSGDynamicObjectSystem::setShaderData(const QByteArray &inPath,
const QByteArray &inData,
const QByteArray &inShaderType,
const QByteArray &inShaderVersion,
bool inHasGeomShader,
bool inIsComputeShader)
{
// inData = inData ? inData : "";
auto foundIt = m_expandedFiles.find(inPath);
if (foundIt != m_expandedFiles.end())
foundIt.value() = inData;
else
m_expandedFiles.insert(inPath, inData);
// set shader type and version if available
if (!inShaderType.isNull() || !inShaderVersion.isNull() || inHasGeomShader || inIsComputeShader) {
// UdoL TODO: Add this to the load / save setction
// In addition we should merge the source code into SDynamicObjectShaderInfo as well
QSSGDynamicObjectShaderInfo &theShaderInfo = m_shaderInfoMap.insert(inPath, QSSGDynamicObjectShaderInfo()).value();
theShaderInfo.m_type = inShaderType;
theShaderInfo.m_version = inShaderVersion;
theShaderInfo.m_hasGeomShader = inHasGeomShader;
theShaderInfo.m_isComputeShader = inIsComputeShader;
}
}
QByteArray QSSGDynamicObjectSystem::getShaderCacheKey(const QByteArray &inId,
const QByteArray &inProgramMacro,
const dynamic::QSSGDynamicShaderProgramFlags &inFlags)
{
QByteArray shaderKey = inId;
if (!inProgramMacro.isEmpty()) {
shaderKey.append("#");
shaderKey.append(inProgramMacro);
}
if (inFlags & ShaderCacheProgramFlagValues::TessellationEnabled) {
shaderKey.append("#");
shaderKey.append(toString(inFlags.tessMode));
}
if (inFlags & ShaderCacheProgramFlagValues::GeometryShaderEnabled && inFlags.wireframeMode) {
shaderKey.append("#");
shaderKey.append(inFlags.wireframeToString(inFlags.wireframeMode));
}
return shaderKey;
}
void QSSGDynamicObjectSystem::insertShaderHeaderInformation(QByteArray &theReadBuffer, const QByteArray &inPathToEffect)
{
doInsertShaderHeaderInformation(theReadBuffer, inPathToEffect);
}
void QSSGDynamicObjectSystem::doInsertShaderHeaderInformation(QByteArray &theReadBuffer, const QByteArray &inPathToEffect)
{
// Now do search and replace for the headers
for (int thePos = theReadBuffer.indexOf(includeSearch()); thePos != -1;
thePos = theReadBuffer.indexOf(includeSearch(), thePos + 1)) {
int theEndQuote = theReadBuffer.indexOf('\"', thePos + includeSearch().length() + 1);
// Indicates an unterminated include file.
if (theEndQuote == -1) {
qCCritical(INVALID_OPERATION, "Unterminated include in file: %s", inPathToEffect.constData());
theReadBuffer.clear();
break;
}
const int theActualBegin = thePos + includeSearch().length();
const auto &theInclude = theReadBuffer.mid(theActualBegin, theEndQuote - theActualBegin);
// If we haven't included the file yet this round
auto theHeader = doLoadShader(theInclude);
// Strip copywrite headers from include if present
if (theHeader.startsWith(copyrightHeaderStart())) {
int clipPos = theHeader.indexOf(copyrightHeaderEnd()) ;
if (clipPos >= 0)
theHeader.remove(0, clipPos + copyrightHeaderEnd().count());
}
// Write insert comment for begin source
theHeader.prepend(QByteArrayLiteral("\n// begin \"") + theInclude + QByteArrayLiteral("\"\n"));
// Write insert comment for end source
theHeader.append(QByteArrayLiteral("\n// end \"" ) + theInclude + QByteArrayLiteral("\"\n"));
theReadBuffer = theReadBuffer.replace(thePos, (theEndQuote + 1) - thePos, theHeader);
}
}
QByteArray QSSGDynamicObjectSystem::doLoadShader(const QByteArray &inPathToEffect)
{
auto theInsert = m_expandedFiles.find(inPathToEffect);
const bool found = (theInsert != m_expandedFiles.end());
QByteArray theReadBuffer;
if (!found) {
const QString defaultDir = getShaderCodeLibraryDirectory();
const QString platformDir = shaderCodeLibraryPlatformDirectory();
const auto ver = shaderCodeLibraryVersion();
QString fullPath;
QSharedPointer<QIODevice> theStream;
if (!platformDir.isEmpty()) {
QTextStream stream(&fullPath);
stream << platformDir << QLatin1Char('/') << QString::fromLocal8Bit(inPathToEffect);
theStream = m_context->inputStreamFactory()->getStreamForFile(fullPath, true);
}
if (theStream.isNull()) {
fullPath.clear();
QTextStream stream(&fullPath);
stream << defaultDir << QLatin1Char('/') << ver << QLatin1Char('/') << QString::fromLocal8Bit(inPathToEffect);
theStream = m_context->inputStreamFactory()->getStreamForFile(fullPath, true);
if (theStream.isNull()) {
fullPath.clear();
QTextStream stream(&fullPath);
stream << defaultDir << QLatin1Char('/') << QString::fromLocal8Bit(inPathToEffect);
theStream = m_context->inputStreamFactory()->getStreamForFile(fullPath, false);
}
}
if (!theStream.isNull()) {
char readBuf[1024];
qint64 amountRead = 0;
do {
amountRead = theStream->read(readBuf, 1024);
if (amountRead)
theReadBuffer.append(readBuf, int(amountRead));
} while (amountRead);
} else {
qCCritical(INVALID_OPERATION, "Failed to find include file %s", qPrintable(QString::fromLocal8Bit(inPathToEffect)));
Q_ASSERT(false);
}
theInsert = m_expandedFiles.insert(inPathToEffect, theReadBuffer);
} else {
theReadBuffer = theInsert.value();
}
doInsertShaderHeaderInformation(theReadBuffer, inPathToEffect);
return theReadBuffer;
}
QStringList QSSGDynamicObjectSystem::getParameters(const QString &str, int begin, int end)
{
const QString s = str.mid(begin, end - begin + 1);
return s.split(',');
}
void QSSGDynamicObjectSystem::insertSnapperDirectives(QString &str)
{
int beginIndex = 0;
// Snapper macros:
// #define SNAPPER_SAMPLER2D(propName, propNiceName, texFilter, texWrap, showUI )
// uniform sampler2D propName;
// uniform int flag##propName;
// uniform vec4 propName##Info;
// vec4 texture2D_##propName(vec2 uv)
// {
// return GetTextureValue( propName, uv, propName##Info.z );
// }
//
// #define SNAPPER_SAMPLER2DWITHDEFAULT(propName, propNiceName, texFilter, texWrap, defaultPath, showUI )
// SNAPPER_SAMPLER2D( propName, propNiceName, texFilter, texWrap, showUI )
//
// #define SNAPPER_SAMPLERCUBE(propName, propNiceName, texFilter, texWrap )
// uniform samplerCube propName;
// uniform vec2 propName##UVRange;
// uniform int flag##propName;
// uniform vec2 propName##Size;
QString snapperSampler = QStringLiteral("SNAPPER_SAMPLER2D(");
QString snapperSamplerDefault = QStringLiteral("SNAPPER_SAMPLER2DWITHDEFAULT(");
QString snapperSamplerCube = QStringLiteral("SNAPPER_SAMPLERCUBE(");
QString endingBracket = QStringLiteral(")");
while ((beginIndex = str.indexOf(snapperSampler, beginIndex)) >= 0) {
int endIndex = str.indexOf(endingBracket, beginIndex);
const QStringList list = getParameters(str, beginIndex + snapperSampler.length(), endIndex);
str.remove(beginIndex, endIndex - beginIndex + 1);
if (list.size() == 5) {
QString insertStr;
QTextStream stream(&insertStr);
stream << "uniform sampler2D " << list[0] << ";\n";
stream << "uniform int flag" << list[0] << ";\n";
stream << "vec4 " << list[0] << "Info;\n";
stream << "vec4 texture2D_" << list[0] << "(vec2 uv) "
<< "{ return GetTextureValue( " << list[0] << ", uv, " << list[0] << "Info.z ); }\n";
str.insert(beginIndex, insertStr);
}
}
beginIndex = 0;
while ((beginIndex = str.indexOf(snapperSamplerDefault, beginIndex)) >= 0) {
int endIndex = str.indexOf(endingBracket, beginIndex);
const QStringList list = getParameters(str, beginIndex + snapperSamplerDefault.length(), endIndex);
str.remove(beginIndex, endIndex - beginIndex + 1);
if (list.size() == 5) {
QString insertStr;
QTextStream stream(&insertStr);
stream << "uniform sampler2D " << list[0] << ";\n";
stream << "uniform int flag" << list[0] << ";\n";
stream << "vec4 " << list[0] << "Info;\n";
stream << "vec4 texture2D_" << list[0] << "(vec2 uv) "
<< "{ return GetTextureValue( " << list[0] << ", uv, " << list[0] << "Info.z ); }\n";
str.insert(beginIndex, insertStr);
}
}
beginIndex = 0;
while ((beginIndex = str.indexOf(snapperSamplerCube, beginIndex)) >= 0) {
int endIndex = str.indexOf(endingBracket, beginIndex);
const QStringList list = getParameters(str, beginIndex + snapperSamplerCube.length(), endIndex);
str.remove(beginIndex, endIndex - beginIndex + 1);
if (list.size() == 4) {
QString insertStr;
QTextStream stream(&insertStr);
stream << "uniform samplerCube " << list[0] << ";\n";
stream << "uniform vec2 " << list[0] << "UVRange;\n";
stream << "uniform int flag" << list[0] << ";\n";
stream << "uniform vec2 " << list[0] << "Size;\n";
str.insert(beginIndex, insertStr);
}
}
}
QSSGRef<QSSGRenderShaderProgram> QSSGDynamicObjectSystem::compileShader(const QByteArray &inId,
const QByteArray &inProgramSource,
const QByteArray &inGeomSource,
const QByteArray &inProgramMacroName,
const ShaderFeatureSetList &inFeatureSet,
const dynamic::QSSGDynamicShaderProgramFlags &inFlags,
bool inForceCompilation)
{
m_vertShader.clear();
m_fragShader.clear();
m_geometryShader.clear();
QSSGShaderCacheProgramFlags theFlags;
m_vertShader.append("#define VERTEX_SHADER\n");
m_fragShader.append("#define FRAGMENT_SHADER\n");
if (!inProgramMacroName.isEmpty()) {
m_vertShader.append("#define ");
m_vertShader.append(inProgramMacroName);
m_vertShader.append("\n");
m_fragShader.append("#define ");
m_fragShader.append(inProgramMacroName);
m_fragShader.append("\n");
}
if (!inGeomSource.isEmpty() && inFlags & ShaderCacheProgramFlagValues::GeometryShaderEnabled) {
theFlags |= ShaderCacheProgramFlagValues::GeometryShaderEnabled;
m_geometryShader.append("#define GEOMETRY_SHADER 1\n");
m_geometryShader.append(inGeomSource);
m_vertShader.append("#define GEOMETRY_SHADER 1\n");
} else if (inFlags & ShaderCacheProgramFlagValues::GeometryShaderEnabled) {
theFlags |= ShaderCacheProgramFlagValues::GeometryShaderEnabled;
m_geometryShader.append("#define USER_GEOMETRY_SHADER 1\n");
m_geometryShader.append(inProgramSource);
m_vertShader.append("#define GEOMETRY_SHADER 0\n");
m_fragShader.append("#define GEOMETRY_WIREFRAME_SHADER 0\n");
} else {
m_vertShader.append("#define GEOMETRY_SHADER 0\n");
m_fragShader.append("#define GEOMETRY_WIREFRAME_SHADER 0\n");
}
if (strstr(inProgramSource, "SNAPPER_SAMPLER")) {
QString programSource = QString::fromLatin1(inProgramSource);
insertSnapperDirectives(programSource);
QByteArray data = programSource.toLatin1();
const char *source = data.constData();
m_vertShader.append(source);
m_fragShader.append(source);
} else {
m_vertShader.append(inProgramSource);
m_fragShader.append(inProgramSource);
}
QSSGRef<QSSGShaderCache> theShaderCache = m_context->shaderCache();
QByteArray theKey = getShaderCacheKey(inId, inProgramMacroName, inFlags);
if (inForceCompilation) {
return theShaderCache->forceCompileProgram(theKey, m_vertShader, m_fragShader, nullptr, nullptr, m_geometryShader, theFlags, inFeatureSet, false);
}
return theShaderCache->compileProgram(theKey, m_vertShader, m_fragShader, nullptr, nullptr, m_geometryShader, theFlags, inFeatureSet);
}
QByteArray QSSGDynamicObjectSystem::getShaderSource(const QByteArray &inPath)
{
// QByteArray source(QByteArrayLiteral("#define FRAGMENT_SHADER\n"));
// source.append(doLoadShader(inPath));
return doLoadShader(inPath);
}
TShaderAndFlags QSSGDynamicObjectSystem::getShaderProgram(const QByteArray &inPath,
const QByteArray &inProgramMacro,
const ShaderFeatureSetList &inFeatureSet,
const dynamic::QSSGDynamicShaderProgramFlags &inFlags,
bool inForceCompilation)
{
dynamic::QSSGDynamicShaderMapKey shaderMapKey(TStrStrPair(inPath, inProgramMacro), inFeatureSet, inFlags.tessMode, inFlags.wireframeMode);
auto theInserter = m_shaderMap.find(shaderMapKey);
const bool found = (theInserter != m_shaderMap.end());
if (!found)
theInserter = m_shaderMap.insert(shaderMapKey, TShaderAndFlags());
// TODO: This looks funky (if found)...
if (found || inForceCompilation) {
QSSGRef<QSSGRenderShaderProgram> theProgram = m_context->shaderCache()
->getProgram(getShaderCacheKey(inPath,
inProgramMacro,
inFlags),
inFeatureSet);
dynamic::QSSGDynamicShaderProgramFlags theFlags(inFlags);
if (!theProgram || inForceCompilation) {
QSSGDynamicObjectShaderInfo
&theShaderInfo = m_shaderInfoMap.insert(inPath, QSSGDynamicObjectShaderInfo()).value();
if (theShaderInfo.m_isComputeShader == false) {
QByteArray programSource = doLoadShader(inPath);
if (theShaderInfo.m_hasGeomShader)
theFlags |= ShaderCacheProgramFlagValues::GeometryShaderEnabled;
theProgram = compileShader(inPath, programSource.constData(), nullptr, inProgramMacro, inFeatureSet, theFlags, inForceCompilation);
} else {
QByteArray theShaderBuffer;
const char *shaderVersionStr = "#version 430\n";
if (m_context->renderContext()->renderContextType() == QSSGRenderContextType::GLES3PLUS)
shaderVersionStr = "#version 310 es\n";
theShaderBuffer = doLoadShader(inPath);
theShaderBuffer.insert(0, shaderVersionStr);
theProgram = m_context->renderContext()->compileComputeSource(inPath, toByteView(theShaderBuffer)).m_shader;
}
}
theInserter.value() = TShaderAndFlags(theProgram, theFlags);
}
return theInserter.value();
}
TShaderAndFlags QSSGDynamicObjectSystem::getDepthPrepassShader(const QByteArray &inPath, const QByteArray &inPMacro, const ShaderFeatureSetList &inFeatureSet)
{
QSSGDynamicObjectShaderInfo &theShaderInfo = m_shaderInfoMap.insert(inPath, QSSGDynamicObjectShaderInfo()).value();
if (theShaderInfo.m_hasGeomShader == false)
return TShaderAndFlags();
// else, here we go...
dynamic::QSSGDynamicShaderProgramFlags theFlags;
const QByteArray shaderKey = inPMacro + QByteArrayLiteral("depthprepass");
const QByteArray &theProgramMacro = shaderKey;
const dynamic::QSSGDynamicShaderMapKey shaderMapKey(TStrStrPair(inPath, theProgramMacro),
inFeatureSet,
theFlags.tessMode,
theFlags.wireframeMode);
const TShaderAndFlags shaderFlags;
auto theInserter = m_shaderMap.find(shaderMapKey);
const bool found = theInserter != m_shaderMap.end();
if (found) {
QSSGRef<QSSGRenderShaderProgram> theProgram = m_context->shaderCache()
->getProgram(getShaderCacheKey(inPath,
theProgramMacro,
theFlags),
inFeatureSet);
dynamic::QSSGDynamicShaderProgramFlags flags(theFlags);
if (!theProgram) {
QByteArray geomSource = doLoadShader(inPath);
QSSGShaderVertexCodeGenerator vertexShader(m_context->renderContext()->renderContextType());
QSSGShaderFragmentCodeGenerator fragmentShader(vertexShader, m_context->renderContext()->renderContextType());
vertexShader.addAttribute("attr_pos", "vec3");
vertexShader.addUniform("modelViewProjection", "mat4");
vertexShader.append("void main() {");
vertexShader.append("\tgl_Position = modelViewProjection * vec4(attr_pos, 1.0);");
vertexShader.append("}");
fragmentShader.append("void main() {");
fragmentShader.append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);");
fragmentShader.append("}");
QByteArray vertexSource = vertexShader.buildShaderSource();
QByteArray fragmentSource = fragmentShader.buildShaderSource();
QByteArray programBuffer(QByteArrayLiteral("#ifdef VERTEX_SHADER\n"));
programBuffer.append(vertexSource);
programBuffer.append("\n#endif\n");
programBuffer.append("\n#ifdef FRAGMENT_SHADER\n");
programBuffer.append(fragmentSource);
programBuffer.append("\n#endif");
flags |= ShaderCacheProgramFlagValues::GeometryShaderEnabled;
theProgram = compileShader(inPath, programBuffer, geomSource, theProgramMacro, inFeatureSet, flags);
}
theInserter.value() = TShaderAndFlags(theProgram, flags);
}
return theInserter.value();
}
void QSSGDynamicObjectSystem::setShaderCodeLibraryVersion(const QByteArray &version)
{
m_shaderLibraryVersion = version;
}
QByteArray QSSGDynamicObjectSystem::shaderCodeLibraryVersion()
{
return m_shaderLibraryVersion;
}
void QSSGDynamicObjectSystem::setShaderCodeLibraryPlatformDirectory(const QString &directory)
{
m_shaderLibraryPlatformDirectory = directory;
}
QString QSSGDynamicObjectSystem::shaderCodeLibraryPlatformDirectory()
{
return m_shaderLibraryPlatformDirectory;
}
QT_END_NAMESPACE