summaryrefslogtreecommitdiffstats
path: root/src/render/backend/rendershaderdata.cpp
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2015-01-09 10:10:13 +0100
committerSean Harmer <sean.harmer@kdab.com>2015-01-18 15:30:47 +0100
commit074263ba87e73c67e355ee5dc5037477090f6957 (patch)
tree18bf0672f9c93c5a1a64d50d6723791f50637f6f /src/render/backend/rendershaderdata.cpp
parent67ffbd60ed6f383f3629a1848587da049c3ff3c9 (diff)
RenderShaderData handling entirely reworked
- UBO created for each Shader/ShaderData - We deal with nested QShaderData by looking for QNodeId only instead of looking for QShaderData* - We update only the values that have changed into the UBO - A CleanupFrameJob was added to properly clear all RenderShaderData set for updates after a frame - RenderShaderData is cleared of all UBO rendering logic - BlockToUBO contains all the updated properties for a given UBO in a RenderView, that solves the issue of properties being updated in the RenderShaderData while performing a draw call Note: the QShaderData transformed properties were commented for now deferred-examples and playground qml updates to follow Change-Id: I8ecf155288c154f41b505cf465d31f5eb8a71b5d Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Diffstat (limited to 'src/render/backend/rendershaderdata.cpp')
-rw-r--r--src/render/backend/rendershaderdata.cpp215
1 files changed, 135 insertions, 80 deletions
diff --git a/src/render/backend/rendershaderdata.cpp b/src/render/backend/rendershaderdata.cpp
index 7d7639884..0466f6e67 100644
--- a/src/render/backend/rendershaderdata.cpp
+++ b/src/render/backend/rendershaderdata.cpp
@@ -46,6 +46,9 @@
#include <QMetaObject>
#include <Qt3DCore/qscenepropertychange.h>
#include <private/qgraphicscontext_p.h>
+#include <private/qbackendnode_p.h>
+#include <private/uniformbuffer_p.h>
+#include <private/managers_p.h>
QT_BEGIN_NAMESPACE
@@ -53,12 +56,13 @@ namespace Qt3D {
namespace Render {
+QList<QNodeId> RenderShaderData::m_updatedShaderData;
+
RenderShaderData::RenderShaderData()
: QBackendNode()
- , m_initialized(false)
- , m_mutex(new QMutex)
+ , m_manager(Q_NULLPTR)
+ , m_mutex(new QMutex())
{
- m_needsBufferUpdate.fetchAndStoreOrdered(1);
}
RenderShaderData::~RenderShaderData()
@@ -71,112 +75,163 @@ void RenderShaderData::updateFromPeer(QNode *peer)
m_properties.clear();
const QShaderData *shaderData = static_cast<const QShaderData *>(peer);
m_propertyReader = shaderData->propertyReader();
- if (!m_propertyReader.isNull()) {
- const QMetaObject *metaObject = shaderData->metaObject();
- const int propertyOffset = QShaderData::staticMetaObject.propertyOffset();
- const int propertyCount = metaObject->propertyCount();
-
- for (int i = propertyOffset; i < propertyCount; ++i) {
- const QMetaProperty property = metaObject->property(i);
- if (strcmp(property.name(), "data") == 0 || strcmp(property.name(), "childNodes") == 0) // We don't handle default Node properties
- continue;
- // We should be making clones of inner QShaderData properties
- // TO DO: If the property is a QShaderData as well, we should register ourself as an observer of updates
- // otherwise we won't ever be notified about a nested property change
- // Q_D(QBackendNode);
- // d->m_arbiter->registerObserver(QBackendNodePrivate::get(this), subQShaderData->nodeId(),NodeUpdated)
- m_properties.insert(QString::fromLatin1(property.name()),
- m_propertyReader->readProperty(shaderData->property(property.name())));
- }
- }
+ if (!m_propertyReader.isNull())
+ readPeerProperties(const_cast<QShaderData *>(shaderData));
}
-void RenderShaderData::initialize(const ShaderUniformBlock &block)
+// Call by cleanup job (single thread)
+void RenderShaderData::clearUpdate()
{
- m_block = block;
- Q_ASSERT(m_block.m_size > 0);
- // Allocate CPU side buffer
- m_data = QByteArray(m_block.m_size, 0);
- m_initialized = true;
- // Force UBO buffer to be allocated on the GPU when apply is called
- m_needsBufferUpdate.fetchAndStoreOrdered(1);
+ m_updatedProperties.clear();
+ const QHash<QString, QVariant>::const_iterator end = m_nestedShaderDataProperties.end();
+ QHash<QString, QVariant>::const_iterator it = m_nestedShaderDataProperties.begin();
+
+ while (it != end) {
+ if (it.value().userType() == QMetaType::QVariantList) {
+ Q_FOREACH (const QVariant &v, it.value().value<QVariantList>()) {
+ RenderShaderData *nested = m_manager->lookupResource(v.value<QNodeId>());
+ if (nested != Q_NULLPTR)
+ nested->clearUpdate();
+ }
+ } else {
+ RenderShaderData *nested = m_manager->lookupResource(it.value().value<QNodeId>());
+ if (nested != Q_NULLPTR)
+ nested->clearUpdate();
+ }
+ ++it;
+ }
}
-void RenderShaderData::appendActiveProperty(const QString &propertyName, const ShaderUniform &description)
+// Called by renderview jobs (several concurrent threads)
+bool RenderShaderData::needsUpdate()
{
- m_activeProperties.insert(propertyName, description);
+ // We can't perform this only once as we don't know if we would be call as the root or a
+ // nested RenderShaderData
+ QMutexLocker lock(m_mutex);
+ const QHash<QString, QVariant>::const_iterator end = m_nestedShaderDataProperties.end();
+ QHash<QString, QVariant>::const_iterator it = m_nestedShaderDataProperties.begin();
+
+ while (it != end) {
+ if (it.value().userType() == QMetaType::QVariantList) {
+ QVariantList updatedNodes;
+ Q_FOREACH (const QVariant &v, it.value().value<QVariantList>()) {
+ RenderShaderData *nested = m_manager->lookupResource(v.value<QNodeId>());
+ if (nested != Q_NULLPTR && nested->needsUpdate())
+ updatedNodes << v;
+ }
+ if (!updatedNodes.empty())
+ m_updatedProperties.insert(it.key(), updatedNodes);
+ } else {
+ RenderShaderData *nested = m_manager->lookupResource(it.value().value<QNodeId>());
+ if (nested != Q_NULLPTR && nested->needsUpdate())
+ m_updatedProperties.insert(it.key(), it.value());
+ }
+ ++it;
+ }
+ return m_updatedProperties.size() > 0;
}
-// TO DO: This isn't the cleanest solution but for now, this helps us getting UBOs working
-// and we'll be able to correct that easily once we have something working
-void RenderShaderData::setActiveUniformValues(const QHash<QString, QVariant> &newValues)
+// This will add the RenderShaderData to be cleared from updates at the end of the frame
+// by the cleanup job
+// Called by renderview jobs (several concurrent threads)
+void RenderShaderData::addToClearUpdateList()
{
QMutexLocker lock(m_mutex);
- m_activeUniformToValues = newValues;
+ if (!RenderShaderData::m_updatedShaderData.contains(peerUuid()))
+ RenderShaderData::m_updatedShaderData.append(peerUuid());
}
-void RenderShaderData::updateUniformBuffer(QGraphicsContext *ctx)
+const int qNodeIdTypeId = qMetaTypeId<QNodeId>();
+
+void RenderShaderData::readPeerProperties(QShaderData *shaderData)
{
- const QHash<QString, ShaderUniform>::const_iterator uniformsEnd = m_activeProperties.end();
- QHash<QString, ShaderUniform>::const_iterator uniformsIt = m_activeProperties.begin();
-
- if (!m_ubo.isCreated()) {
- m_ubo.create(ctx);
- m_ubo.allocate(ctx, m_block.m_size);
- // We need to fill the UBO the first time it is created
- while (uniformsIt != uniformsEnd) {
- if (m_activeUniformToValues.contains(uniformsIt.key()))
- ctx->buildUniformBuffer(m_activeUniformToValues.value(uniformsIt.key()), uniformsIt.value(), m_data);
- ++uniformsIt;
- }
- // Upload the whole buffer to GPU for the first time
- m_ubo.update(ctx, m_data.constData(), m_block.m_size);
- }
- uniformsIt = m_activeProperties.begin();
- while (uniformsIt != uniformsEnd) {
- if (m_activeUniformToValues.contains(uniformsIt.key())) {
- // Update CPU side sub buffer
- ctx->buildUniformBuffer(m_activeUniformToValues.value(uniformsIt.key()), uniformsIt.value(), m_data);
- // Upload sub buffer to GPU
- m_ubo.update(ctx, m_data.constData() + uniformsIt.value().m_offset, uniformsIt.value().m_rawByteSize, uniformsIt.value().m_offset);
+ const QMetaObject *metaObject = shaderData->metaObject();
+ const int propertyOffset = QShaderData::staticMetaObject.propertyOffset();
+ const int propertyCount = metaObject->propertyCount();
+
+ for (int i = propertyOffset; i < propertyCount; ++i) {
+ const QMetaProperty property = metaObject->property(i);
+ if (strcmp(property.name(), "data") == 0 || strcmp(property.name(), "childNodes") == 0) // We don't handle default Node properties
+ continue;
+ QVariant propertyValue = m_propertyReader->readProperty(shaderData->property(property.name()));
+ QString propertyName = QString::fromLatin1(property.name());
+
+ m_properties.insert(propertyName, propertyValue);
+
+ // We check if the property is a QNodeId or QList<QNodeId> so that we can
+ // check nested QShaderData for update
+ if (propertyValue.userType() == qNodeIdTypeId) {
+ m_nestedShaderDataProperties.insert(propertyName, propertyValue);
+ } else if (propertyValue.userType() == QMetaType::QVariantList) {
+ QVariantList list = propertyValue.value<QVariantList>();
+ if (list.count() > 0 && list.at(0).userType() == qNodeIdTypeId)
+ m_nestedShaderDataProperties.insert(propertyName, propertyValue);
}
- ++uniformsIt;
}
- m_updatedProperties.clear();
}
-// Make sure QGraphicsContext::bindUniformBlock is called prior to this
-void RenderShaderData::apply(QGraphicsContext *ctx, int bindingPoint)
+void RenderShaderData::setManager(ShaderDataManager *manager)
{
- // Upload new data to GPU if it has changed
- if (m_needsBufferUpdate.fetchAndStoreOrdered(0)) {
- QMutexLocker lock(m_mutex);
- // We lock in case m_properties is being updated at the same time
- updateUniformBuffer(ctx);
- }
- m_ubo.bindToUniformBlock(ctx, bindingPoint);
+ m_manager = manager;
}
-// We have a concurrency issue here, updateUniformBuffer might be called
-// by the RenderThread while sceneChangeEvent is being called by the
-// AspectThread / Job thread for transformed properties
void RenderShaderData::sceneChangeEvent(const QSceneChangePtr &e)
{
- // TO DO check if property update comes from the root QShaderData
- // or if it comes from one of the nested QShaderData
- if (!m_propertyReader.isNull() && e->type() == NodeUpdated) {
+ if (!m_propertyReader.isNull()) {
QScenePropertyChangePtr propertyChange = qSharedPointerCast<QScenePropertyChange>(e);
QString propertyName = QString::fromLatin1(propertyChange->propertyName());
- QMutexLocker lock(m_mutex);
- // lock to update m_properties;
- if (m_properties.contains(propertyName)) {
+ switch (e->type()) {
+ case NodeUpdated: {
+ // Note we aren't notified about nested QShaderData in this call
+ // only scalar / vec properties
+ if (m_properties.contains(propertyName)) {
+ const QVariant &val = m_propertyReader->readProperty(propertyChange->value());
+ m_properties.insert(propertyName, val);
+ m_updatedProperties.insert(propertyName, val);
+ }
+ break;
+ }
+ case NodeAdded: {
m_properties.insert(propertyName, m_propertyReader->readProperty(propertyChange->value()));
- m_updatedProperties.append(propertyName);
- m_needsBufferUpdate.fetchAndStoreOrdered(1);
+ m_nestedShaderDataProperties.insert(propertyName, propertyChange->value());
+ break;
+ }
+ case NodeRemoved: {
+ if (m_properties.contains(propertyName)) {
+ m_properties.remove(propertyName);
+ m_nestedShaderDataProperties.remove(propertyName);
+ }
+ break;
+ }
+ default:
+ break;
}
}
}
+RenderShaderDataFunctor::RenderShaderDataFunctor(ShaderDataManager *manager)
+ : m_manager(manager)
+{
+}
+
+QBackendNode *RenderShaderDataFunctor::create(QNode *frontend) const
+{
+ RenderShaderData *backend = m_manager->getOrCreateResource(frontend->id());
+ backend->setManager(m_manager);
+ backend->setPeer(frontend);
+ return backend;
+}
+
+QBackendNode *RenderShaderDataFunctor::get(QNode *frontend) const
+{
+ return m_manager->lookupResource(frontend->id());
+}
+
+void RenderShaderDataFunctor::destroy(QNode *frontend) const
+{
+ m_manager->releaseResource(frontend->id());
+}
+
} // Render
} // Qt3D