summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2020-04-30 16:09:46 +0200
committerPaul Lemire <paul.lemire@kdab.com>2020-06-04 14:02:33 +0200
commit80e73b0a8537c794be187e8847f84d376c30bd66 (patch)
treea28c70f7985678880f16982a7da1260beebc0c06
parent0c9c4e163458f99ac350aef4979754cbe147dac3 (diff)
Leverage RV cache to reuse render commands when possible
- Only perform render command filtering when needed (when camera has moved usually) - Keep RenderCommand around so that we only update them if needed: -> We keep a double set of commands we per RV and switch in between every frame - Introduce EntityRenderCommandDataView as a wrapper around RenderCommands -> Use std::vector for RenderCommand storage (avoids hidden detachments) -> Filter and sort indices into the RenderCommand vector rather than the commands directly - Cleanup RenderView -> Remove InnerData field -> Hide direct RenderCommand access - Next steps -> Only update uniforms that need to be updated -> Most likely lights/standard uniforms won't all change every frame -> Only update light sources for entity when needed Change-Id: I153822a16c0989a8ac5b83d756385056c96572aa Reviewed-by: Mike Krus <mike.krus@kdab.com> (cherry picked from commit 59345bc1a733ee035b342feecadc923e062a850c)
-rw-r--r--src/plugins/renderers/opengl/debug/imguirenderer.cpp17
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp10
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h12
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp12
-rw-r--r--src/plugins/renderers/opengl/renderer/commandexecuter.cpp4
-rw-r--r--src/plugins/renderers/opengl/renderer/rendercommand_p.h68
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.cpp24
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer_p.h4
-rw-r--r--src/plugins/renderers/opengl/renderer/renderercache_p.h15
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview.cpp483
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview_p.h172
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp185
-rw-r--r--tests/auto/render/opengl/renderviews/tst_renderviews.cpp223
13 files changed, 735 insertions, 494 deletions
diff --git a/src/plugins/renderers/opengl/debug/imguirenderer.cpp b/src/plugins/renderers/opengl/debug/imguirenderer.cpp
index 0acee945a..f193d624e 100644
--- a/src/plugins/renderers/opengl/debug/imguirenderer.cpp
+++ b/src/plugins/renderers/opengl/debug/imguirenderer.cpp
@@ -201,19 +201,18 @@ void ImGuiRenderer::renderDebugOverlay(const QVector<RenderView *> &renderViews,
QSet<HGeometryRenderer> inUseGeometries;
QSet<Qt3DCore::QNodeId> inUseTextures;
for (int j=0; j<renderViewsCount; j++) {
- const auto &commands = renderViews.at(j)->commands();
- nCommands += commands.size();
- for (int k=0; k<commands.size(); k++) {
- const RenderCommand &command = commands.at(k);
+ RenderView *rv = renderViews.at(j);
+ nCommands += rv->commandCount();
+ rv->forEachCommand([&] (const RenderCommand &command) {
if (command.m_type != RenderCommand::Draw)
- continue;
+ return;
nVertices += command.m_primitiveCount;
nPrimitives += vertexToPrimitiveCount(command.m_primitiveType, command.m_primitiveCount);
inUseGeometries.insert(command.m_geometryRenderer);
const auto &textures = command.m_parameterPack.textures();
for (const auto &ns: textures)
inUseTextures.insert(ns.nodeId);
- }
+ });
}
auto columnNumber = [](int i) {
@@ -340,8 +339,8 @@ void ImGuiRenderer::showRenderDetails(const QVector<RenderView *> &renderViews)
ImGui::Text("Clear Depth Value: %f", static_cast<double>(view->clearDepthValue()));
ImGui::Text("Clear Stencil Value: %d", view->clearStencilValue());
int j = 1;
- const auto commands = view->commands();
- for (const RenderCommand &command: commands) {
+
+ view->forEachCommand([&] (const RenderCommand &command) {
GeometryRenderer *rGeometryRenderer = m_renderer->nodeManagers()->data<GeometryRenderer, GeometryRendererManager>(command.m_geometryRenderer);
QString label = QString(QLatin1String("Command %1 {%2}")).arg(QString::number(j++), QString::number(rGeometryRenderer->peerId().id()));
if (ImGui::TreeNode(label.toLatin1().data())) {
@@ -352,7 +351,7 @@ void ImGuiRenderer::showRenderDetails(const QVector<RenderView *> &renderViews)
ImGui::Text("# Instances: %d", command.m_instanceCount);
ImGui::TreePop();
}
- }
+ });
ImGui::TreePop();
ImGui::Separator();
}
diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
index 9bbdf50fd..0c84fde9e 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
+++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
@@ -70,7 +70,7 @@ public:
bool RenderViewCommandUpdaterJobPrivate::isRequired() const
{
Q_Q(const RenderViewCommandUpdaterJob);
- return q->m_renderView && !q->m_renderView->noDraw() && q->m_count > 0;
+ return q->m_renderView && !q->m_renderView->noDraw() && q->m_renderablesSubView.count > 0;
}
void RenderViewCommandUpdaterJobPrivate::postFrame(Qt3DCore::QAspectManager *manager)
@@ -83,11 +83,9 @@ void RenderViewCommandUpdaterJobPrivate::postFrame(Qt3DCore::QAspectManager *man
RenderViewCommandUpdaterJob::RenderViewCommandUpdaterJob()
: Qt3DCore::QAspectJob(*new RenderViewCommandUpdaterJobPrivate(this))
- , m_offset(0)
- , m_count(0)
, m_renderView(nullptr)
, m_renderer(nullptr)
- , m_renderables()
+ , m_renderablesSubView()
{
SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++)
}
@@ -97,10 +95,10 @@ void RenderViewCommandUpdaterJob::run()
// Build RenderCommand should perform the culling as we have no way to determine
// if a child has a mesh in the view frustum while its parent isn't contained in it.
if (!m_renderView->noDraw()) {
- if (m_count == 0)
+ if (m_renderablesSubView.count == 0)
return;
// Update Render Commands (Uniform Change, Depth Change)
- m_renderView->updateRenderCommand(m_renderables.data(), m_offset, m_count);
+ m_renderView->updateRenderCommand(m_renderablesSubView);
}
}
diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h
index 9c7e84edd..1ebeb0249 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h
+++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h
@@ -74,24 +74,20 @@ public:
inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; }
inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; }
- inline void setRenderables(const EntityRenderCommandDataPtr &renderables, int offset, int count) Q_DECL_NOTHROW
+ inline void setRenderablesSubView(const EntityRenderCommandDataSubView &renderablesSubView) Q_DECL_NOTHROW
{
- m_offset = offset;
- m_count = count;
- m_renderables = renderables;
+ m_renderablesSubView = renderablesSubView;
}
- EntityRenderCommandDataPtr renderables() const { return m_renderables; }
+ EntityRenderCommandDataSubView renderablesSubView() const { return m_renderablesSubView; }
inline void setRebuildFlags(RebuildFlagSet rebuildFlags) { m_rebuildFlags = rebuildFlags; }
void run() final;
private:
- int m_offset;
- int m_count;
RebuildFlagSet m_rebuildFlags;
RenderView *m_renderView;
Renderer *m_renderer;
- EntityRenderCommandDataPtr m_renderables;
+ EntityRenderCommandDataSubView m_renderablesSubView;
Q_DECLARE_PRIVATE(RenderViewCommandUpdaterJob)
};
diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
index 8774ac368..eb1e3dd38 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
+++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
@@ -188,17 +188,13 @@ void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphN
case FrameGraphNode::StateSet: {
const Render::StateSetNode *rStateSet = static_cast<const Render::StateSetNode *>(node);
- // Create global RenderStateSet for renderView if no stateSet was set before
- RenderStateSet *stateSet = rv->stateSet();
- if (stateSet == nullptr && rStateSet->hasRenderStates()) {
- stateSet = new RenderStateSet();
- rv->setStateSet(stateSet);
- }
-
// Add states from new stateSet we might be missing
// but don' t override existing states (lower StateSetNode always has priority)
- if (rStateSet->hasRenderStates())
+ if (rStateSet->hasRenderStates()) {
+ // Create global RenderStateSet for renderView if no stateSet was set before
+ RenderStateSet *stateSet = rv->getOrCreateStateSet();
addStatesToRenderStateSet(stateSet, rStateSet->renderStates(), manager->renderStateManager());
+ }
break;
}
diff --git a/src/plugins/renderers/opengl/renderer/commandexecuter.cpp b/src/plugins/renderers/opengl/renderer/commandexecuter.cpp
index 03d5d5a7f..70fdbb943 100644
--- a/src/plugins/renderers/opengl/renderer/commandexecuter.cpp
+++ b/src/plugins/renderers/opengl/renderer/commandexecuter.cpp
@@ -346,7 +346,7 @@ void CommandExecuter::performAsynchronousCommandExecution(const QVector<Render::
viewObj.insert(QLatin1String("clearStencilValue"), v->clearStencilValue());
QJsonArray renderCommandsArray;
- for (Render::OpenGL::RenderCommand &c : v->commands()) {
+ v->forEachCommand([&] (Render::OpenGL::RenderCommand &c) {
QJsonObject commandObj;
Render::NodeManagers *nodeManagers = m_renderer->nodeManagers();
commandObj.insert(QLatin1String("shader"), typeToJsonValue(QVariant::fromValue(c.m_shaderId)));
@@ -357,7 +357,7 @@ void CommandExecuter::performAsynchronousCommandExecution(const QVector<Render::
commandObj.insert(QLatin1String("shaderParameterPack"), parameterPackToJson(c.m_parameterPack));
renderCommandsArray.push_back(commandObj);
- }
+ });
viewObj.insert(QLatin1String("commands"), renderCommandsArray);
viewArray.push_back(viewObj);
}
diff --git a/src/plugins/renderers/opengl/renderer/rendercommand_p.h b/src/plugins/renderers/opengl/renderer/rendercommand_p.h
index ce81e041d..a2305ecdf 100644
--- a/src/plugins/renderers/opengl/renderer/rendercommand_p.h
+++ b/src/plugins/renderers/opengl/renderer/rendercommand_p.h
@@ -61,6 +61,7 @@
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMatrix4x4>
+#include <vector>
QT_BEGIN_NAMESPACE
@@ -145,18 +146,18 @@ inline bool operator!=(const RenderCommand &lhs, const RenderCommand &rhs) noexc
struct EntityRenderCommandData
{
- QVector<Entity *> entities;
- QVector<RenderCommand> commands;
- QVector<RenderPassParameterData> passesData;
+ std::vector<Entity *> entities;
+ std::vector<RenderCommand> commands;
+ std::vector<RenderPassParameterData> passesData;
- void reserve(int size)
+ void reserve(size_t size)
{
entities.reserve(size);
commands.reserve(size);
passesData.reserve(size);
}
- inline int size() const { return entities.size(); }
+ inline size_t size() const { return entities.size(); }
inline void push_back(Entity *e, const RenderCommand &c, const RenderPassParameterData &p)
{
@@ -174,15 +175,64 @@ struct EntityRenderCommandData
EntityRenderCommandData &operator+=(EntityRenderCommandData &&t)
{
- entities += std::move(t.entities);
- commands += std::move(t.commands);
- passesData += std::move(t.passesData);
+ entities.insert(entities.cend(),
+ std::make_move_iterator(t.entities.begin()),
+ std::make_move_iterator(t.entities.end()));
+ commands.insert(commands.cend(),
+ std::make_move_iterator(t.commands.begin()),
+ std::make_move_iterator(t.commands.end()));
+ passesData.insert(passesData.cend(),
+ std::make_move_iterator(t.passesData.begin()),
+ std::make_move_iterator(t.passesData.end()));
return *this;
}
};
-using EntityRenderCommandDataPtr = QSharedPointer<EntityRenderCommandData>;
+struct EntityRenderCommandDataView
+{
+ EntityRenderCommandData data;
+ std::vector<size_t> indices;
+
+ size_t size() const noexcept { return indices.size(); }
+
+ template<typename F>
+ void forEachCommand(F func)
+ {
+ for (size_t idx : indices)
+ func(data.commands[idx]);
+ }
+};
+using EntityRenderCommandDataViewPtr = QSharedPointer<EntityRenderCommandDataView>;
+
+struct EntityRenderCommandDataSubView
+{
+ EntityRenderCommandDataViewPtr view;
+ size_t offset;
+ size_t count;
+
+ template<typename F>
+ void forEach(F func)
+ {
+ for (size_t i = 0, m = size_t(count); i < m; ++i) {
+ const size_t idx = view->indices[offset + i];
+ func(view->data.entities[idx],
+ view->data.passesData[idx],
+ view->data.commands[idx]);
+ }
+ }
+
+ template<typename F>
+ void forEach(F func) const
+ {
+ for (size_t i = 0, m = size_t(count); i < m; ++i) {
+ const size_t idx = view->indices[offset + i];
+ func(view->data.entities[idx],
+ view->data.passesData[idx],
+ view->data.commands[idx]);
+ }
+ }
+};
} // namespace OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp
index a5d624c34..63e5fe6c3 100644
--- a/src/plugins/renderers/opengl/renderer/renderer.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderer.cpp
@@ -938,8 +938,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView
QHash<HVao, bool> updatedTable;
for (RenderView *rv: renderViews) {
- QVector<RenderCommand> &commands = rv->commands();
- for (RenderCommand &command : commands) {
+ rv->forEachCommand([&] (RenderCommand &command) {
// Update/Create VAO
if (command.m_type == RenderCommand::Draw) {
Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry);
@@ -998,7 +997,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView
GLShader *shader = command.m_glShader;
Q_ASSERT(shader);
}
- }
+ });
}
// Make sure we leave nothing bound
@@ -1525,7 +1524,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
for (int i = 0; i < renderViewsCount; ++i) {
// Initialize GraphicsContext for drawing
// If the RenderView has a RenderStateSet defined
- const RenderView *renderView = renderViews.at(i);
+ RenderView *renderView = renderViews.at(i);
if (renderView->shouldSkipSubmission())
continue;
@@ -1982,7 +1981,7 @@ QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const
}
// Called by executeCommands
-void Renderer::performDraw(RenderCommand *command)
+void Renderer::performDraw(const RenderCommand *command)
{
// Indirect Draw Calls
if (command->m_drawIndirect) {
@@ -2108,12 +2107,11 @@ void Renderer::createOrUpdateVAO(RenderCommand *command,
// Called by RenderView->submit() in RenderThread context
// Returns true, if all RenderCommands were sent to the GPU
-bool Renderer::executeCommandsSubmission(const RenderView *rv)
+bool Renderer::executeCommandsSubmission(RenderView *rv)
{
bool allCommandsIssued = true;
// Render drawing commands
- QVector<RenderCommand> commands = rv->commands();
// Use the graphicscontext to submit the commands to the underlying
// graphics API (OpenGL)
@@ -2122,7 +2120,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv)
RenderStateSet *globalState = m_submissionContext->currentStateSet();
OpenGLVertexArrayObject *vao = nullptr;
- for (RenderCommand &command : commands) {
+ rv->forEachCommand([&] (RenderCommand &command) {
if (command.m_type == RenderCommand::Compute) { // Compute Call
performCompute(rv, &command);
@@ -2130,7 +2128,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv)
// Check if we have a valid command that can be drawn
if (!command.m_isValid) {
allCommandsIssued = false;
- continue;
+ return;
}
vao = m_glResourceManagers->vaoManager()->data(command.m_vao);
@@ -2138,7 +2136,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv)
// something may have went wrong when initializing the VAO
if (!vao->isSpecified()) {
allCommandsIssued = false;
- continue;
+ return;
}
{
@@ -2147,7 +2145,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv)
GLShader *shader = command.m_glShader;
if (!m_submissionContext->activateShader(shader)) {
allCommandsIssued = false;
- continue;
+ return;
}
}
@@ -2164,7 +2162,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv)
allCommandsIssued = false;
// If we have failed to set uniform (e.g unable to bind a texture)
// we won't perform the draw call which could show invalid content
- continue;
+ return;
}
}
@@ -2192,7 +2190,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv)
//// Draw Calls
performDraw(&command);
}
- } // end of RenderCommands loop
+ }); // end of RenderCommands loop
// We cache the VAO and release it only at the end of the exectute frame
// We try to minimize VAO binding between RenderCommands
diff --git a/src/plugins/renderers/opengl/renderer/renderer_p.h b/src/plugins/renderers/opengl/renderer/renderer_p.h
index eff5e9d96..fddff18c8 100644
--- a/src/plugins/renderers/opengl/renderer/renderer_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderer_p.h
@@ -254,7 +254,7 @@ public:
GLuint defaultFramebuffer);
void prepareCommandsSubmission(const QVector<RenderView *> &renderViews);
- bool executeCommandsSubmission(const RenderView *rv);
+ bool executeCommandsSubmission(RenderView *rv);
bool updateVAOWithAttributes(Geometry *geometry,
const RenderCommand *command,
GLShader *shader,
@@ -366,7 +366,7 @@ private:
QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests;
- void performDraw(RenderCommand *command);
+ void performDraw(const RenderCommand *command);
void performCompute(const RenderView *rv, RenderCommand *command);
void createOrUpdateVAO(RenderCommand *command,
HVao *previousVAOHandle,
diff --git a/src/plugins/renderers/opengl/renderer/renderercache_p.h b/src/plugins/renderers/opengl/renderer/renderercache_p.h
index 5e02df623..60f08bcbd 100644
--- a/src/plugins/renderers/opengl/renderer/renderercache_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderercache_p.h
@@ -70,6 +70,7 @@ struct RendererCache
{
struct LeafNodeData
{
+ Matrix4x4 viewProjectionMatrix;
// Set by the FilterLayerJob
// Contains all Entities that satisfy the layer filtering for the RV
QVector<Entity *> filterEntitiesByLayer;
@@ -78,11 +79,17 @@ struct RendererCache
MaterialParameterGathererData materialParameterGatherer;
// Set by the SyncRenderViewPreCommandUpdateJob
- // Contains all Entities that are renderables (either compute or draw
- // depending on the RV) and that satisfy the layer filtering.
- QVector<Entity *> layeredFilteredRenderables;
+ // Contains caches of different filtering stages that can
+ // be cached across frame
+ QVector<Entity *> layeredFilteredRenderables; // Changes rarely
+ QVector<Entity *> filteredAndCulledRenderables; // Changes if camera is modified
QVector<LightSource> layeredFilteredLightSources;
- EntityRenderCommandData renderCommandData;
+
+ // We keep two sets of filtered render commands
+ // and we flip in between them every frame
+ int viewIdx = 0;
+ bool requestFilteringAtNextFrame = false;
+ EntityRenderCommandDataViewPtr filteredRenderCommandDataViews[2];
};
// Variabled below are shared amongst all RV
diff --git a/src/plugins/renderers/opengl/renderer/renderview.cpp b/src/plugins/renderers/opengl/renderer/renderview.cpp
index 6707c6c2f..9e4bd9f46 100644
--- a/src/plugins/renderers/opengl/renderer/renderview.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderview.cpp
@@ -156,34 +156,34 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa
case ModelMatrix:
return UniformValue(model);
case ViewMatrix:
- return UniformValue(m_data.m_viewMatrix);
+ return UniformValue(m_viewMatrix);
case ProjectionMatrix:
- return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens));
+ return UniformValue(getProjectionMatrix(m_renderCameraLens));
case ModelViewMatrix:
- return UniformValue(m_data.m_viewMatrix * model);
+ return UniformValue(m_viewMatrix * model);
case ViewProjectionMatrix:
- return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix);
+ return UniformValue(getProjectionMatrix(m_renderCameraLens) * m_viewMatrix);
case ModelViewProjectionMatrix:
- return UniformValue(m_data.m_viewProjectionMatrix * model);
+ return UniformValue(m_viewProjectionMatrix * model);
case InverseModelMatrix:
return UniformValue(model.inverted());
case InverseViewMatrix:
- return UniformValue(m_data.m_viewMatrix.inverted());
+ return UniformValue(m_viewMatrix.inverted());
case InverseProjectionMatrix: {
- return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens).inverted());
+ return UniformValue(getProjectionMatrix(m_renderCameraLens).inverted());
}
case InverseModelViewMatrix:
- return UniformValue((m_data.m_viewMatrix * model).inverted());
+ return UniformValue((m_viewMatrix * model).inverted());
case InverseViewProjectionMatrix: {
- const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix;
+ const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_renderCameraLens) * m_viewMatrix;
return UniformValue(viewProjectionMatrix.inverted());
}
case InverseModelViewProjectionMatrix:
- return UniformValue((m_data.m_viewProjectionMatrix * model).inverted());
+ return UniformValue((m_viewProjectionMatrix * model).inverted());
case ModelNormalMatrix:
return UniformValue(convertToQMatrix4x4(model).normalMatrix());
case ModelViewNormalMatrix:
- return UniformValue(convertToQMatrix4x4(m_data.m_viewMatrix * model).normalMatrix());
+ return UniformValue(convertToQMatrix4x4(m_viewMatrix * model).normalMatrix());
case ViewportMatrix: {
QMatrix4x4 viewportMatrix;
// TO DO: Implement on Matrix4x4
@@ -199,13 +199,13 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa
case AspectRatio:
return float(m_surfaceSize.width()) / std::max(1.f, float(m_surfaceSize.height()));
case Exposure:
- return UniformValue(m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f);
+ return UniformValue(m_renderCameraLens ? m_renderCameraLens->exposure() : 0.0f);
case Gamma:
return UniformValue(m_gamma);
case Time:
return UniformValue(float(m_renderer->time() / 1000000000.0f));
case EyePosition:
- return UniformValue(m_data.m_eyePos);
+ return UniformValue(m_eyePos);
case SkinningPalette: {
const Armature *armature = entity->renderComponent<Armature>();
if (!armature) {
@@ -221,29 +221,7 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa
}
RenderView::RenderView()
- : m_isDownloadBuffersEnable(false)
- , m_hasBlitFramebufferInfo(false)
- , m_renderer(nullptr)
- , m_manager(nullptr)
- , m_devicePixelRatio(1.)
- , m_viewport(QRectF(0., 0., 1., 1.))
- , m_gamma(2.2f)
- , m_surface(nullptr)
- , m_clearBuffer(QClearBuffers::None)
- , m_clearDepthValue(1.f)
- , m_clearStencilValue(0)
- , m_stateSet(nullptr)
- , m_noDraw(false)
- , m_compute(false)
- , m_frustumCulling(false)
- , m_showDebugOverlay(false)
- , m_memoryBarrier(QMemoryBarrier::None)
- , m_environmentLight(nullptr)
{
- m_workGroups[0] = 1;
- m_workGroups[1] = 1;
- m_workGroups[2] = 1;
-
if (Q_UNLIKELY(!wasInitialized.exchange(true))) {
// Needed as we can control the init order of static/global variables across compile units
// and this hash relies on the static StringToInt class
@@ -254,7 +232,6 @@ RenderView::RenderView()
RenderView::~RenderView()
{
- delete m_stateSet;
}
namespace {
@@ -327,26 +304,31 @@ struct AdjacentSubRangeFinder<QSortPolicy::Texture>
};
template<typename Predicate>
-int advanceUntilNonAdjacent(const QVector<RenderCommand> &commands,
- const int beg, const int end, Predicate pred)
+int advanceUntilNonAdjacent(const EntityRenderCommandDataView *view,
+ const size_t beg, const size_t end, Predicate pred)
{
- int i = beg + 1;
- while (i < end) {
- if (!pred(*(commands.begin() + beg), *(commands.begin() + i)))
- break;
- ++i;
+ const std::vector<size_t> &commandIndices = view->indices;
+ const std::vector<RenderCommand> &commands = view->data.commands;
+ size_t i = beg + 1;
+ if (i < end) {
+ const size_t startIdx = commandIndices[beg];
+ while (i < end) {
+ const size_t targetIdx = commandIndices[i];
+ if (!pred(commands[startIdx], commands[targetIdx]))
+ break;
+ ++i;
+ }
}
return i;
}
-using CommandIt = QVector<RenderCommand>::iterator;
-
template<int SortType>
struct SubRangeSorter
{
- static void sortSubRange(CommandIt begin, const CommandIt end)
+ static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end)
{
+ Q_UNUSED(view)
Q_UNUSED(begin)
Q_UNUSED(end)
Q_UNREACHABLE();
@@ -356,9 +338,14 @@ struct SubRangeSorter
template<>
struct SubRangeSorter<QSortPolicy::StateChangeCost>
{
- static void sortSubRange(CommandIt begin, const CommandIt end)
+ static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end)
{
- std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ std::vector<size_t> &commandIndices = view->indices;
+ const std::vector<RenderCommand> &commands = view->data.commands;
+ std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end,
+ [&commands] (const size_t &iA, const size_t &iB) {
+ const RenderCommand &a = commands[iA];
+ const RenderCommand &b = commands[iB];
return a.m_changeCost > b.m_changeCost;
});
}
@@ -367,9 +354,14 @@ struct SubRangeSorter<QSortPolicy::StateChangeCost>
template<>
struct SubRangeSorter<QSortPolicy::BackToFront>
{
- static void sortSubRange(CommandIt begin, const CommandIt end)
+ static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end)
{
- std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ std::vector<size_t> &commandIndices = view->indices;
+ const std::vector<RenderCommand> &commands = view->data.commands;
+ std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end,
+ [&commands] (const size_t &iA, const size_t &iB) {
+ const RenderCommand &a = commands[iA];
+ const RenderCommand &b = commands[iB];
return a.m_depth > b.m_depth;
});
}
@@ -378,10 +370,14 @@ struct SubRangeSorter<QSortPolicy::BackToFront>
template<>
struct SubRangeSorter<QSortPolicy::Material>
{
- static void sortSubRange(CommandIt begin, const CommandIt end)
+ static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end)
{
- // First we sort by shader
- std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ std::vector<size_t> &commandIndices = view->indices;
+ const std::vector<RenderCommand> &commands = view->data.commands;
+ std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end,
+ [&commands] (const size_t &iA, const size_t &iB) {
+ const RenderCommand &a = commands[iA];
+ const RenderCommand &b = commands[iB];
return a.m_glShader > b.m_glShader;
});
}
@@ -390,9 +386,14 @@ struct SubRangeSorter<QSortPolicy::Material>
template<>
struct SubRangeSorter<QSortPolicy::FrontToBack>
{
- static void sortSubRange(CommandIt begin, const CommandIt end)
+ static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end)
{
- std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ std::vector<size_t> &commandIndices = view->indices;
+ const std::vector<RenderCommand> &commands = view->data.commands;
+ std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end,
+ [&commands] (const size_t &iA, const size_t &iB) {
+ const RenderCommand &a = commands[iA];
+ const RenderCommand &b = commands[iB];
return a.m_depth < b.m_depth;
});
}
@@ -401,10 +402,15 @@ struct SubRangeSorter<QSortPolicy::FrontToBack>
template<>
struct SubRangeSorter<QSortPolicy::Texture>
{
- static void sortSubRange(CommandIt begin, const CommandIt end)
+ static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end)
{
#ifndef Q_OS_WIN
- std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ std::vector<size_t> &commandIndices = view->indices;
+ const std::vector<RenderCommand> &commands = view->data.commands;
+ std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end,
+ [&commands] (const int &iA, const int &iB) {
+ const RenderCommand &a = commands[iA];
+ const RenderCommand &b = commands[iB];
QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures();
QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures();
@@ -426,21 +432,21 @@ struct SubRangeSorter<QSortPolicy::Texture>
}
};
-int findSubRange(const QVector<RenderCommand> &commands,
+int findSubRange(const EntityRenderCommandDataView *view,
const int begin, const int end,
const QSortPolicy::SortType sortType)
{
switch (sortType) {
case QSortPolicy::StateChangeCost:
- return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange);
+ return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange);
case QSortPolicy::BackToFront:
- return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange);
+ return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange);
case QSortPolicy::Material:
- return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
case QSortPolicy::FrontToBack:
- return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange);
+ return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange);
case QSortPolicy::Texture:
- return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange);
+ return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange);
case QSortPolicy::Uniform:
return end;
default:
@@ -449,22 +455,27 @@ int findSubRange(const QVector<RenderCommand> &commands,
}
}
-void sortByMaterial(QVector<RenderCommand> &commands, int begin, const int end)
+void sortByMaterial(EntityRenderCommandDataView *view, int begin, const int end)
{
// We try to arrange elements so that their rendering cost is minimized for a given shader
- int rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ std::vector<size_t> &commandIndices = view->indices;
+ const std::vector<RenderCommand> &commands = view->data.commands;
+ int rangeEnd = advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
while (begin != end) {
if (begin + 1 < rangeEnd) {
- std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (const RenderCommand &a, const RenderCommand &b){
+ std::stable_sort(commandIndices.begin() + begin + 1, commandIndices.begin() + rangeEnd,
+ [&commands] (const int &iA, const int &iB) {
+ const RenderCommand &a = commands[iA];
+ const RenderCommand &b = commands[iB];
return a.m_material.handle() < b.m_material.handle();
});
}
begin = rangeEnd;
- rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ rangeEnd = advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
}
}
-void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end, const int level,
+void sortCommandRange(EntityRenderCommandDataView *view, int begin, int end, const int level,
const QVector<Qt3DRender::QSortPolicy::SortType> &sortingTypes)
{
if (level >= sortingTypes.size())
@@ -472,22 +483,22 @@ void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end
switch (sortingTypes.at(level)) {
case QSortPolicy::StateChangeCost:
- SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(view, begin, end);
break;
case QSortPolicy::BackToFront:
- SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(view, begin, end);
break;
case QSortPolicy::Material:
// Groups all same shader DNA together
- SubRangeSorter<QSortPolicy::Material>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ SubRangeSorter<QSortPolicy::Material>::sortSubRange(view, begin, end);
// Group all same material together (same parameters most likely)
- sortByMaterial(commands, begin, end);
+ sortByMaterial(view, begin, end);
break;
case QSortPolicy::FrontToBack:
- SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(view, begin, end);
break;
case QSortPolicy::Texture:
- SubRangeSorter<QSortPolicy::Texture>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ SubRangeSorter<QSortPolicy::Texture>::sortSubRange(view, begin, end);
break;
case QSortPolicy::Uniform:
break;
@@ -497,11 +508,11 @@ void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end
// For all sub ranges of adjacent item for sortType[i]
// Perform filtering with sortType[i + 1]
- int rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level));
+ int rangeEnd = findSubRange(view, begin, end, sortingTypes.at(level));
while (begin != end) {
- sortCommandRange(commands, begin, rangeEnd, level + 1, sortingTypes);
+ sortCommandRange(view, begin, rangeEnd, level + 1, sortingTypes);
begin = rangeEnd;
- rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level));
+ rangeEnd = findSubRange(view, begin, end, sortingTypes.at(level));
}
}
@@ -509,37 +520,41 @@ void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end
void RenderView::sort()
{
+ assert(m_renderCommandDataView);
// Compares the bitsetKey of the RenderCommands
// Key[Depth | StateCost | Shader]
- sortCommandRange(m_commands, 0, m_commands.size(), 0, m_data.m_sortingTypes);
+ sortCommandRange(m_renderCommandDataView.data(), 0, m_renderCommandDataView->size(), 0, m_sortingTypes);
// For RenderCommand with the same shader
// We compute the adjacent change cost
// Only perform uniform minimization if we explicitly asked for it
- if (!m_data.m_sortingTypes.contains(QSortPolicy::Uniform))
+ if (!m_sortingTypes.contains(QSortPolicy::Uniform))
return;
// Minimize uniform changes
int i = 0;
- const int commandSize = m_commands.size();
+ std::vector<RenderCommand> &commands = m_renderCommandDataView->data.commands;
+ const std::vector<size_t> &indices = m_renderCommandDataView->indices;
+ const size_t commandSize = indices.size();
+
while (i < commandSize) {
- int j = i;
+ size_t j = i;
// Advance while commands share the same shader
while (i < commandSize &&
- m_commands[j].m_glShader == m_commands[i].m_glShader)
+ commands[indices[j]].m_glShader == commands[indices[i]].m_glShader)
++i;
if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes
- PackUniformHash cachedUniforms = m_commands[j++].m_parameterPack.uniforms();
+ PackUniformHash cachedUniforms = commands[indices[j++]].m_parameterPack.uniforms();
while (j < i) {
// We need the reference here as we are modifying the original container
// not the copy
- PackUniformHash &uniforms = m_commands[j].m_parameterPack.m_uniforms;
+ PackUniformHash &uniforms = commands[indices[j]].m_parameterPack.m_uniforms;
- for (int u = 0; u < uniforms.keys.size();) {
+ for (size_t u = 0; u < uniforms.keys.size();) {
// We are comparing the values:
// - raw uniform values
// - the texture Node id if the uniform represents a texture
@@ -572,6 +587,13 @@ void RenderView::setRenderer(Renderer *renderer)
m_manager = renderer->nodeManagers();
}
+RenderStateSet *RenderView::getOrCreateStateSet()
+{
+ if (!m_stateSet)
+ m_stateSet.reset(new RenderStateSet());
+ return m_stateSet.data();
+}
+
void RenderView::addClearBuffers(const ClearBuffers *cb) {
QClearBuffers::BufferTypeFlags type = cb->type();
@@ -649,8 +671,8 @@ EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity
if (pass->hasRenderStates()) {
command.m_stateSet = RenderStateSetPtr::create();
addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager());
- if (m_stateSet != nullptr)
- command.m_stateSet->merge(m_stateSet);
+ if (m_stateSet)
+ command.m_stateSet->merge(m_stateSet.data());
command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data());
}
command.m_shaderId = pass->shaderProgram();
@@ -773,7 +795,7 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Ent
// Merge per pass stateset with global stateset
// so that the local stateset only overrides
if (m_stateSet != nullptr)
- command.m_stateSet->merge(m_stateSet);
+ command.m_stateSet->merge(m_stateSet.data());
command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data());
}
command.m_shaderId = pass->shaderProgram();
@@ -800,9 +822,7 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Ent
return commands;
}
-void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData,
- int offset,
- int count)
+void RenderView::updateRenderCommand(const EntityRenderCommandDataSubView &subView)
{
// Note: since many threads can be building render commands
// we need to ensure that the UniformBlockValueBuilder they are using
@@ -812,38 +832,14 @@ void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData,
builder->textureManager = m_manager->textureManager();
m_localData.setLocalData(builder);
- for (int i = 0, m = count; i < m; ++i) {
- const int idx = offset + i;
- Entity *entity = renderCommandData->entities.at(idx);
- const RenderPassParameterData passData = renderCommandData->passesData.at(idx);
- RenderCommand &command = renderCommandData->commands[idx];
-
- // Pick which lights to take in to account.
- // For now decide based on the distance by taking the MAX_LIGHTS closest lights.
- // Replace with more sophisticated mechanisms later.
- // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command
- QVector<LightSource> lightSources;
- EnvironmentLight *environmentLight = nullptr;
-
+ subView.forEach([this] (const Entity *entity,
+ const RenderPassParameterData &passData,
+ RenderCommand &command) {
if (command.m_type == RenderCommand::Draw) {
// Project the camera-to-object-center vector onto the camera
// view vector. This gives a depth value suitable as the key
// for BackToFront sorting.
- command.m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir);
-
- environmentLight = m_environmentLight;
- lightSources = m_lightSources;
-
- if (lightSources.size() > 1) {
- const Vector3D entityCenter = entity->worldBoundingVolume()->center();
- std::sort(lightSources.begin(), lightSources.end(),
- [&] (const LightSource &a, const LightSource &b) {
- const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center());
- const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center());
- return distA < distB;
- });
- }
- lightSources = lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS));
+ command.m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_eyePos, m_eyeViewDir);
} else { // Compute
// Note: if frameCount has reached 0 in the previous frame, isEnabled
// would be false
@@ -852,16 +848,12 @@ void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData,
computeJob->updateFrameCount();
}
- ParameterInfoList globalParameters = passData.parameterInfo;
// setShaderAndUniforms can initialize a localData
// make sure this is cleared before we leave this function
-
setShaderAndUniforms(&command,
- globalParameters,
- entity,
- lightSources,
- environmentLight);
- }
+ passData.parameterInfo,
+ entity);
+ });
// We reset the local data once we are done with it
m_localData.setLocalData(nullptr);
@@ -869,11 +861,11 @@ void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData,
void RenderView::updateMatrices()
{
- if (m_data.m_renderCameraNode && m_data.m_renderCameraLens && m_data.m_renderCameraLens->isEnabled()) {
- const Matrix4x4 cameraWorld = *(m_data.m_renderCameraNode->worldTransform());
- setViewMatrix(m_data.m_renderCameraLens->viewMatrix(cameraWorld));
+ if (m_renderCameraNode && m_renderCameraLens && m_renderCameraLens->isEnabled()) {
+ const Matrix4x4 cameraWorld = *(m_renderCameraNode->worldTransform());
+ setViewMatrix(m_renderCameraLens->viewMatrix(cameraWorld));
- setViewProjectionMatrix(m_data.m_renderCameraLens->projection() * viewMatrix());
+ setViewProjectionMatrix(m_renderCameraLens->projection() * viewMatrix());
//To get the eyePosition of the camera, we need to use the inverse of the
//camera's worldTransform matrix.
const Matrix4x4 inverseWorldTransform = viewMatrix().inverted();
@@ -882,7 +874,7 @@ void RenderView::updateMatrices()
// Get the viewing direction of the camera. Use the normal matrix to
// ensure non-uniform scale works too.
- const QMatrix3x3 normalMat = convertToQMatrix4x4(m_data.m_viewMatrix).normalMatrix();
+ const QMatrix3x3 normalMat = convertToQMatrix4x4(m_viewMatrix).normalMatrix();
// dir = normalize(QVector3D(0, 0, -1) * normalMat)
setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized());
}
@@ -976,7 +968,7 @@ void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &unif
builder->activeUniformNamesToValue.clear();
// Set the view matrix to be used to transform "Transformed" properties in the ShaderData
- builder->viewMatrix = m_data.m_viewMatrix;
+ builder->viewMatrix = m_viewMatrix;
// Force to update the whole block
builder->updatedPropertiesOnly = false;
// Retrieve names and description of each active uniforms in the uniform block
@@ -1031,10 +1023,8 @@ void RenderView::applyParameter(const Parameter *param,
void RenderView::setShaderAndUniforms(RenderCommand *command,
- ParameterInfoList &parameters,
- Entity *entity,
- const QVector<LightSource> &activeLightSources,
- EnvironmentLight *environmentLight) const
+ const ParameterInfoList &parameters,
+ const Entity *entity) const
{
// The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex here
// Set shader, technique, and effect by basically doing :
@@ -1047,8 +1037,16 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
// Once that works, improve that to try and minimize QUniformPack updates
GLShader *shader = command->m_glShader;
- if (shader != nullptr && shader->isLoaded()) {
+ if (shader == nullptr || !shader->isLoaded())
+ return;
+ // If we have already build the uniforms previously, we should
+ // only update values of uniforms that have changed
+ // If parameters add been added/removed, the command would have been rebuild
+ // and the parameter pack would be empty
+ const bool updateUniformsOnly = command->m_parameterPack.submissionUniformIndices().size() > 0;
+
+ if (!updateUniformsOnly) {
// Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings
// If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name
// equals to the parameter name
@@ -1075,112 +1073,141 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
// We still need to process the uniforms as the command could be a compute command
command->m_isValid = !command->m_activeAttributes.empty();
- if (shader->hasActiveVariables()) {
+ // Reserve amount of uniforms we are going to need
+ command->m_parameterPack.reserve(shader->parameterPackSize());
+ }
- // Reserve amount of uniforms we are going to need
- command->m_parameterPack.reserve(shader->parameterPackSize());
+ if (shader->hasActiveVariables()) {
+ const QVector<int> &standardUniformNamesIds = shader->standardUniformNameIds();
- const QVector<int> &standardUniformNamesIds = shader->standardUniformNameIds();
- for (const int uniformNameId : standardUniformNamesIds)
- setStandardUniformValue(command->m_parameterPack, uniformNameId, entity);
+ // It only makes sense to update the standard uniforms if:
+ // - Camera changed
+ // - Entity transform changed
+ // - Viewport/Surface changed
- ParameterInfoList::const_iterator it = parameters.cbegin();
- const ParameterInfoList::const_iterator parametersEnd = parameters.cend();
+ for (const int uniformNameId : standardUniformNamesIds)
+ setStandardUniformValue(command->m_parameterPack, uniformNameId, entity);
- while (it != parametersEnd) {
- const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
- applyParameter(param, command, shader);
- ++it;
- }
+ ParameterInfoList::const_iterator it = parameters.cbegin();
+ const ParameterInfoList::const_iterator parametersEnd = parameters.cend();
- // Lights
- const QVector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds();
- if (!lightUniformNamesIds.empty()) {
- int lightIdx = 0;
- for (const LightSource &lightSource : activeLightSources) {
- if (lightIdx == MAX_LIGHTS)
- break;
- Entity *lightEntity = lightSource.entity;
- const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform());
- const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f);
- for (Light *light : lightSource.lights) {
- if (!light->isEnabled())
- continue;
-
- ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData());
- if (!shaderData)
- continue;
-
- if (lightIdx == MAX_LIGHTS)
- break;
+ while (it != parametersEnd) {
+ const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
+ applyParameter(param, command, shader);
+ ++it;
+ }
- // Note: implicit conversion of values to UniformValue
- if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[lightIdx])) {
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[lightIdx], worldPos);
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[lightIdx], 0.5f);
- } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) {
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos);
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f);
- }
+ // Lights
+ updateLightUniforms(command, entity);
+ }
- // There is no risk in doing that even if multithreaded
- // since we are sure that a shaderData is unique for a given light
- // and won't ever be referenced as a Component either
- Matrix4x4 *worldTransform = lightEntity->worldTransform();
- if (worldTransform)
- shaderData->updateWorldTransform(*worldTransform);
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ if (!updateUniformsOnly)
+ shader->prepareUniforms(command->m_parameterPack);
+}
- setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::LIGHT_STRUCT_NAMES[lightIdx]);
- setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::LIGHT_STRUCT_UNROLL_NAMES[lightIdx]);
- ++lightIdx;
- }
- }
+void RenderView::updateLightUniforms(RenderCommand *command, const Entity *entity) const
+{
+ GLShader *shader = command->m_glShader;
+ const QVector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds();
+ if (!lightUniformNamesIds.empty()) {
+ // Pick which lights to take in to account.
+ // For now decide based on the distance by taking the MAX_LIGHTS closest lights.
+ // Replace with more sophisticated mechanisms later.
+ // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command
+ QVector<LightSource> lightSources = m_lightSources;
+
+ if (lightSources.size() > 1) {
+ const Vector3D entityCenter = entity->worldBoundingVolume()->center();
+ std::sort(lightSources.begin(), lightSources.end(),
+ [&] (const LightSource &a, const LightSource &b) {
+ const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center());
+ const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center());
+ return distA < distB;
+ });
+ }
+ m_lightSources = lightSources.mid(0, std::min(lightSources.size(), MAX_LIGHTS));
+
+ int lightIdx = 0;
+ for (const LightSource &lightSource : qAsConst(m_lightSources)) {
+ if (lightIdx == MAX_LIGHTS)
+ break;
+ Entity *lightEntity = lightSource.entity;
+ const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform());
+ const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f);
+ for (Light *light : lightSource.lights) {
+ if (!light->isEnabled())
+ continue;
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx)));
-
- // If no active light sources and no environment light, add a default light
- if (activeLightSources.isEmpty() && !environmentLight) {
- // Note: implicit conversion of values to UniformValue
- if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[0])) {
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[0], 0.5f);
- } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) {
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
- setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f);
- }
+ ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData());
+ if (!shaderData)
+ continue;
+
+ if (lightIdx == MAX_LIGHTS)
+ break;
+
+ // Note: implicit conversion of values to UniformValue
+ if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[lightIdx], worldPos);
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[lightIdx], 0.5f);
+ } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos);
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f);
}
+
+ // There is no risk in doing that even if multithreaded
+ // since we are sure that a shaderData is unique for a given light
+ // and won't ever be referenced as a Component either
+ Matrix4x4 *worldTransform = lightEntity->worldTransform();
+ if (worldTransform)
+ shaderData->updateWorldTransform(*worldTransform);
+
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::LIGHT_STRUCT_NAMES[lightIdx]);
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::LIGHT_STRUCT_UNROLL_NAMES[lightIdx]);
+ ++lightIdx;
}
+ }
- // Environment Light
- int envLightCount = 0;
- if (environmentLight && environmentLight->isEnabled()) {
- ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData());
- if (shaderData) {
- setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight"));
- envLightCount = 1;
- }
- } else {
- // with some drivers, samplers (like the envbox sampler) need to be bound even though
- // they may not be actually used, otherwise draw calls can fail
- static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance"));
- static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular"));
- setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount());
- setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount());
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COUNT_NAME_ID, UniformValue(qMax((m_environmentLight ? 0 : 1), lightIdx)));
+
+ // If no active light sources and no environment light, add a default light
+ if (m_lightSources.isEmpty() && !m_environmentLight) {
+ // Note: implicit conversion of values to UniformValue
+ if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[0])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[0], 0.5f);
+ } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f);
}
- setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount);
}
+ }
- // Prepare the ShaderParameterPack based on the active uniforms of the shader
- shader->prepareUniforms(command->m_parameterPack);
+ // Environment Light
+ int envLightCount = 0;
+ if (m_environmentLight && m_environmentLight->isEnabled()) {
+ ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(m_environmentLight->shaderData());
+ if (shaderData) {
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight"));
+ envLightCount = 1;
+ }
+ } else {
+ // with some drivers, samplers (like the envbox sampler) need to be bound even though
+ // they may not be actually used, otherwise draw calls can fail
+ static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance"));
+ static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular"));
+ setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount());
+ setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount());
}
+ setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount);
}
bool RenderView::hasBlitFramebufferInfo() const
@@ -1195,7 +1222,7 @@ void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo)
bool RenderView::shouldSkipSubmission() const
{
- if (!m_commands.empty())
+ if (commandCount() > 0)
return false;
if (m_hasBlitFramebufferInfo)
diff --git a/src/plugins/renderers/opengl/renderer/renderview_p.h b/src/plugins/renderers/opengl/renderer/renderview_p.h
index 85dafe3fd..53f76dbfe 100644
--- a/src/plugins/renderers/opengl/renderer/renderview_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderview_p.h
@@ -141,29 +141,29 @@ public:
inline void setDevicePixelRatio(qreal r) Q_DECL_NOTHROW { m_devicePixelRatio = r; }
inline qreal devicePixelRatio() const Q_DECL_NOTHROW { return m_devicePixelRatio; }
- inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW { m_data.m_renderCameraLens = renderCameraLens; }
- inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_data.m_renderCameraLens; }
+ inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW { m_renderCameraLens = renderCameraLens; }
+ inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_renderCameraLens; }
- inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW { m_data.m_renderCameraNode = renderCameraNode; }
- inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_data.m_renderCameraNode; }
+ inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW { m_renderCameraNode = renderCameraNode; }
+ inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_renderCameraNode; }
- inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_data.m_viewMatrix = viewMatrix; }
- inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_data.m_viewMatrix; }
+ inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_viewMatrix = viewMatrix; }
+ inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_viewMatrix; }
- inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_data.m_viewProjectionMatrix = viewProjectionMatrix; }
- inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_data.m_viewProjectionMatrix; }
+ inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_viewProjectionMatrix = viewProjectionMatrix; }
+ inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_viewProjectionMatrix; }
- inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_data.m_eyePos = eyePos; }
- inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_data.m_eyePos; }
+ inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_eyePos = eyePos; }
+ inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_eyePos; }
- inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW { m_data.m_eyeViewDir = dir; }
- inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_data.m_eyeViewDir; }
+ inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW { m_eyeViewDir = dir; }
+ inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_eyeViewDir; }
- inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW { m_data.m_layerFilterIds.push_back(layerFilterId); }
- inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW { return m_data.m_layerFilterIds; }
+ inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW { m_layerFilterIds.push_back(layerFilterId); }
+ inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW { return m_layerFilterIds; }
- inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId) { m_data.m_proximityFilterIds.push_back(proximityFilterId); }
- inline Qt3DCore::QNodeIdVector proximityFilterIds() const { return m_data.m_proximityFilterIds; }
+ inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId) { m_proximityFilterIds.push_back(proximityFilterId); }
+ inline Qt3DCore::QNodeIdVector proximityFilterIds() const { return m_proximityFilterIds; }
inline void appendInsertFenceId(const Qt3DCore::QNodeId setFenceId) { m_insertFenceIds.push_back(setFenceId); }
// We prefix with get to avoid confusion when it is called
@@ -172,14 +172,17 @@ public:
inline void appendWaitFence(const QWaitFenceData &data) { m_waitFences.push_back(data); }
inline QVector<QWaitFenceData> waitFences() const { return m_waitFences; }
- inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW { m_data.m_passFilter = rpFilter; }
- inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_data.m_passFilter; }
+ inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW { m_passFilter = rpFilter; }
+ inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_passFilter; }
- inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW { m_data.m_techniqueFilter = filter; }
- inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_data.m_techniqueFilter; }
+ inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW { m_techniqueFilter = filter; }
+ inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_techniqueFilter; }
- inline RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet; }
- void setStateSet(RenderStateSet *stateSet) Q_DECL_NOTHROW { m_stateSet = stateSet; }
+ inline void setRenderCommandDataView(const EntityRenderCommandDataViewPtr &renderCommandDataView) Q_DECL_NOTHROW { m_renderCommandDataView = renderCommandDataView; }
+ inline EntityRenderCommandDataViewPtr renderCommandDataView() const Q_DECL_NOTHROW { return m_renderCommandDataView; }
+
+ RenderStateSet *getOrCreateStateSet();
+ RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet.data(); }
inline bool noDraw() const Q_DECL_NOTHROW { return m_noDraw; }
void setNoDraw(bool noDraw) Q_DECL_NOTHROW { m_noDraw = noDraw; }
@@ -224,14 +227,7 @@ public:
EntityRenderCommandData buildComputeRenderCommands(const QVector<Entity *> &entities,
int offset, int count) const;
-
- void updateRenderCommand(EntityRenderCommandData *renderCommandData,
- int offset, int count);
-
-
- void setCommands(const QVector<RenderCommand> &commands) Q_DECL_NOTHROW { m_commands = commands; }
- QVector<RenderCommand> &commands() { return m_commands; }
- QVector<RenderCommand> commands() const { return m_commands; }
+ void updateRenderCommand(const EntityRenderCommandDataSubView &subView);
void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; }
const AttachmentPack &attachmentPack() const { return m_attachmentPack; }
@@ -239,7 +235,7 @@ public:
void setRenderTargetId(Qt3DCore::QNodeId renderTargetId) Q_DECL_NOTHROW { m_renderTarget = renderTargetId; }
Qt3DCore::QNodeId renderTargetId() const Q_DECL_NOTHROW { return m_renderTarget; }
- void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes) { m_data.m_sortingTypes.append(sortTypes); }
+ void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes) { m_sortingTypes.append(sortTypes); }
void setSurface(QSurface *surface) { m_surface = surface; }
QSurface *surface() const { return m_surface; }
@@ -257,30 +253,6 @@ public:
void setMemoryBarrier(QMemoryBarrier::Operations barrier) Q_DECL_NOTHROW { m_memoryBarrier = barrier; }
QMemoryBarrier::Operations memoryBarrier() const Q_DECL_NOTHROW { return m_memoryBarrier; }
- // Helps making the size of RenderView smaller
- // Contains all the data needed for the actual building of the RenderView
- // But that aren't used later by the Renderer
- struct InnerData {
- InnerData()
- : m_renderCameraLens(nullptr)
- , m_renderCameraNode(nullptr)
- , m_techniqueFilter(nullptr)
- , m_passFilter(nullptr)
- {
- }
- CameraLens *m_renderCameraLens;
- Entity *m_renderCameraNode;
- const TechniqueFilter *m_techniqueFilter;
- const RenderPassFilter *m_passFilter;
- Matrix4x4 m_viewMatrix;
- Matrix4x4 m_viewProjectionMatrix;
- Qt3DCore::QNodeIdVector m_layerFilterIds;
- QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes;
- Vector3D m_eyePos;
- Vector3D m_eyeViewDir;
- Qt3DCore::QNodeIdVector m_proximityFilterIds;
- };
-
bool isDownloadBuffersEnable() const;
void setIsDownloadBuffersEnable(bool isDownloadBuffersEnable);
@@ -292,53 +264,83 @@ public:
bool shouldSkipSubmission() const;
+ template<typename F>
+ inline void forEachCommand(F func) const
+ {
+ if (!m_renderCommandDataView)
+ return;
+ m_renderCommandDataView->forEachCommand(func);
+ }
+
+ template<typename F>
+ inline void forEachCommand(F func)
+ {
+ if (!m_renderCommandDataView)
+ return;
+ m_renderCommandDataView->forEachCommand(func);
+ }
+
+ inline int commandCount() const { return m_renderCommandDataView ? m_renderCommandDataView->size() : 0; }
+
private:
void setShaderAndUniforms(RenderCommand *command,
- ParameterInfoList &parameters,
- Entity *entity,
- const QVector<LightSource> &activeLightSources,
- EnvironmentLight *environmentLight) const;
+ const ParameterInfoList &parameters,
+ const Entity *entity) const;
+
+ void updateLightUniforms(RenderCommand *command,
+ const Entity *entity) const;
+
mutable QThreadStorage<UniformBlockValueBuilder*> m_localData;
+ Renderer *m_renderer = nullptr;
+ NodeManagers *m_manager = nullptr;
+ EntityRenderCommandDataViewPtr m_renderCommandDataView;
+
+ QSize m_surfaceSize;
+ float m_devicePixelRatio = 1.0f;
+ QRectF m_viewport = QRectF(0.0f, 0.0f, 1.0f, 1.0f);
+ float m_gamma = 2.2f;
+
Qt3DCore::QNodeId m_renderCaptureNodeId;
QRenderCaptureRequest m_renderCaptureRequest;
- bool m_isDownloadBuffersEnable;
+ bool m_isDownloadBuffersEnable = false;
- bool m_hasBlitFramebufferInfo;
+ bool m_hasBlitFramebufferInfo = false;
BlitFramebufferInfo m_blitFrameBufferInfo;
- Renderer *m_renderer;
- NodeManagers *m_manager;
- QSize m_surfaceSize;
- qreal m_devicePixelRatio;
-
- InnerData m_data;
-
- QRectF m_viewport;
- float m_gamma;
+ QSurface *m_surface = nullptr;
Qt3DCore::QNodeId m_renderTarget;
- QSurface *m_surface;
AttachmentPack m_attachmentPack;
- QClearBuffers::BufferTypeFlags m_clearBuffer;
- float m_clearDepthValue;
- int m_clearStencilValue;
+ QClearBuffers::BufferTypeFlags m_clearBuffer = QClearBuffers::None;
+ float m_clearDepthValue = 1.0f;
+ int m_clearStencilValue = 0;
ClearBufferInfo m_globalClearColorBuffer; // global ClearColor
QVector<ClearBufferInfo> m_specificClearColorBuffers; // different draw buffers with distinct colors
- RenderStateSet *m_stateSet;
- bool m_noDraw:1;
- bool m_compute:1;
- bool m_frustumCulling:1;
- bool m_showDebugOverlay:1;
- int m_workGroups[3];
- QMemoryBarrier::Operations m_memoryBarrier;
+
+ QScopedPointer<RenderStateSet> m_stateSet;
+ CameraLens *m_renderCameraLens = nullptr;
+ Entity *m_renderCameraNode = nullptr;
+ const TechniqueFilter *m_techniqueFilter = nullptr;
+ const RenderPassFilter *m_passFilter = nullptr;
+ bool m_noDraw = false;
+ bool m_compute = false;
+ bool m_frustumCulling = false;
+ bool m_showDebugOverlay = false;
+ int m_workGroups[3] = { 1, 1, 1};
+ QMemoryBarrier::Operations m_memoryBarrier = QMemoryBarrier::None;
QVector<Qt3DCore::QNodeId> m_insertFenceIds;
QVector<QWaitFenceData> m_waitFences;
-
- QVector<RenderCommand> m_commands;
- mutable QVector<LightSource> m_lightSources;
- EnvironmentLight *m_environmentLight;
+ QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes;
+ Qt3DCore::QNodeIdVector m_proximityFilterIds;
+ Qt3DCore::QNodeIdVector m_layerFilterIds;
+ Matrix4x4 m_viewMatrix;
+ Matrix4x4 m_viewProjectionMatrix;
+ Vector3D m_eyePos;
+ Vector3D m_eyeViewDir;
MaterialParameterGathererData m_parameters;
+ mutable QVector<LightSource> m_lightSources;
+ EnvironmentLight *m_environmentLight = nullptr;
enum StandardUniform
{
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
index 6ba1ac361..e971c3f3c 100644
--- a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
@@ -114,10 +114,12 @@ class SyncRenderViewPostCommandUpdate
public:
explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob,
const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdateJobs,
- Renderer *renderer)
+ Renderer *renderer,
+ FrameGraphNode *leafNode)
: m_renderViewJob(renderViewJob)
, m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs)
, m_renderer(renderer)
+ , m_leafNode(leafNode)
{}
void operator()()
@@ -125,15 +127,30 @@ public:
// Append all the commands and sort them
RenderView *rv = m_renderViewJob->renderView();
- const EntityRenderCommandDataPtr commandData = m_renderViewCommandUpdaterJobs.first()->renderables();
-
- if (commandData) {
- const QVector<RenderCommand> commands = std::move(commandData->commands);
- rv->setCommands(commands);
+ if (!rv->noDraw()) {
+ RendererCache *cache = m_renderer->cache();
+ RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode];
- // TO DO: Find way to store commands once or at least only when required
- // Sort the commands
+ // Sort command on RenderView
rv->sort();
+
+ // Flip between the 2 EntityRenderCommandDataView on the leaf node case
+ {
+ const int currentViewIdx = writableCacheForLeaf.viewIdx;
+ const int nextViewIdx = 1 - currentViewIdx;
+ EntityRenderCommandDataViewPtr currentDataView = writableCacheForLeaf.filteredRenderCommandDataViews[currentViewIdx];
+
+ // In case the next view has yet to be initialized, we make a copy of the current
+ // view
+ if (writableCacheForLeaf.filteredRenderCommandDataViews[nextViewIdx].isNull()) {
+ EntityRenderCommandDataViewPtr nextDataView = EntityRenderCommandDataViewPtr::create();
+ nextDataView->data = currentDataView->data;
+ nextDataView->indices = currentDataView->indices;
+ writableCacheForLeaf.filteredRenderCommandDataViews[nextViewIdx] = nextDataView;
+ }
+ // Flip index for next frame
+ writableCacheForLeaf.viewIdx = nextViewIdx;
+ }
}
// TO DO: Record the commandData information with the idea of being to
@@ -148,6 +165,7 @@ private:
RenderViewInitializerJobPtr m_renderViewJob;
QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
};
class SyncPreFrustumCulling
@@ -275,11 +293,27 @@ public:
// no conflict
const bool isDraw = !rv->isCompute();
- const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode];
+ RendererCache::LeafNodeData &cacheForLeaf = cache->leafNodeCache[m_leafNode];
const bool fullRebuild = m_rebuildFlags.testFlag(RebuildFlag::FullCommandRebuild);
const bool layerFilteringRebuild = m_rebuildFlags.testFlag(RebuildFlag::LayerCacheRebuild);
const bool lightsCacheRebuild = m_rebuildFlags.testFlag(RebuildFlag::LightCacheRebuild);
+ const bool cameraDirty = cacheForLeaf.viewProjectionMatrix != rv->viewProjectionMatrix();
+ const bool hasProximityFilter = !rv->proximityFilterIds().empty();
+ const bool commandFilteringRequired =
+ fullRebuild ||
+ layerFilteringRebuild ||
+ lightsCacheRebuild ||
+ cameraDirty ||
+ hasProximityFilter ||
+ cacheForLeaf.requestFilteringAtNextFrame;
+
+ // Reset flag on leaf
+ cacheForLeaf.requestFilteringAtNextFrame = false;
+
+ // If we have no filteredRenderCommandDataViews then we should have fullRebuild set to true
+ // otherwise something is wrong
+ Q_ASSERT(fullRebuild || cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx]);
// Rebuild RenderCommands if required
// This should happen fairly infrequently (FrameGraph Change, Geometry/Material change)
@@ -297,92 +331,130 @@ public:
}
// Store new cache
- RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode];
- writableCacheForLeaf.renderCommandData = std::move(commandData);
+ EntityRenderCommandDataViewPtr dataView = EntityRenderCommandDataViewPtr::create();
+ dataView->data = std::move(commandData);
+ // Store the update dataView
+ cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx] = dataView;
+ // Clear the other dataView
+ cacheForLeaf.filteredRenderCommandDataViews[1 - cacheForLeaf.viewIdx].clear();
}
- const EntityRenderCommandData commandData = dataCacheForLeaf.renderCommandData;
// Should be fairly infrequent
if (layerFilteringRebuild || fullRebuild) {
- RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode];
-
// Filter out renderable entities that weren't selected by the layer filters and store that in cache
- writableCacheForLeaf.layeredFilteredRenderables = RenderViewBuilder::entitiesInSubset(
+ cacheForLeaf.layeredFilteredRenderables = RenderViewBuilder::entitiesInSubset(
isDraw ? cache->renderableEntities : cache->computeEntities,
- dataCacheForLeaf.filterEntitiesByLayer);
+ cacheForLeaf.filterEntitiesByLayer);
+ // Set default value for filteredAndCulledRenderables
+ if (isDraw)
+ cacheForLeaf.filteredAndCulledRenderables = cacheForLeaf.layeredFilteredRenderables;
}
+ // Should be fairly infrequent
if (lightsCacheRebuild) {
// Filter out renderable entities that weren't selected by the
// layer filters and store that in cache
- const QVector<Entity *> &layeredFilteredEntities = dataCacheForLeaf.filterEntitiesByLayer;
+ const QVector<Entity *> &layeredFilteredEntities = cacheForLeaf.filterEntitiesByLayer;
QVector<LightSource> filteredLightSources = cache->gatheredLights;
for (int i = 0; i < filteredLightSources.count(); ++i) {
if (!layeredFilteredEntities.contains(filteredLightSources[i].entity))
filteredLightSources.removeAt(i--);
}
- RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode];
- writableCacheForLeaf.layeredFilteredLightSources = filteredLightSources;
+ cacheForLeaf.layeredFilteredLightSources = filteredLightSources;
}
- QVector<Entity *> renderableEntities = dataCacheForLeaf.layeredFilteredRenderables;
- rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer);
+ // This is likely very frequent
+ if (cameraDirty) {
+ // Record the updated viewProjectionMatrix in the cache to allow check to be performed
+ // next frame
+ cacheForLeaf.viewProjectionMatrix = rv->viewProjectionMatrix();
+
+ // Filter out frustum culled entity for drawable entities and store in cache
+ if (isDraw && rv->frustumCulling()) {
+ cacheForLeaf.filteredAndCulledRenderables = RenderViewBuilder::entitiesInSubset(
+ cacheForLeaf.layeredFilteredRenderables,
+ m_frustumCullingJob->visibleEntities());
+ }
+ }
+
+ rv->setMaterialParameterTable(cacheForLeaf.materialParameterGatherer);
rv->setEnvironmentLight(cache->environmentLight);
// Set the light sources, with layer filters applied.
- rv->setLightSources(dataCacheForLeaf.layeredFilteredLightSources);
+ rv->setLightSources(cacheForLeaf.layeredFilteredLightSources);
+
+ QVector<Entity *> renderableEntities = isDraw ? cacheForLeaf.filteredAndCulledRenderables : cacheForLeaf.layeredFilteredRenderables;
+ // TO DO: Find a way to do that only if proximity entities has changed
if (isDraw) {
- // Filter out frustum culled entity for drawable entities
- if (rv->frustumCulling())
- renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_frustumCullingJob->visibleEntities());
// Filter out entities which didn't satisfy proximity filtering
- if (!rv->proximityFilterIds().empty())
- renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities());
+ if (hasProximityFilter)
+ renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities,
+ m_filterProximityJob->filteredEntities());
}
+ EntityRenderCommandDataViewPtr filteredCommandData = cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx];
+
+ // Set RenderCommandDataView on RV (will be used later on to sort commands ...)
+ rv->setRenderCommandDataView(filteredCommandData);
+
// Early return in case we have nothing to filter
if (renderableEntities.size() == 0)
return;
// Filter out Render commands for which the Entity wasn't selected because
// of frustum, proximity or layer filtering
- EntityRenderCommandDataPtr filteredCommandData = EntityRenderCommandDataPtr::create();
- filteredCommandData->reserve(renderableEntities.size());
- // Because dataCacheForLeaf.renderableEntities or computeEntities are sorted
- // What we get out of EntityRenderCommandData is also sorted by Entity
- auto eIt = renderableEntities.cbegin();
- const auto eEnd = renderableEntities.cend();
- int cIt = 0;
- const int cEnd = commandData.size();
-
- while (eIt != eEnd) {
- const Entity *targetEntity = *eIt;
- // Advance until we have commands whose Entity has a lower address
- // than the selected filtered entity
- while (cIt != cEnd && commandData.entities.at(cIt) < targetEntity)
- ++cIt;
-
- // Push pointers to command data for all commands that match the
- // entity
- while (cIt != cEnd && commandData.entities.at(cIt) == targetEntity) {
- filteredCommandData->push_back(commandData.entities.at(cIt),
- commandData.commands.at(cIt),
- commandData.passesData.at(cIt));
- ++cIt;
+ if (commandFilteringRequired) {
+ const std::vector<Entity *> &entities = filteredCommandData->data.entities;
+ // Because cacheForLeaf.renderableEntities or computeEntities are sorted
+ // What we get out of EntityRenderCommandData is also sorted by Entity
+ auto eIt = renderableEntities.cbegin();
+ const auto eEnd = renderableEntities.cend();
+ size_t cIt = 0;
+ const size_t cEnd = entities.size();
+
+ std::vector<size_t> filteredCommandIndices;
+ filteredCommandIndices.reserve(renderableEntities.size());
+
+ while (eIt != eEnd) {
+ const Entity *targetEntity = *eIt;
+ // Advance until we have commands whose Entity has a lower address
+ // than the selected filtered entity
+ while (cIt != cEnd && entities[cIt] < targetEntity)
+ ++cIt;
+
+ // Push pointers to command data for all commands that match the
+ // entity
+ while (cIt != cEnd && entities[cIt] == targetEntity) {
+ filteredCommandIndices.push_back(cIt);
+ ++cIt;
+ }
+ ++eIt;
}
- ++eIt;
+
+ // Store result in cache
+ cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx]->indices = std::move(filteredCommandIndices);
+
+ // Request filtering at next frame (indices for view0 and view1
+ // could mistmatch if something is dirty for frame 0 and not at
+ // frame 1 (given we have 2 views we alternate with)
+ cacheForLeaf.requestFilteringAtNextFrame = true;
}
// Split among the number of command updaters
const int jobCount = m_renderViewCommandUpdaterJobs.size();
- const int idealPacketSize = std::min(std::max(10, filteredCommandData->size() / jobCount), filteredCommandData->size());
- const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize, jobCount);
+ const int commandCount = filteredCommandData->size();
+ const int idealPacketSize = std::min(std::max(10, commandCount), commandCount);
+ const int m = findIdealNumberOfWorkers(commandCount, idealPacketSize, jobCount);
for (int i = 0; i < m; ++i) {
+ // TO DO: Based on whether we had to update the commands
+ // we should be able to know what needs to be recomputed
+ // -> lights/standard uniforms ... might no have to be set over and over again
+ // if they are identical
const RenderViewCommandUpdaterJobPtr &renderViewCommandUpdater = m_renderViewCommandUpdaterJobs.at(i);
- const int count = (i == m - 1) ? filteredCommandData->size() - (i * idealPacketSize) : idealPacketSize;
- renderViewCommandUpdater->setRenderables(filteredCommandData, i * idealPacketSize, count);
+ const size_t count = (i == m - 1) ? commandCount - (i * idealPacketSize) : idealPacketSize;
+ renderViewCommandUpdater->setRenderablesSubView({filteredCommandData, size_t(i * idealPacketSize), count});
}
}
}
@@ -655,7 +727,8 @@ void RenderViewBuilder::prepareJobs()
m_syncRenderViewPostCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPostCommandUpdate(m_renderViewJob,
m_renderViewCommandUpdaterJobs,
- m_renderer),
+ m_renderer,
+ m_leafNode),
JobTypes::SyncRenderViewPostCommandUpdate);
m_syncRenderViewPostInitializationJob = CreateSynchronizerJobPtr(SyncRenderViewPostInitialization(m_renderViewJob,
diff --git a/tests/auto/render/opengl/renderviews/tst_renderviews.cpp b/tests/auto/render/opengl/renderviews/tst_renderviews.cpp
index 0b1e22909..aec13071a 100644
--- a/tests/auto/render/opengl/renderviews/tst_renderviews.cpp
+++ b/tests/auto/render/opengl/renderviews/tst_renderviews.cpp
@@ -78,7 +78,6 @@ private Q_SLOTS:
{
QSKIP("Allocated Disabled");
QVERIFY(sizeof(RenderView) <= 192);
- QVERIFY(sizeof(RenderView::InnerData) <= 192);
}
void checkRenderViewInitialState()
@@ -137,13 +136,40 @@ private Q_SLOTS:
// TO DO: Complete tests for other framegraph node types
}
+ void checkDoesntCrashWhenNoCommandsToSort()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ RenderView renderView;
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderView.setRenderer(&renderer);
+
+ QVector<QSortPolicy::SortType> sortTypes;
+ sortTypes.push_back(QSortPolicy::BackToFront);
+
+ // WHEN
+ renderView.addSortType(sortTypes);
+
+ EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create();
+ renderView.setRenderCommandDataView(view);
+
+ // THEN -> shouldn't crash
+ renderView.sort();
+
+ // RenderCommands are deleted by RenderView dtor
+ renderer.shutdown();
+ }
+
void checkRenderCommandBackToFrontSorting()
{
// GIVEN
Qt3DRender::Render::NodeManagers nodeManagers;
Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
RenderView renderView;
- QVector<RenderCommand> rawCommands;
+ std::vector<RenderCommand> rawCommands;
+
QVector<QSortPolicy::SortType> sortTypes;
renderer.setNodeManagers(&nodeManagers);
@@ -159,14 +185,25 @@ private Q_SLOTS:
// WHEN
renderView.addSortType(sortTypes);
- renderView.setCommands(rawCommands);
+
+ EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create();
+ view->data.commands = rawCommands;
+ view->indices.resize(rawCommands.size());
+ std::iota(view->indices.begin(), view->indices.end(), 0);
+
+ renderView.setRenderCommandDataView(view);
+
renderView.sort();
// THEN
- const QVector<RenderCommand> sortedCommands = renderView.commands();
- QCOMPARE(rawCommands.size(), sortedCommands.size());
- for (int j = 1; j < sortedCommands.size(); ++j)
- QVERIFY(sortedCommands.at(j - 1).m_depth > sortedCommands.at(j).m_depth);
+ int j = 0;
+ RenderCommand previousRC;
+ renderView.forEachCommand([&] (const RenderCommand &command) {
+ if (j > 0)
+ QVERIFY(previousRC.m_depth > command.m_depth);
+ previousRC = command;
+ ++j;
+ });
// RenderCommands are deleted by RenderView dtor
renderer.shutdown();
@@ -178,7 +215,8 @@ private Q_SLOTS:
Qt3DRender::Render::NodeManagers nodeManagers;
Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
RenderView renderView;
- QVector<RenderCommand> rawCommands;
+ std::vector<RenderCommand> rawCommands;
+
QVector<QSortPolicy::SortType> sortTypes;
renderer.setNodeManagers(&nodeManagers);
@@ -202,23 +240,31 @@ private Q_SLOTS:
// WHEN
renderView.addSortType(sortTypes);
- renderView.setCommands(rawCommands);
+
+ EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create();
+ view->data.commands = rawCommands;
+ view->indices.resize(rawCommands.size());
+ std::iota(view->indices.begin(), view->indices.end(), 0);
+
+ renderView.setRenderCommandDataView(view);
+
renderView.sort();
// THEN
- const QVector<RenderCommand> sortedCommands = renderView.commands();
- QCOMPARE(rawCommands.size(), sortedCommands.size());
+ int j = 0;
GLShader *targetShader;
+ RenderCommand previousRC;
- for (int j = 0; j < sortedCommands.size(); ++j) {
-
+ renderView.forEachCommand([&] (const RenderCommand &command) {
if (j % 4 == 0) {
- targetShader = sortedCommands.at(j).m_glShader;
+ targetShader = command.m_glShader;
if (j > 0)
- QVERIFY(targetShader != sortedCommands.at(j - 1).m_glShader);
+ QVERIFY(targetShader != previousRC.m_glShader);
}
- QCOMPARE(targetShader, sortedCommands.at(j).m_glShader);
- }
+ QCOMPARE(targetShader, command.m_glShader);
+ previousRC = command;
+ ++j;
+ });
// RenderCommands are deleted by RenderView dtor
renderer.shutdown();
@@ -280,7 +326,8 @@ private Q_SLOTS:
}
RenderView renderView;
- QVector<RenderCommand> rawCommands;
+ std::vector<RenderCommand> rawCommands;
+
renderView.setRenderer(&renderer);
for (int i = 0, m = shaders.size(); i < m; ++i) {
@@ -292,19 +339,24 @@ private Q_SLOTS:
}
// WHEN
- renderView.setCommands(rawCommands);
renderView.addSortType((QVector<QSortPolicy::SortType>() << QSortPolicy::Uniform));
+
+ EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create();
+ view->data.commands = rawCommands;
+ view->indices.resize(rawCommands.size());
+ std::iota(view->indices.begin(), view->indices.end(), 0);
+
+ renderView.setRenderCommandDataView(view);
+
renderView.sort();
// THEN
- const QVector<RenderCommand> sortedCommands = renderView.commands();
- QCOMPARE(rawCommands, sortedCommands);
-
- for (int i = 0, m = shaders.size(); i < m; ++i) {
- const RenderCommand c = sortedCommands.at(i);
- QCOMPARE(c.m_shaderId, shaders.at(i)->id());
- compareShaderParameterPacks(c.m_parameterPack, expectedMinimizedParameters.at(i));
- }
+ int j = 0;
+ renderView.forEachCommand([&] (const RenderCommand &command) {
+ QCOMPARE(command.m_shaderId, shaders.at(j)->id());
+ compareShaderParameterPacks(command.m_parameterPack, expectedMinimizedParameters.at(j));
+ ++j;
+ });
renderer.shutdown();
}
@@ -316,7 +368,8 @@ private Q_SLOTS:
Qt3DRender::Render::NodeManagers nodeManagers;
Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
RenderView renderView;
- QVector<RenderCommand> rawCommands;
+ std::vector<RenderCommand> rawCommands;
+
QVector<QSortPolicy::SortType> sortTypes;
renderer.setNodeManagers(&nodeManagers);
@@ -332,14 +385,25 @@ private Q_SLOTS:
// WHEN
renderView.addSortType(sortTypes);
- renderView.setCommands(rawCommands);
+
+ EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create();
+ view->data.commands = rawCommands;
+ view->indices.resize(rawCommands.size());
+ std::iota(view->indices.begin(), view->indices.end(), 0);
+
+ renderView.setRenderCommandDataView(view);
+
renderView.sort();
// THEN
- const QVector<RenderCommand> sortedCommands = renderView.commands();
- QCOMPARE(rawCommands.size(), sortedCommands.size());
- for (int j = 1; j < sortedCommands.size(); ++j)
- QVERIFY(sortedCommands.at(j - 1).m_depth < sortedCommands.at(j).m_depth);
+ int j = 0;
+ RenderCommand previousRC;
+ renderView.forEachCommand([&] (const RenderCommand &command) {
+ if (j > 0)
+ QVERIFY(previousRC.m_depth < command.m_depth);
+ previousRC = command;
+ ++j;
+ });
// RenderCommands are deleted by RenderView dtor
renderer.shutdown();
@@ -351,7 +415,8 @@ private Q_SLOTS:
Qt3DRender::Render::NodeManagers nodeManagers;
Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
RenderView renderView;
- QVector<RenderCommand> rawCommands;
+ std::vector<RenderCommand> rawCommands;
+
QVector<QSortPolicy::SortType> sortTypes;
renderer.setNodeManagers(&nodeManagers);
@@ -367,14 +432,26 @@ private Q_SLOTS:
// WHEN
renderView.addSortType(sortTypes);
- renderView.setCommands(rawCommands);
+
+ EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create();
+ view->data.commands = rawCommands;
+ view->indices.resize(rawCommands.size());
+ std::iota(view->indices.begin(), view->indices.end(), 0);
+
+ renderView.setRenderCommandDataView(view);
+
renderView.sort();
// THEN
- const QVector<RenderCommand> sortedCommands = renderView.commands();
- QCOMPARE(rawCommands.size(), sortedCommands.size());
- for (int j = 1; j < sortedCommands.size(); ++j)
- QVERIFY(sortedCommands.at(j - 1).m_changeCost > sortedCommands.at(j).m_changeCost);
+ int j = 0;
+ RenderCommand previousRC;
+ renderView.forEachCommand([&] (const RenderCommand &command) {
+ if (j > 0)
+ QVERIFY(previousRC.m_changeCost > command.m_changeCost);
+ previousRC = command;
+ ++j;
+ });
+
// RenderCommands are deleted by RenderView dtor
renderer.shutdown();
@@ -386,7 +463,8 @@ private Q_SLOTS:
Qt3DRender::Render::NodeManagers nodeManagers;
Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
RenderView renderView;
- QVector<RenderCommand> rawCommands;
+ std::vector<RenderCommand> rawCommands;
+
QVector<QSortPolicy::SortType> sortTypes;
renderer.setNodeManagers(&nodeManagers);
@@ -435,29 +513,38 @@ private Q_SLOTS:
RenderCommand c7 = buildRC(dna[0], depth[2], stateChangeCost[0]);
RenderCommand c6 = buildRC(dna[0], depth[1], stateChangeCost[0]);
- rawCommands << c0 << c1 << c2 << c3 << c4 << c5 << c6 << c7 << c8 << c9;
+ rawCommands = { c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 };
// WHEN
renderView.addSortType(sortTypes);
- renderView.setCommands(rawCommands);
+
+ EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create();
+ view->data.commands = rawCommands;
+ view->indices.resize(rawCommands.size());
+ std::iota(view->indices.begin(), view->indices.end(), 0);
+
+ renderView.setRenderCommandDataView(view);
+
+
renderView.sort();
// THEN
- const QVector<RenderCommand> sortedCommands = renderView.commands();
+ const std::vector<RenderCommand> &sortedCommands = view->data.commands;
+ const std::vector<size_t> &sortedCommandIndices = view->indices;
QCOMPARE(rawCommands.size(), sortedCommands.size());
// Ordered by higher state, higher shaderDNA and higher depth
- QCOMPARE(c0, sortedCommands.at(4));
- QCOMPARE(c3, sortedCommands.at(1));
- QCOMPARE(c4, sortedCommands.at(2));
- QCOMPARE(c5, sortedCommands.at(0));
- QCOMPARE(c8, sortedCommands.at(3));
-
- QCOMPARE(c1, sortedCommands.at(7));
- QCOMPARE(c2, sortedCommands.at(5));
- QCOMPARE(c6, sortedCommands.at(9));
- QCOMPARE(c7, sortedCommands.at(8));
- QCOMPARE(c9, sortedCommands.at(6));
+ QCOMPARE(c0, sortedCommands.at(sortedCommandIndices[4]));
+ QCOMPARE(c3, sortedCommands.at(sortedCommandIndices[1]));
+ QCOMPARE(c4, sortedCommands.at(sortedCommandIndices[2]));
+ QCOMPARE(c5, sortedCommands.at(sortedCommandIndices[0]));
+ QCOMPARE(c8, sortedCommands.at(sortedCommandIndices[3]));
+
+ QCOMPARE(c1, sortedCommands.at(sortedCommandIndices[7]));
+ QCOMPARE(c2, sortedCommands.at(sortedCommandIndices[5]));
+ QCOMPARE(c6, sortedCommands.at(sortedCommandIndices[9]));
+ QCOMPARE(c7, sortedCommands.at(sortedCommandIndices[8]));
+ QCOMPARE(c9, sortedCommands.at(sortedCommandIndices[6]));
// RenderCommands are deleted by RenderView dtor
renderer.shutdown();
@@ -524,21 +611,29 @@ private Q_SLOTS:
}
// WHEN
- QVector<RenderCommand> rawCommands = {a, b, c, d, e, f, g};
+ std::vector<RenderCommand> rawCommands = {a, b, c, d, e, f, g};
renderView.addSortType(sortTypes);
- renderView.setCommands(rawCommands);
+
+ EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create();
+ view->data.commands = rawCommands;
+ view->indices.resize(rawCommands.size());
+ std::iota(view->indices.begin(), view->indices.end(), 0);
+
+ renderView.setRenderCommandDataView(view);
+
renderView.sort();
// THEN
- const QVector<RenderCommand> sortedCommands = renderView.commands();
+ const std::vector<RenderCommand> &sortedCommands = view->data.commands;
+ const std::vector<size_t> &sortedCommandIndices = view->indices;
QCOMPARE(rawCommands.size(), sortedCommands.size());
- QCOMPARE(sortedCommands.at(0), a);
- QCOMPARE(sortedCommands.at(1), g);
- QCOMPARE(sortedCommands.at(2), d);
- QCOMPARE(sortedCommands.at(3), c);
- QCOMPARE(sortedCommands.at(4), e);
- QCOMPARE(sortedCommands.at(5), f);
- QCOMPARE(sortedCommands.at(6), b);
+ QCOMPARE(sortedCommands.at(sortedCommandIndices[0]), a);
+ QCOMPARE(sortedCommands.at(sortedCommandIndices[1]), g);
+ QCOMPARE(sortedCommands.at(sortedCommandIndices[2]), d);
+ QCOMPARE(sortedCommands.at(sortedCommandIndices[3]), c);
+ QCOMPARE(sortedCommands.at(sortedCommandIndices[4]), e);
+ QCOMPARE(sortedCommands.at(sortedCommandIndices[5]), f);
+ QCOMPARE(sortedCommands.at(sortedCommandIndices[6]), b);
// RenderCommands are deleted by RenderView dtor
}
private: