blob: 32ed8354b790d393e74cc5e83878415c844d8092 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2020 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 "qssgrendereffectsystem_p.h"
#include <QtQuick3DRender/private/qssgrendercontext_p.h>
#include "qssgrenderinputstreamfactory_p.h"
#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderresourcemanager_p.h>
#include "qssgrenderdynamicobjectsystemcommands_p.h"
#include <QtQuick3DRender/private/qssgrenderframebuffer_p.h>
#include <QtQuick3DRender/private/qssgrendershaderconstant_p.h>
#include <QtQuick3DRender/private/qssgrendershaderprogram_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendererutil_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderprefiltertexture_p.h>
#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
#include "qssgrenderdynamicobjectsystemutil_p.h"
QT_BEGIN_NAMESPACE
using namespace dynamic;
// None of this code will work if the size of void* changes because that would mean that
// the alignment of some of the objects isn't 4 bytes but would be 8 bytes.
struct QSSGAllocatedBufferEntry
{
QAtomicInt ref;
QByteArray name;
QSSGRef<QSSGRenderFrameBuffer> frameBuffer;
QSSGRef<QSSGRenderTexture2D> texture;
QSSGAllocateBufferFlags flags;
bool needsClear = true;
QSSGAllocatedBufferEntry(const QByteArray &inName,
QSSGRenderFrameBuffer &inFb,
QSSGRenderTexture2D &inTexture,
QSSGAllocateBufferFlags inFlags)
: name(inName),
frameBuffer(&inFb),
texture(&inTexture),
flags(inFlags)
{
}
QSSGAllocatedBufferEntry() = default;
};
struct QSSGAllocatedImageEntry
{
QAtomicInt ref;
QByteArray name;
QSSGRef<QSSGRenderImage2D> image;
QSSGRef<QSSGRenderTexture2D> texture;
QSSGAllocateBufferFlags flags;
QSSGAllocatedImageEntry(const QByteArray &inName,
QSSGRenderImage2D &inImage,
QSSGRenderTexture2D &inTexture,
QSSGAllocateBufferFlags inFlags)
: name(inName),
image(&inImage),
texture(&inTexture),
flags(inFlags)
{
}
QSSGAllocatedImageEntry() = default;
};
struct QSSGImageEntry
{
QAtomicInt ref;
QSSGRef<QSSGRenderShaderProgram> shader;
QSSGRenderCachedShaderProperty<QSSGRenderImage2D *> image;
QSSGImageEntry(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const char *inImageName)
: shader(inShader),
image(inImageName, inShader)
{
}
void set(QSSGRenderImage2D *inImage) { image.set(inImage); }
static QSSGImageEntry createImageEntry(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const char *inStem)
{
return QSSGImageEntry(inShader, inStem);
}
};
struct QSSGAllocatedDataBufferEntry
{
QAtomicInt ref;
QByteArray name;
QSSGRef<QSSGRenderDataBuffer> dataBuffer;
QSSGRenderBufferType bufferType;
QSSGByteRef bufferData;
QSSGAllocateBufferFlags flags;
bool needsClear = false;
QSSGAllocatedDataBufferEntry(const QByteArray &inName,
QSSGRenderDataBuffer &inDataBuffer,
QSSGRenderBufferType inType,
const QSSGByteRef &data,
QSSGAllocateBufferFlags inFlags)
: name(inName),
dataBuffer(&inDataBuffer),
bufferType(inType),
bufferData(data),
flags(inFlags)
{
}
QSSGAllocatedDataBufferEntry() = default;
};
struct QSSGDataBufferEntry
{
QAtomicInt ref;
QSSGRef<QSSGRenderShaderProgram> shader;
QSSGRenderCachedShaderBuffer<QSSGRenderShaderBufferBase> dataBuffer;
QSSGDataBufferEntry(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const char *inBufferName)
: shader(inShader),
dataBuffer(inBufferName, inShader)
{
}
void set(QSSGRenderDataBuffer *inBuffer)
{
if (inBuffer)
inBuffer->bind();
dataBuffer.set();
}
static QSSGDataBufferEntry createDataBufferEntry(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const char *inStem)
{
return QSSGDataBufferEntry(inShader, inStem);
}
};
struct QSSGTextureEntry
{
QAtomicInt ref;
QSSGRef<QSSGRenderShaderProgram> shader;
QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> texture;
QSSGRenderCachedShaderProperty<QVector4D> textureData;
QSSGRenderCachedShaderProperty<qint32> textureFlags;
QSSGTextureEntry(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const char *inTexName,
const char *inDataName,
const char *inFlagName)
: shader(inShader),
texture(inTexName, inShader),
textureData(inDataName, inShader),
textureFlags(inFlagName, inShader)
{
}
void set(const QSSGRef<QSSGRenderTexture2D> &inTexture,
bool inNeedsAlphaMultiply,
const QSSGRenderEffect::TextureProperty *inDefinition)
{
float theMixValue(inNeedsAlphaMultiply ? 0.0f : 1.0f);
if (inTexture && inDefinition) {
inTexture->setMagFilter(inDefinition->magFilterType);
inTexture->setMinFilter(static_cast<QSSGRenderTextureMinifyingOp>(inDefinition->magFilterType));
inTexture->setTextureWrapS(inDefinition->clampType);
inTexture->setTextureWrapT(inDefinition->clampType);
}
texture.set(inTexture.data());
if (inTexture) {
QSSGTextureDetails theDetails(inTexture->textureDetails());
textureData.set(QVector4D(float(theDetails.width),
float(theDetails.height),
theMixValue,
0.0f));
// I have no idea what these flags do.
textureFlags.set(1);
} else {
textureFlags.set(0);
}
}
static QSSGTextureEntry createTextureEntry(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QByteArray &inStem,
QString &inBuilder,
QString &inBuilder2)
{
inBuilder = QString::fromLatin1(inStem);
inBuilder.append(QString::fromLatin1("Info"));
inBuilder2 = QString::fromLatin1("flag");
inBuilder2.append(QString::fromLatin1(inStem));
return QSSGTextureEntry(inShader, inStem, inBuilder.toLocal8Bit(), inBuilder2.toLocal8Bit());
}
};
typedef QPair<QByteArray, QSSGRef<QSSGTextureEntry>> TNamedTextureEntry;
typedef QPair<QByteArray, QSSGRef<QSSGImageEntry>> TNamedImageEntry;
typedef QPair<QByteArray, QSSGRef<QSSGDataBufferEntry>> TNamedDataBufferEntry;
struct QSSGEffectContext
{
QAtomicInt ref;
const char *m_className;
QSSGRenderContextInterface *m_context;
QSSGRef<QSSGResourceManager> m_resourceManager;
QVector<QSSGAllocatedBufferEntry> m_allocatedBuffers;
QVector<QSSGAllocatedImageEntry> m_allocatedImages;
QVector<QSSGAllocatedDataBufferEntry> m_allocatedDataBuffers;
QVector<TNamedTextureEntry> m_textureEntries;
QVector<TNamedImageEntry> m_imageEntries;
QVector<TNamedDataBufferEntry> m_dataBufferEntries;
QSSGEffectContext(const char *inName,
QSSGRenderContextInterface *ctx,
const QSSGRef<QSSGResourceManager> &inManager)
: m_className(inName),
m_context(ctx),
m_resourceManager(inManager)
{
}
~QSSGEffectContext()
{
while (m_allocatedBuffers.size())
releaseBuffer(0);
while (m_allocatedImages.size())
releaseImage(0);
while (m_allocatedDataBuffers.size())
releaseDataBuffer(0);
}
void releaseBuffer(qint32 inIdx)
{
QSSGAllocatedBufferEntry &theEntry(m_allocatedBuffers[inIdx]);
theEntry.frameBuffer->attach(QSSGRenderFrameBufferAttachment::Color0, QSSGRenderTextureOrRenderBuffer());
m_resourceManager->release(theEntry.frameBuffer);
m_resourceManager->release(theEntry.texture);
{ // replace_with_last
m_allocatedBuffers[inIdx] = m_allocatedBuffers.back();
m_allocatedBuffers.pop_back();
}
}
void releaseImage(qint32 inIdx)
{
QSSGAllocatedImageEntry &theEntry(m_allocatedImages[inIdx]);
m_resourceManager->release(theEntry.image);
m_resourceManager->release(theEntry.texture);
{ // replace_with_last
m_allocatedImages[inIdx] = m_allocatedImages.back();
m_allocatedImages.pop_back();
}
}
void releaseDataBuffer(qint32 inIdx)
{
QSSGAllocatedDataBufferEntry &theEntry(m_allocatedDataBuffers[inIdx]);
::free(theEntry.bufferData.begin());
{ // replace_with_last
m_allocatedDataBuffers[inIdx] = m_allocatedDataBuffers.back();
m_allocatedDataBuffers.pop_back();
}
}
qint32 findBuffer(const QByteArray &inName)
{
for (qint32 idx = 0, end = m_allocatedBuffers.size(); idx < end; ++idx)
if (m_allocatedBuffers[idx].name == inName)
return idx;
return m_allocatedBuffers.size();
}
qint32 findImage(const QByteArray &inName)
{
for (qint32 idx = 0, end = m_allocatedImages.size(); idx < end; ++idx)
if (m_allocatedImages[idx].name == inName)
return idx;
return m_allocatedImages.size();
}
qint32 findDataBuffer(const QByteArray &inName)
{
for (qint32 idx = 0, end = m_allocatedDataBuffers.size(); idx < end; ++idx) {
if (m_allocatedDataBuffers[idx].name == inName)
return idx;
}
return m_allocatedDataBuffers.size();
}
void setTexture(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QByteArray &inPropName,
const QSSGRef<QSSGRenderTexture2D> &inTexture,
bool inNeedsMultiply,
QString &inStringBuilder,
QString &inStringBuilder2,
const QSSGRenderEffect::TextureProperty *inPropDec = nullptr)
{
QSSGRef<QSSGTextureEntry> theTextureEntry;
for (qint32 idx = 0, end = m_textureEntries.size(); idx < end && theTextureEntry == nullptr; ++idx) {
if (m_textureEntries[idx].first == inPropName && m_textureEntries[idx].second->shader == inShader)
theTextureEntry = m_textureEntries[idx].second;
}
if (theTextureEntry == nullptr) {
QSSGRef<QSSGTextureEntry> theNewEntry(new QSSGTextureEntry(
QSSGTextureEntry::createTextureEntry(inShader, inPropName, inStringBuilder, inStringBuilder2)));
m_textureEntries.push_back(QPair<QByteArray, QSSGRef<QSSGTextureEntry>>(inPropName, theNewEntry));
theTextureEntry = theNewEntry;
}
theTextureEntry->set(inTexture, inNeedsMultiply, inPropDec);
}
void setImage(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QByteArray &inPropName,
const QSSGRef<QSSGRenderImage2D> &inImage)
{
QSSGRef<QSSGImageEntry> theImageEntry;
for (qint32 idx = 0, end = m_imageEntries.size(); idx < end && theImageEntry == nullptr; ++idx) {
if (m_imageEntries[idx].first == inPropName && m_imageEntries[idx].second->shader == inShader)
theImageEntry = m_imageEntries[idx].second;
}
if (theImageEntry == nullptr) {
QSSGRef<QSSGImageEntry> theNewEntry(new QSSGImageEntry(QSSGImageEntry::createImageEntry(inShader, inPropName)));
m_imageEntries.push_back(QPair<QByteArray, QSSGRef<QSSGImageEntry>>(inPropName, theNewEntry));
theImageEntry = theNewEntry;
}
theImageEntry->set(inImage.data());
}
void setDataBuffer(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QByteArray &inPropName,
const QSSGRef<QSSGRenderDataBuffer> &inBuffer)
{
QSSGRef<QSSGDataBufferEntry> theDataBufferEntry;
for (qint32 idx = 0, end = m_dataBufferEntries.size(); idx < end && theDataBufferEntry == nullptr; ++idx) {
if (m_dataBufferEntries[idx].first == inPropName && m_dataBufferEntries[idx].second->shader == inShader)
theDataBufferEntry = m_dataBufferEntries[idx].second;
}
if (theDataBufferEntry == nullptr) {
QSSGRef<QSSGDataBufferEntry> theNewEntry(
new QSSGDataBufferEntry(QSSGDataBufferEntry::createDataBufferEntry(inShader, inPropName)));
m_dataBufferEntries.push_back(QPair<QByteArray, QSSGRef<QSSGDataBufferEntry>>(inPropName, theNewEntry));
theDataBufferEntry = theNewEntry;
}
theDataBufferEntry->set(inBuffer.data());
}
};
/* We setup some shared state on the effect shaders */
struct QSSGEffectShader
{
QAtomicInt ref;
QSSGRef<QSSGRenderShaderProgram> m_shader;
QSSGRenderCachedShaderProperty<QMatrix4x4> m_mvp;
QSSGRenderCachedShaderProperty<QVector2D> m_fragColorAlphaSettings;
QSSGRenderCachedShaderProperty<QVector2D> m_destSize;
QSSGRenderCachedShaderProperty<float> m_appFrame;
QSSGRenderCachedShaderProperty<float> m_fps;
QSSGRenderCachedShaderProperty<QVector2D> m_cameraClipRange;
QSSGTextureEntry m_textureEntry;
QSSGEffectShader(const QSSGRef<QSSGRenderShaderProgram> &inShader);
};
QSSGEffectShader::QSSGEffectShader(const QSSGRef<QSSGRenderShaderProgram> &inShader)
: m_shader(inShader)
, m_mvp("ModelViewProjectionMatrix", inShader)
, m_fragColorAlphaSettings("FragColorAlphaSettings", inShader)
, m_destSize("DestSize", inShader)
, m_appFrame("AppFrame", inShader)
, m_fps("FPS", inShader)
, m_cameraClipRange("CameraClipRange", inShader)
, m_textureEntry(inShader, "Texture0", "Texture0Info", "Texture0Flags")
{
}
QSSGEffectRenderArgument::QSSGEffectRenderArgument(QSSGRenderEffect *inEffect,
const QSSGRef<QSSGRenderTexture2D> &inColorBuffer,
const QVector2D &inCameraClipRange,
const QSSGRef<QSSGRenderTexture2D> &inDepthTexture,
const QSSGRef<QSSGRenderTexture2D> &inDepthBuffer)
: m_effect(inEffect)
, m_colorBuffer(inColorBuffer)
, m_cameraClipRange(inCameraClipRange)
, m_depthTexture(inDepthTexture)
, m_depthStencilBuffer(inDepthBuffer)
{
}
QSSGRenderEffect::~QSSGRenderEffect() {}
QSSGEffectSystem::QSSGEffectSystem(QSSGRenderContextInterface *inContext) : m_context(inContext)
{
init();
}
QSSGEffectSystem::~QSSGEffectSystem() = default;
QSSGEffectContext &QSSGEffectSystem::getEffectContext(QSSGRenderEffect &inEffect)
{
if (inEffect.m_context == nullptr) {
inEffect.m_context = new QSSGEffectContext(inEffect.className, m_context, m_resourceManager);
m_contexts.push_back(inEffect.m_context);
}
return *inEffect.m_context;
}
void QSSGEffectSystem::allocateBuffer(QSSGRenderEffect &inEffect,
const QSSGAllocateBuffer &inCommand,
qint32 inFinalWidth,
qint32 inFinalHeight,
QSSGRenderTextureFormat inSourceTextureFormat)
{
// Check to see if it is already allocated and if it is, is it the correct size. If both of
// these assumptions hold, then we are good.
QSSGRef<QSSGRenderTexture2D> theBufferTexture;
const qint32 theWidth = qint32(QSSGRendererUtil::nextMultipleOf4(quint32(inFinalWidth * inCommand.m_sizeMultiplier)));
const qint32 theHeight = qint32(QSSGRendererUtil::nextMultipleOf4(quint32(inFinalHeight * inCommand.m_sizeMultiplier)));
Q_ASSERT(theWidth >= 0 && theHeight >= 0);
QSSGRenderTextureFormat resultFormat = inCommand.m_format;
if (resultFormat == QSSGRenderTextureFormat::Unknown)
resultFormat = inSourceTextureFormat;
if (inEffect.m_context) {
QSSGEffectContext &theContext(*inEffect.m_context);
// size intentionally requiried every loop;
qint32 bufferIdx = theContext.findBuffer(inCommand.m_name);
if (bufferIdx < theContext.m_allocatedBuffers.size()) {
QSSGAllocatedBufferEntry &theEntry(theContext.m_allocatedBuffers[bufferIdx]);
QSSGTextureDetails theDetails = theEntry.texture->textureDetails();
if (theDetails.width == theWidth && theDetails.height == theHeight && theDetails.format == resultFormat) {
theBufferTexture = theEntry.texture;
} else {
theContext.releaseBuffer(bufferIdx);
}
}
}
if (theBufferTexture == nullptr) {
QSSGEffectContext &theContext(getEffectContext(inEffect));
auto theFB(m_resourceManager->allocateFrameBuffer());
auto theTexture(m_resourceManager->allocateTexture2D(theWidth, theHeight, resultFormat));
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);
theContext.m_allocatedBuffers.push_back(
QSSGAllocatedBufferEntry(inCommand.m_name, *theFB, *theTexture, inCommand.m_bufferFlags));
theBufferTexture = theTexture;
}
}
void QSSGEffectSystem::allocateImage(QSSGRenderEffect &inEffect,
const QSSGAllocateImage &inCommand,
qint32 inFinalWidth,
qint32 inFinalHeight)
{
QSSGRef<QSSGRenderImage2D> theImage;
const qint32 theWidth = qint32(QSSGRendererUtil::nextMultipleOf4(quint32(inFinalWidth * inCommand.m_sizeMultiplier)));
const qint32 theHeight = qint32(QSSGRendererUtil::nextMultipleOf4(quint32(inFinalHeight * inCommand.m_sizeMultiplier)));
Q_ASSERT(theWidth >= 0 && theHeight >= 0);
Q_ASSERT(inCommand.m_format != QSSGRenderTextureFormat::Unknown);
if (inEffect.m_context) {
QSSGEffectContext &theContext(*inEffect.m_context);
// size intentionally requiried every loop;
qint32 imageIdx = theContext.findImage(inCommand.m_name);
if (imageIdx < theContext.m_allocatedImages.size()) {
QSSGAllocatedImageEntry &theEntry(theContext.m_allocatedImages[imageIdx]);
QSSGTextureDetails theDetails = theEntry.texture->textureDetails();
if (theDetails.width == theWidth && theDetails.height == theHeight && theDetails.format == inCommand.m_format) {
theImage = theEntry.image;
} else {
theContext.releaseImage(imageIdx);
}
}
}
if (theImage == nullptr) {
QSSGEffectContext &theContext(getEffectContext(inEffect));
// allocate an immutable texture
auto theTexture(m_resourceManager->allocateTexture2D(theWidth, theHeight, inCommand.m_format, 1, true));
theTexture->setMagFilter(inCommand.m_filterOp);
theTexture->setMinFilter(static_cast<QSSGRenderTextureMinifyingOp>(inCommand.m_filterOp));
theTexture->setTextureWrapS(inCommand.m_texCoordOp);
theTexture->setTextureWrapT(inCommand.m_texCoordOp);
auto theImage = (m_resourceManager->allocateImage2D(theTexture, inCommand.m_access));
theContext.m_allocatedImages.push_back(
QSSGAllocatedImageEntry(inCommand.m_name, *theImage, *theTexture, inCommand.m_bufferFlags));
}
}
void QSSGEffectSystem::allocateDataBuffer(QSSGRenderEffect &inEffect, const QSSGAllocateDataBuffer &inCommand)
{
const size_t theBufferSize = size_t(inCommand.m_size);
Q_ASSERT(theBufferSize > 0);
QSSGRef<QSSGRenderDataBuffer> theDataBuffer;
QSSGRef<QSSGRenderDataBuffer> theDataWrapBuffer;
if (inEffect.m_context) {
QSSGEffectContext &theContext(*inEffect.m_context);
// size intentionally requiried every loop;
qint32 bufferIdx = theContext.findDataBuffer(inCommand.m_name);
if (bufferIdx < theContext.m_allocatedDataBuffers.size()) {
QSSGAllocatedDataBufferEntry &theEntry(theContext.m_allocatedDataBuffers[bufferIdx]);
if (theEntry.bufferType == inCommand.m_dataBufferType && theEntry.bufferData.size() == qint32(theBufferSize)) {
theDataBuffer = theEntry.dataBuffer;
} else {
// if type and size don't match something is wrong
Q_ASSERT(false);
}
}
}
if (theDataBuffer == nullptr) {
QSSGEffectContext &theContext(getEffectContext(inEffect));
const auto &theRenderContext(m_context->renderContext());
quint8 *initialData = reinterpret_cast<quint8 *>(::malloc(theBufferSize));
QSSGByteRef data(reinterpret_cast<quint8 *>(initialData), qint32(theBufferSize));
memset(initialData, 0x0L, theBufferSize);
if (Q_LIKELY(inCommand.m_dataBufferType == QSSGRenderBufferType::Storage)) {
theDataBuffer = new QSSGRenderStorageBuffer(theRenderContext, inCommand.m_name, QSSGRenderBufferUsageType::Dynamic, data, nullptr);
} else {
Q_ASSERT(false);
}
theContext.m_allocatedDataBuffers.push_back(QSSGAllocatedDataBufferEntry(inCommand.m_name,
*theDataBuffer,
inCommand.m_dataBufferType,
data,
inCommand.m_bufferFlags));
// create wrapper buffer
if (inCommand.m_dataBufferWrapType == QSSGRenderBufferType::Storage && !inCommand.m_wrapName.isEmpty() && theDataBuffer) {
theDataWrapBuffer = new QSSGRenderStorageBuffer(theRenderContext,
inCommand.m_wrapName,
QSSGRenderBufferUsageType::Dynamic,
data,
theDataBuffer.data());
theContext.m_allocatedDataBuffers.push_back(QSSGAllocatedDataBufferEntry(inCommand.m_wrapName,
*theDataWrapBuffer,
inCommand.m_dataBufferWrapType,
QSSGByteRef(),
inCommand.m_bufferFlags));
}
::free(initialData);
}
}
QSSGRef<QSSGRenderTexture2D> QSSGEffectSystem::findTexture(QSSGRenderEffect *inEffect, const QByteArray &inName)
{
if (inEffect->m_context) {
QSSGEffectContext &theContext(*inEffect->m_context);
qint32 bufferIdx = theContext.findBuffer(inName);
if (bufferIdx < theContext.m_allocatedBuffers.size())
return theContext.m_allocatedBuffers[bufferIdx].texture;
}
Q_ASSERT(false);
return nullptr;
}
QSSGRef<QSSGRenderFrameBuffer> QSSGEffectSystem::bindBuffer(QSSGRenderEffect &inEffect,
const QSSGBindBuffer &inCommand,
QMatrix4x4 &outMVP,
QVector2D &outDestSize)
{
QSSGRef<QSSGRenderFrameBuffer> theBuffer;
QSSGRef<QSSGRenderTexture2D> theTexture;
if (inEffect.m_context) {
QSSGEffectContext &theContext(*inEffect.m_context);
qint32 bufferIdx = theContext.findBuffer(inCommand.m_bufferName);
if (bufferIdx < theContext.m_allocatedBuffers.size()) {
theBuffer = theContext.m_allocatedBuffers[bufferIdx].frameBuffer;
theTexture = theContext.m_allocatedBuffers[bufferIdx].texture;
theContext.m_allocatedBuffers[bufferIdx].needsClear = false;
}
}
if (theBuffer == nullptr) {
qCCritical(INVALID_OPERATION,
"Effect %s: Failed to find buffer %s for bind",
inEffect.className,
inCommand.m_bufferName.constData());
QString errorMsg = QObject::tr("Failed to compile \"%1\" effect.\nConsider"
" removing it from the presentation.")
.arg(QString::fromLatin1(inEffect.className));
outMVP = QMatrix4x4();
return nullptr;
}
if (theTexture) {
QSSGRenderCamera::setupOrthographicCameraForOffscreenRender(*theTexture, outMVP);
QSSGTextureDetails theDetails(theTexture->textureDetails());
m_context->renderContext()->setViewport(QRect(0, 0, theDetails.width, theDetails.height));
outDestSize = QVector2D(float(theDetails.width), float(theDetails.height));
}
return theBuffer;
}
QSSGRef<QSSGEffectShader> QSSGEffectSystem::bindShader(const QSSGRenderEffect &effect,
const QSSGBindShader &inCommand)
{
const bool forceCompilation = effect.requiresCompilation;
auto key = TStrStrPair(inCommand.m_shaderPath, inCommand.m_shaderDefine);
auto theInsertResult = m_shaderMap.find(key);
const bool found = (theInsertResult != m_shaderMap.end());
if (!found)
theInsertResult = m_shaderMap.insert(key, QSSGRef<QSSGEffectShader>());
if (found || forceCompilation) {
auto theProgram = m_context->dynamicObjectSystem()
->getShaderProgram(inCommand.m_shaderPath,
inCommand.m_shaderDefine,
ShaderFeatureSetList(),
QSSGDynamicShaderProgramFlags(),
forceCompilation)
.first;
if (theProgram)
theInsertResult.value() = QSSGRef<QSSGEffectShader>(new QSSGEffectShader(theProgram));
}
if (theInsertResult.value()) {
const auto &theContext(m_context->renderContext());
theContext->setActiveShader(theInsertResult.value()->m_shader);
}
return theInsertResult.value();
}
void QSSGEffectSystem::doApplyInstanceValue(QSSGRenderEffect *inEffect,
const QByteArray &inPropertyName,
const QVariant &propertyValue,
QSSGRenderShaderDataType inPropertyType,
const QSSGRef<QSSGRenderShaderProgram> &inShader)
{
auto theConstant = inShader->shaderConstant(inPropertyName);
if (theConstant) {
if (theConstant->isCompatibleType(inPropertyType)) {
if (inPropertyType == QSSGRenderShaderDataType::Texture2D) {
const auto &theBufferManager(m_context->bufferManager());
bool needsAlphaMultiply = true;
const QSSGRenderEffect::TextureProperty *textureProperty = reinterpret_cast<QSSGRenderEffect::TextureProperty *>(propertyValue.value<void *>());
QSSGRenderImage *image = textureProperty->texImage;
if (image) {
const QString &imageSource = image->m_imagePath;
QSSGRef<QSSGRenderTexture2D> theTexture;
if (!imageSource.isEmpty()) {
QSSGRenderImageTextureData theTextureData = theBufferManager->loadRenderImage(imageSource,
QSSGRenderTextureFormat::Unknown);
needsAlphaMultiply = true;
theTexture = theTextureData.m_texture;
}
getEffectContext(*inEffect).setTexture(inShader,
inPropertyName,
theTexture,
needsAlphaMultiply,
m_textureStringBuilder,
m_textureStringBuilder2,
textureProperty);
}
} else if (inPropertyType == QSSGRenderShaderDataType::Image2D) {
// TODO:
// StaticAssert<sizeof(QString)
// == sizeof(QSSGRenderTexture2DPtr)>::valid_expression();
QSSGRef<QSSGRenderImage2D> theImage;
getEffectContext(*inEffect).setImage(inShader, inPropertyName, theImage);
} else if (inPropertyType == QSSGRenderShaderDataType::DataBuffer) {
// we don't handle this here
} else {
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,
"Effect ApplyInstanceValue command datatype "
"and shader datatypes differ for property %s",
inPropertyName.constData());
Q_ASSERT(false);
}
}
}
void QSSGEffectSystem::applyInstanceValue(QSSGRenderEffect &inEffect,
const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGApplyInstanceValue &inCommand)
{
if (!inCommand.m_propertyName.isNull()) {
const auto &properties = inEffect.properties;
const auto foundIt = std::find_if(properties.cbegin(), properties.cend(), [&inCommand](const QSSGRenderEffect::Property &prop) {
return (prop.name == inCommand.m_propertyName);
});
if (foundIt != properties.cend())
doApplyInstanceValue(&inEffect, foundIt->name, foundIt->value, foundIt->shaderDataType, inShader);
} else {
const auto &properties = inEffect.properties;
for (const auto &prop : properties)
doApplyInstanceValue(&inEffect, prop.name, prop.value, prop.shaderDataType, inShader);
const auto textProps = inEffect.textureProperties;
for (const auto &prop : textProps) {
doApplyInstanceValue(&inEffect,
prop.name,
QVariant::fromValue((void *)&prop),
prop.shaderDataType,
inShader);
}
}
}
void QSSGEffectSystem::applyValue(QSSGRenderEffect &inEffect,
const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGApplyValue &inCommand)
{
if (!inCommand.m_propertyName.isNull()) {
const auto &properties = inEffect.properties;
const auto foundIt = std::find_if(properties.cbegin(), properties.cend(), [&inCommand](const QSSGRenderEffect::Property &prop) {
return (prop.name == inCommand.m_propertyName);
});
if (foundIt != properties.cend())
doApplyInstanceValue(&inEffect, foundIt->name, inCommand.m_value, foundIt->shaderDataType, inShader);
}
}
bool QSSGEffectSystem::applyBlending(const QSSGApplyBlending &inCommand)
{
const auto &theContext(m_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);
return true;
}
QSSGEffectTextureData QSSGEffectSystem::applyBufferValue(QSSGRenderEffect *inEffect,
const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGApplyBufferValue &inCommand,
const QSSGRef<QSSGRenderTexture2D> &inSourceTexture,
const QSSGEffectTextureData &inCurrentSourceTexture)
{
QSSGEffectTextureData theTextureToBind;
if (!inCommand.m_bufferName.isEmpty()) {
if (inEffect->m_context) {
QSSGEffectContext &theContext(*inEffect->m_context);
qint32 bufferIdx = theContext.findBuffer(inCommand.m_bufferName);
if (bufferIdx < theContext.m_allocatedBuffers.size()) {
QSSGAllocatedBufferEntry &theEntry(theContext.m_allocatedBuffers[bufferIdx]);
if (theEntry.needsClear) {
auto theRenderContext(m_context->renderContext());
theRenderContext->setRenderTarget(theEntry.frameBuffer);
// Note that depth/stencil buffers need an explicit clear in their bind
// commands in order to ensure
// we clear the least amount of information possible.
if (theEntry.texture) {
QSSGRenderTextureFormat theTextureFormat = theEntry.texture->textureDetails().format;
if (theTextureFormat != QSSGRenderTextureFormat::Depth16 && theTextureFormat != QSSGRenderTextureFormat::Depth24
&& theTextureFormat != QSSGRenderTextureFormat::Depth32
&& theTextureFormat != QSSGRenderTextureFormat::Depth24Stencil8) {
QSSGRenderContextScopedProperty<QVector4D> __clearColor(*theRenderContext,
&QSSGRenderContext::clearColor,
&QSSGRenderContext::setClearColor,
QVector4D());
theRenderContext->clear(QSSGRenderClearValues::Color);
}
}
theEntry.needsClear = false;
}
theTextureToBind = QSSGEffectTextureData(theEntry.texture, false);
}
}
if (theTextureToBind.texture == nullptr) {
Q_ASSERT(false);
qCCritical(INVALID_OPERATION,
"Effect %s: Failed to find buffer %s for bind",
inEffect->className,
inCommand.m_bufferName.constData());
Q_ASSERT(false);
}
} else { // no name means bind the source
theTextureToBind = QSSGEffectTextureData(inSourceTexture, false);
}
if (!inCommand.m_paramName.isEmpty()) {
auto theConstant = inShader->shaderConstant(inCommand.m_paramName);
if (theConstant) {
if (theConstant->getShaderConstantType() != QSSGRenderShaderDataType::Texture2D) {
qCCritical(INVALID_OPERATION,
"Effect %s: Binding buffer to parameter %s that is not a texture",
inEffect->className,
inCommand.m_paramName.constData());
Q_ASSERT(false);
} else {
getEffectContext(*inEffect).setTexture(inShader,
inCommand.m_paramName,
theTextureToBind.texture,
theTextureToBind.needsAlphaMultiply,
m_textureStringBuilder,
m_textureStringBuilder2);
}
}
return inCurrentSourceTexture;
} else {
return theTextureToBind;
}
}
void QSSGEffectSystem::applyDepthValue(QSSGRenderEffect *inEffect,
const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGApplyDepthValue &inCommand,
const QSSGRef<QSSGRenderTexture2D> &inTexture)
{
auto theConstant = inShader->shaderConstant(inCommand.m_paramName);
if (theConstant) {
if (theConstant->getShaderConstantType() != QSSGRenderShaderDataType::Texture2D) {
qCCritical(INVALID_OPERATION,
"Effect %s: Binding buffer to parameter %s that is not a texture",
inEffect->className,
inCommand.m_paramName.constData());
Q_ASSERT(false);
} else {
getEffectContext(*inEffect).setTexture(inShader,
inCommand.m_paramName,
inTexture,
false,
m_textureStringBuilder,
m_textureStringBuilder2);
}
}
}
void QSSGEffectSystem::applyImageValue(QSSGRenderEffect *inEffect,
const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGApplyImageValue &inCommand)
{
QSSGAllocatedImageEntry theImageToBind;
if (!inCommand.m_imageName.isEmpty()) {
if (inEffect->m_context) {
QSSGEffectContext &theContext(*inEffect->m_context);
qint32 bufferIdx = theContext.findImage(inCommand.m_imageName);
if (bufferIdx < theContext.m_allocatedImages.size()) {
theImageToBind = QSSGAllocatedImageEntry(theContext.m_allocatedImages[bufferIdx]);
}
}
}
if (theImageToBind.image == nullptr) {
qCCritical(INVALID_OPERATION,
"Effect %s: Failed to find image %s for bind",
inEffect->className,
inCommand.m_imageName.constData());
Q_ASSERT(false);
}
if (!inCommand.m_paramName.isEmpty()) {
auto theConstant = inShader->shaderConstant(inCommand.m_paramName);
if (theConstant) {
if (inCommand.m_needSync) {
QSSGRenderBufferBarrierFlags flags(QSSGRenderBufferBarrierValues::TextureFetch
| QSSGRenderBufferBarrierValues::TextureUpdate);
inShader->renderContext()->setMemoryBarrier(flags);
}
if (theConstant->getShaderConstantType() == QSSGRenderShaderDataType::Image2D && !inCommand.m_bindAsTexture) {
getEffectContext(*inEffect).setImage(inShader,
inCommand.m_paramName,
theImageToBind.image);
} else if (theConstant->getShaderConstantType() == QSSGRenderShaderDataType::Texture2D && inCommand.m_bindAsTexture) {
getEffectContext(*inEffect).setTexture(inShader,
inCommand.m_paramName,
theImageToBind.texture,
false,
m_textureStringBuilder,
m_textureStringBuilder2);
} else {
qCCritical(INVALID_OPERATION,
"Effect %s: Binding buffer to parameter %s that is not a texture",
inEffect->className,
inCommand.m_paramName.constData());
Q_ASSERT(false);
}
}
}
}
void QSSGEffectSystem::applyDataBufferValue(QSSGRenderEffect *inEffect,
const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGApplyDataBufferValue &inCommand)
{
QSSGAllocatedDataBufferEntry theBufferToBind;
if (!inCommand.m_paramName.isEmpty()) {
if (inEffect->m_context) {
QSSGEffectContext &theContext(*inEffect->m_context);
qint32 bufferIdx = theContext.findDataBuffer(inCommand.m_paramName);
if (bufferIdx < theContext.m_allocatedDataBuffers.size()) {
theBufferToBind = QSSGAllocatedDataBufferEntry(theContext.m_allocatedDataBuffers[bufferIdx]);
if (theBufferToBind.needsClear) {
QSSGByteRef pData = theBufferToBind.dataBuffer->mapBuffer();
memset(pData.begin(), 0x0L, size_t(theBufferToBind.bufferData.size()));
theBufferToBind.dataBuffer->unmapBuffer();
theBufferToBind.needsClear = false;
}
}
}
if (theBufferToBind.dataBuffer == nullptr) {
qCCritical(INVALID_OPERATION,
"Effect %s: Failed to find buffer %s for bind",
inEffect->className,
inCommand.m_paramName.constData());
Q_ASSERT(false);
}
auto theConstant = inShader->shaderBuffer(inCommand.m_paramName);
if (theConstant)
getEffectContext(*inEffect).setDataBuffer(inShader, inCommand.m_paramName, theBufferToBind.dataBuffer);
}
}
void QSSGEffectSystem::applyRenderStateValue(const QSSGRef<QSSGRenderFrameBuffer> &inTarget,
const QSSGRef<QSSGRenderTexture2D> &inDepthStencilTexture,
const QSSGApplyRenderState &theCommand)
{
const auto &theContext(m_context->renderContext());
bool inEnable = theCommand.m_enabled;
switch (theCommand.m_renderState) {
case QSSGRenderState::StencilTest: {
if (inEnable && inTarget) {
inTarget->attach(QSSGRenderFrameBufferAttachment::DepthStencil, inDepthStencilTexture);
} else if (inTarget) {
inTarget->attach(QSSGRenderFrameBufferAttachment::DepthStencil, QSSGRenderTextureOrRenderBuffer());
}
theContext->setStencilTestEnabled(inEnable);
} break;
default:
Q_ASSERT(false);
break;
}
}
bool QSSGEffectSystem::compareDepthStencilState(QSSGRenderDepthStencilState &inState,
QSSGDepthStencil &inStencil)
{
QSSGRenderStencilFunction theFunction = inState.stencilFunction(QSSGCullFaceMode::Front);
QSSGRenderStencilOperation theOperation = inState.stencilOperation(QSSGCullFaceMode::Front);
return theFunction.m_function == inStencil.m_stencilFunction
&& theFunction.m_mask == inStencil.m_mask
&& theFunction.m_referenceValue == inStencil.m_reference
&& theOperation.m_stencilFail == inStencil.m_stencilFailOperation
&& theOperation.m_depthFail == inStencil.m_depthFailOperation
&& theOperation.m_depthPass == inStencil.m_depthPassOperation;
}
void QSSGEffectSystem::renderPass(QSSGEffectShader &inShader,
const QMatrix4x4 &inMVP,
const QSSGEffectTextureData &inSourceTexture,
const QSSGRef<QSSGRenderFrameBuffer> &inFrameBuffer,
QVector2D &inDestSize,
const QVector2D &inCameraClipRange,
const QSSGRef<QSSGRenderTexture2D> &inDepthStencil,
QSSGOption<QSSGDepthStencil> inDepthStencilCommand)
{
const auto &theContext(m_context->renderContext());
theContext->setRenderTarget(inFrameBuffer);
if (inDepthStencil && inFrameBuffer) {
inFrameBuffer->attach(QSSGRenderFrameBufferAttachment::DepthStencil, inDepthStencil);
if (inDepthStencilCommand.hasValue()) {
QSSGDepthStencil &theDepthStencil(*inDepthStencilCommand);
QSSGRenderClearFlags clearFlags;
if (theDepthStencil.m_glags.hasClearStencil())
clearFlags |= QSSGRenderClearValues::Stencil;
if (theDepthStencil.m_glags.hasClearDepth())
clearFlags |= QSSGRenderClearValues::Depth;
if (clearFlags)
theContext->clear(clearFlags);
QSSGRef<QSSGRenderDepthStencilState> targetState;
for (qint32 idx = 0, end = m_depthStencilStates.size(); idx < end && targetState == nullptr; ++idx) {
QSSGRef<QSSGRenderDepthStencilState> theState = m_depthStencilStates[idx];
if (compareDepthStencilState(*theState, theDepthStencil))
targetState = theState;
}
if (targetState == nullptr) {
QSSGRenderStencilFunction theFunctionArg(theDepthStencil.m_stencilFunction,
theDepthStencil.m_reference,
theDepthStencil.m_mask);
QSSGRenderStencilOperation theOpArg(theDepthStencil.m_stencilFailOperation,
theDepthStencil.m_depthFailOperation,
theDepthStencil.m_depthPassOperation);
targetState = new QSSGRenderDepthStencilState(theContext,
theContext->isDepthTestEnabled(),
theContext->isDepthWriteEnabled(),
theContext->depthFunction(),
true,
theFunctionArg,
theFunctionArg,
theOpArg,
theOpArg);
m_depthStencilStates.push_back(targetState);
}
theContext->setDepthStencilState(targetState);
}
}
theContext->setActiveShader(inShader.m_shader);
inShader.m_mvp.set(inMVP);
if (inSourceTexture.texture) {
inShader.m_textureEntry.set(inSourceTexture.texture,
inSourceTexture.needsAlphaMultiply,
nullptr);
} else {
qCCritical(INTERNAL_ERROR, "Failed to setup pass due to null source texture");
Q_ASSERT(false);
}
inShader.m_fragColorAlphaSettings.set(QVector2D(1.0f, 0.0f));
inShader.m_destSize.set(inDestSize);
if (inShader.m_appFrame.isValid())
inShader.m_appFrame.set(float(m_context->frameCount()));
if (inShader.m_fps.isValid())
inShader.m_fps.set(float(m_context->getFPS().first));
if (inShader.m_cameraClipRange.isValid())
inShader.m_cameraClipRange.set(inCameraClipRange);
m_context->renderer()->renderQuad();
if (inDepthStencil && inFrameBuffer) {
inFrameBuffer->attach(QSSGRenderFrameBufferAttachment::DepthStencil, QSSGRenderTextureOrRenderBuffer());
theContext->setDepthStencilState(m_defaultStencilState);
}
}
// NOTE!!!!: The render target "inTarget" needs to be a copy here, as we will change it
// when calling setRenderTarget()!
void QSSGEffectSystem::doRenderEffect(QSSGRenderEffect *inEffect,
const QSSGRef<QSSGRenderTexture2D> &inSourceTexture,
QMatrix4x4 &inMVP,
const QSSGRef<QSSGRenderFrameBuffer> inTarget /*Intentional copy!!!*/,
bool inEnableBlendWhenRenderToTarget,
const QSSGRef<QSSGRenderTexture2D> &inDepthTexture,
const QSSGRef<QSSGRenderTexture2D> &inDepthStencilTexture,
const QVector2D &inCameraClipRange)
{
// Run through the effect commands and render the effect.
// QSSGRenderTexture2D* theCurrentTexture(&inSourceTexture);
const auto &theContext = m_context->renderContext();
// Context variables that are updated during the course of a pass.
QSSGEffectTextureData theCurrentSourceTexture(inSourceTexture, false);
QSSGRef<QSSGRenderTexture2D> theCurrentDepthStencilTexture;
QSSGRef<QSSGRenderFrameBuffer> theCurrentRenderTarget(inTarget);
QSSGRef<QSSGEffectShader> theCurrentShader;
QRect theOriginalViewport(theContext->viewport());
bool wasScissorEnabled = theContext->isScissorTestEnabled();
bool wasBlendingEnabled = theContext->isBlendingEnabled();
// save current blending setup
QSSGRenderBlendFunctionArgument theBlendFunc = theContext->blendFunction();
QSSGRenderBlendEquationArgument theBlendEqu = theContext->blendEquation();
bool intermediateBlendingEnabled = false;
QSSGTextureDetails theDetails(inSourceTexture->textureDetails());
const qint32 theFinalWidth = theDetails.width;
const qint32 theFinalHeight = theDetails.height;
QVector2D theDestSize;
{
// Ensure no matter the command run goes we replace the rendering system to some
// semblance of the approprate
// setting.
QSSGRenderContextScopedProperty<const QSSGRef<QSSGRenderFrameBuffer> &> __framebuffer(*theContext,
&QSSGRenderContext::renderTarget,
&QSSGRenderContext::setRenderTarget);
QSSGRenderContextScopedProperty<QRect> __viewport(*theContext, &QSSGRenderContext::viewport, &QSSGRenderContext::setViewport);
QSSGRenderContextScopedProperty<bool> __scissorEnabled(*theContext,
&QSSGRenderContext::isScissorTestEnabled,
&QSSGRenderContext::setScissorTestEnabled);
QSSGRenderContextScopedProperty<bool> __stencilTest(*theContext,
&QSSGRenderContext::isStencilTestEnabled,
&QSSGRenderContext::setStencilTestEnabled);
QSSGRenderContextScopedProperty<QSSGRenderBoolOp> __depthFunction(*theContext,
&QSSGRenderContext::depthFunction,
&QSSGRenderContext::setDepthFunction);
QSSGOption<QSSGDepthStencil> theCurrentDepthStencil;
theContext->setScissorTestEnabled(false);
theContext->setBlendingEnabled(false);
theContext->setCullingEnabled(false);
theContext->setDepthTestEnabled(false);
theContext->setDepthWriteEnabled(false);
QMatrix4x4 theMVP;
const auto &theCommands = inEffect->commands;
for (const auto &theCommand : theCommands) {
switch (theCommand->m_type) {
case CommandType::AllocateBuffer:
allocateBuffer(*inEffect,
static_cast<const QSSGAllocateBuffer &>(*theCommand),
theFinalWidth,
theFinalHeight,
theDetails.format);
break;
case CommandType::AllocateImage:
allocateImage(*inEffect, static_cast<const QSSGAllocateImage &>(*theCommand), theFinalWidth, theFinalHeight);
break;
case CommandType::AllocateDataBuffer:
allocateDataBuffer(*inEffect, static_cast<const QSSGAllocateDataBuffer &>(*theCommand));
break;
case CommandType::BindBuffer:
theCurrentRenderTarget = bindBuffer(*inEffect, static_cast<const QSSGBindBuffer &>(*theCommand), theMVP, theDestSize);
break;
case CommandType::BindTarget: {
m_context->renderContext()->setRenderTarget(inTarget);
theCurrentRenderTarget = inTarget;
theMVP = inMVP;
theContext->setViewport(theOriginalViewport);
theDestSize = QVector2D(float(theFinalWidth), float(theFinalHeight));
// This isn't necessary if we are rendering to an offscreen buffer and not
// compositing
// with other objects.
if (inEnableBlendWhenRenderToTarget) {
theContext->setBlendingEnabled(wasBlendingEnabled);
theContext->setScissorTestEnabled(wasScissorEnabled);
// The blending setup was done before we apply the effect
theContext->setBlendFunction(theBlendFunc);
theContext->setBlendEquation(theBlendEqu);
}
} break;
case CommandType::BindShader:
theCurrentShader = bindShader(*inEffect, static_cast<const QSSGBindShader &>(*theCommand));
break;
case CommandType::ApplyInstanceValue:
if (theCurrentShader)
applyInstanceValue(*inEffect, theCurrentShader->m_shader, static_cast<const QSSGApplyInstanceValue &>(*theCommand));
break;
case CommandType::ApplyValue:
if (theCurrentShader)
applyValue(*inEffect, theCurrentShader->m_shader, static_cast<const QSSGApplyValue &>(*theCommand));
break;
case CommandType::ApplyBlending:
intermediateBlendingEnabled = applyBlending(static_cast<const QSSGApplyBlending &>(*theCommand));
break;
case CommandType::ApplyBufferValue:
if (theCurrentShader)
theCurrentSourceTexture = applyBufferValue(inEffect,
theCurrentShader->m_shader,
static_cast<const QSSGApplyBufferValue &>(*theCommand),
inSourceTexture,
theCurrentSourceTexture);
break;
case CommandType::ApplyDepthValue:
if (theCurrentShader)
applyDepthValue(inEffect, theCurrentShader->m_shader, static_cast<const QSSGApplyDepthValue &>(*theCommand), inDepthTexture);
if (!inDepthTexture) {
qCCritical(INVALID_OPERATION,
"Depth value command detected but no "
"depth buffer provided for effect %s",
inEffect->className);
Q_ASSERT(false);
}
break;
case CommandType::ApplyImageValue:
if (theCurrentShader)
applyImageValue(inEffect, theCurrentShader->m_shader, static_cast<const QSSGApplyImageValue &>(*theCommand));
break;
case CommandType::ApplyDataBufferValue:
if (theCurrentShader)
applyDataBufferValue(inEffect, theCurrentShader->m_shader, static_cast<const QSSGApplyDataBufferValue &>(*theCommand));
break;
case CommandType::DepthStencil: {
const QSSGDepthStencil &theDepthStencil = static_cast<const QSSGDepthStencil &>(*theCommand);
theCurrentDepthStencilTexture = findTexture(inEffect, theDepthStencil.m_bufferName);
if (theCurrentDepthStencilTexture)
theCurrentDepthStencil = theDepthStencil;
} break;
case CommandType::Render:
if (theCurrentShader && theCurrentSourceTexture.texture) {
renderPass(*theCurrentShader,
theMVP,
theCurrentSourceTexture,
theCurrentRenderTarget,
theDestSize,
inCameraClipRange,
theCurrentDepthStencilTexture,
theCurrentDepthStencil);
}
// Reset the source texture regardless
theCurrentSourceTexture = QSSGEffectTextureData(inSourceTexture, false);
theCurrentDepthStencilTexture = nullptr;
theCurrentDepthStencil = QSSGOption<QSSGDepthStencil>();
// reset intermediate blending state
if (intermediateBlendingEnabled) {
theContext->setBlendingEnabled(false);
intermediateBlendingEnabled = false;
}
break;
case CommandType::ApplyRenderState:
applyRenderStateValue(theCurrentRenderTarget,
inDepthStencilTexture,
static_cast<const QSSGApplyRenderState &>(*theCommand));
break;
default:
Q_ASSERT(false);
break;
}
}
inEffect->requiresCompilation = false;
// reset to default stencil state
if (inDepthStencilTexture)
theContext->setDepthStencilState(m_defaultStencilState);
// Release any per-frame buffers
if (inEffect->m_context) {
QSSGEffectContext &theContext(*inEffect->m_context);
// Query for size on every loop intentional
for (qint32 idx = 0; idx < theContext.m_allocatedBuffers.size(); ++idx) {
if (!theContext.m_allocatedBuffers[idx].flags.isSceneLifetime()) {
theContext.releaseBuffer(idx);
--idx;
}
}
for (qint32 idx = 0; idx < theContext.m_allocatedImages.size(); ++idx) {
if (!theContext.m_allocatedImages[idx].flags.isSceneLifetime()) {
theContext.releaseImage(idx);
--idx;
}
}
}
}
}
QSSGRef<QSSGRenderTexture2D> QSSGEffectSystem::renderEffect(const QSSGEffectRenderArgument &inRenderArgument)
{
QMatrix4x4 theMVP;
QSSGRenderCamera::setupOrthographicCameraForOffscreenRender(*inRenderArgument.m_colorBuffer, theMVP);
// setup a render target
const auto &theContext(m_context->renderContext());
const auto &theManager(m_context->resourceManager());
QSSGRenderContextScopedProperty<const QSSGRef<QSSGRenderFrameBuffer> &> __framebuffer(*theContext,
&QSSGRenderContext::renderTarget,
&QSSGRenderContext::setRenderTarget);
QSSGTextureDetails theDetails(inRenderArgument.m_colorBuffer->textureDetails());
const qint32 theFinalWidth = qint32(QSSGRendererUtil::nextMultipleOf4(quint32(theDetails.width)));
const qint32 theFinalHeight = qint32(QSSGRendererUtil::nextMultipleOf4(quint32(theDetails.height)));
Q_ASSERT(theFinalWidth >= 0 && theFinalHeight >= 0);
auto theBuffer = theManager->allocateFrameBuffer();
QSSGRenderTextureFormat theOutputFormat = inRenderArgument.m_effect->outputFormat;
// If the effect doesn't define an output format, use the same format as the input texture
if (theOutputFormat == QSSGRenderTextureFormat::Unknown)
theOutputFormat = theDetails.format;
auto theTargetTexture = theManager->allocateTexture2D(theFinalWidth, theFinalHeight, theOutputFormat);
theBuffer->attach(QSSGRenderFrameBufferAttachment::Color0, theTargetTexture);
theContext->setRenderTarget(theBuffer);
QSSGRenderContextScopedProperty<QRect> __viewport(*theContext,
&QSSGRenderContext::viewport,
&QSSGRenderContext::setViewport,
QRect(0, 0, theFinalWidth, theFinalHeight));
QSSGRenderContextScopedProperty<bool> __scissorEnable(*theContext,
&QSSGRenderContext::isScissorTestEnabled,
&QSSGRenderContext::setScissorTestEnabled,
false);
doRenderEffect(inRenderArgument.m_effect,
inRenderArgument.m_colorBuffer,
theMVP,
m_context->renderContext()->renderTarget(),
false,
inRenderArgument.m_depthTexture,
inRenderArgument.m_depthStencilBuffer,
inRenderArgument.m_cameraClipRange);
theBuffer->attach(QSSGRenderFrameBufferAttachment::Color0, QSSGRenderTextureOrRenderBuffer());
theManager->release(theBuffer);
return theTargetTexture;
}
bool QSSGEffectSystem::renderEffect(const QSSGEffectRenderArgument &inRenderArgument,
QMatrix4x4 &inMVP,
bool inEnableBlendWhenRenderToTarget)
{
doRenderEffect(inRenderArgument.m_effect,
inRenderArgument.m_colorBuffer,
inMVP,
m_context->renderContext()->renderTarget(),
inEnableBlendWhenRenderToTarget,
inRenderArgument.m_depthTexture,
inRenderArgument.m_depthStencilBuffer,
inRenderArgument.m_cameraClipRange);
return true;
}
void QSSGEffectSystem::releaseEffectContext(QSSGEffectContext *inContext)
{
if (inContext == nullptr)
return;
for (qint32 idx = 0, end = m_contexts.size(); idx < end; ++idx) {
if (m_contexts[idx] == inContext) {
{ // replace_with_last
m_contexts[idx] = m_contexts.back();
m_contexts.pop_back();
}
}
}
}
void QSSGEffectSystem::resetEffectFrameData(QSSGEffectContext &inContext)
{ // Query for size on every loop intentional
for (qint32 idx = 0; idx < inContext.m_allocatedBuffers.size(); ++idx) {
QSSGAllocatedBufferEntry &theBuffer(inContext.m_allocatedBuffers[idx]);
if (theBuffer.flags.isSceneLifetime())
theBuffer.needsClear = true;
}
for (qint32 idx = 0; idx < inContext.m_allocatedDataBuffers.size(); ++idx) {
QSSGAllocatedDataBufferEntry &theDataBuffer(inContext.m_allocatedDataBuffers[idx]);
if (theDataBuffer.flags.isSceneLifetime())
theDataBuffer.needsClear = true;
}
}
void QSSGEffectSystem::setShaderData(const QByteArray &path,
const char *data,
const char *inShaderType,
const char *inShaderVersion,
bool inHasGeomShader,
bool inIsComputeShader)
{
m_context->dynamicObjectSystem()->setShaderData(path,
data,
inShaderType,
inShaderVersion,
inHasGeomShader,
inIsComputeShader);
}
void QSSGEffectSystem::init()
{
const auto &theContext(m_context->renderContext());
m_resourceManager = m_context->resourceManager();
// create default stencil state
QSSGRenderStencilFunction stencilDefaultFunc(QSSGRenderBoolOp::AlwaysTrue, 0x0, 0xFF);
QSSGRenderStencilOperation stencilDefaultOp(QSSGRenderStencilOp::Keep, QSSGRenderStencilOp::Keep, QSSGRenderStencilOp::Keep);
m_defaultStencilState = new QSSGRenderDepthStencilState(theContext,
theContext->isDepthTestEnabled(),
theContext->isDepthWriteEnabled(),
theContext->depthFunction(),
theContext->isStencilTestEnabled(),
stencilDefaultFunc,
stencilDefaultFunc,
stencilDefaultOp,
stencilDefaultOp);
}
QSSGRef<QSSGResourceManager> QSSGEffectSystem::getResourceManager()
{
return m_resourceManager;
}
QSSGEffectTextureData::QSSGEffectTextureData(const QSSGRef<QSSGRenderTexture2D> &inTexture,
bool inNeedsMultiply)
: texture(inTexture),
needsAlphaMultiply(inNeedsMultiply)
{
}
QT_END_NAMESPACE