Added QSGRenderNode class.
Change-Id: I8c903cae490158b864af60f53c10c10f2faea7c0 Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
This commit is contained in:
parent
330152354a
commit
cb9a213e54
|
@ -62,8 +62,15 @@ int geometryNodesDrawn;
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static bool nodeLessThan(QSGGeometryNode *a, QSGGeometryNode *b)
|
||||
static bool nodeLessThan(QSGNode *nodeA, QSGNode *nodeB)
|
||||
{
|
||||
if (nodeA->type() != nodeB->type())
|
||||
return nodeA->type() < nodeB->type();
|
||||
if (nodeA->type() != QSGNode::GeometryNodeType)
|
||||
return nodeA < nodeB;
|
||||
QSGGeometryNode *a = static_cast<QSGGeometryNode *>(nodeA);
|
||||
QSGGeometryNode *b = static_cast<QSGGeometryNode *>(nodeA);
|
||||
|
||||
// Sort by clip...
|
||||
if (a->clipList() != b->clipList())
|
||||
return a->clipList() < b->clipList();
|
||||
|
@ -83,8 +90,15 @@ static bool nodeLessThan(QSGGeometryNode *a, QSGGeometryNode *b)
|
|||
return a->matrix() < b->matrix();
|
||||
}
|
||||
|
||||
static bool nodeLessThanWithRenderOrder(QSGGeometryNode *a, QSGGeometryNode *b)
|
||||
static bool nodeLessThanWithRenderOrder(QSGNode *nodeA, QSGNode *nodeB)
|
||||
{
|
||||
if (nodeA->type() != nodeB->type())
|
||||
return nodeA->type() < nodeB->type();
|
||||
if (nodeA->type() != QSGNode::GeometryNodeType)
|
||||
return nodeA < nodeB;
|
||||
QSGGeometryNode *a = static_cast<QSGGeometryNode *>(nodeA);
|
||||
QSGGeometryNode *b = static_cast<QSGGeometryNode *>(nodeA);
|
||||
|
||||
// Sort by clip...
|
||||
if (a->clipList() != b->clipList())
|
||||
return a->clipList() < b->clipList();
|
||||
|
@ -112,23 +126,23 @@ static bool nodeLessThanWithRenderOrder(QSGGeometryNode *a, QSGGeometryNode *b)
|
|||
}
|
||||
|
||||
|
||||
QSGDefaultRenderer::IndexGeometryNodePair::IndexGeometryNodePair(int i, QSGGeometryNode *node)
|
||||
: QPair<int, QSGGeometryNode *>(i, node)
|
||||
QSGDefaultRenderer::IndexNodePair::IndexNodePair(int i, QSGNode *node)
|
||||
: QPair<int, QSGNode *>(i, node)
|
||||
{
|
||||
}
|
||||
|
||||
bool QSGDefaultRenderer::IndexGeometryNodePair::operator < (const QSGDefaultRenderer::IndexGeometryNodePair &other) const
|
||||
bool QSGDefaultRenderer::IndexNodePair::operator < (const QSGDefaultRenderer::IndexNodePair &other) const
|
||||
{
|
||||
return nodeLessThan(second, other.second);
|
||||
}
|
||||
|
||||
|
||||
QSGDefaultRenderer::IndexGeometryNodePairHeap::IndexGeometryNodePairHeap()
|
||||
QSGDefaultRenderer::IndexNodePairHeap::IndexNodePairHeap()
|
||||
: v(64)
|
||||
{
|
||||
}
|
||||
|
||||
void QSGDefaultRenderer::IndexGeometryNodePairHeap::insert(const QSGDefaultRenderer::IndexGeometryNodePair &x)
|
||||
void QSGDefaultRenderer::IndexNodePairHeap::insert(const QSGDefaultRenderer::IndexNodePair &x)
|
||||
{
|
||||
int i = v.size();
|
||||
v.add(x);
|
||||
|
@ -138,9 +152,9 @@ void QSGDefaultRenderer::IndexGeometryNodePairHeap::insert(const QSGDefaultRende
|
|||
}
|
||||
}
|
||||
|
||||
QSGDefaultRenderer::IndexGeometryNodePair QSGDefaultRenderer::IndexGeometryNodePairHeap::pop()
|
||||
QSGDefaultRenderer::IndexNodePair QSGDefaultRenderer::IndexNodePairHeap::pop()
|
||||
{
|
||||
IndexGeometryNodePair x = top();
|
||||
IndexNodePair x = top();
|
||||
if (v.size() > 1)
|
||||
qSwap(v.first(), v.last());
|
||||
v.pop_back();
|
||||
|
@ -163,9 +177,11 @@ QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context)
|
|||
, m_opaqueNodes(64)
|
||||
, m_transparentNodes(64)
|
||||
, m_tempNodes(64)
|
||||
, m_renderGroups(4)
|
||||
, m_rebuild_lists(false)
|
||||
, m_needs_sorting(false)
|
||||
, m_sort_front_to_back(false)
|
||||
, m_render_node_added(false)
|
||||
, m_currentRenderOrder(1)
|
||||
{
|
||||
QStringList args = qApp->arguments();
|
||||
|
@ -250,22 +266,30 @@ void QSGDefaultRenderer::render()
|
|||
if (m_rebuild_lists) {
|
||||
m_opaqueNodes.reset();
|
||||
m_transparentNodes.reset();
|
||||
m_renderGroups.reset();
|
||||
m_currentRenderOrder = 1;
|
||||
buildLists(rootNode());
|
||||
m_rebuild_lists = false;
|
||||
m_render_node_added = false;
|
||||
RenderGroup group = { m_opaqueNodes.size(), m_transparentNodes.size() };
|
||||
m_renderGroups.add(group);
|
||||
}
|
||||
|
||||
#ifdef RENDERER_DEBUG
|
||||
int debugtimeLists = debugTimer.elapsed();
|
||||
#endif
|
||||
|
||||
|
||||
if (m_needs_sorting) {
|
||||
if (!m_opaqueNodes.isEmpty()) {
|
||||
qSort(&m_opaqueNodes.first(), &m_opaqueNodes.first() + m_opaqueNodes.size(),
|
||||
m_sort_front_to_back
|
||||
? nodeLessThanWithRenderOrder
|
||||
: nodeLessThan);
|
||||
bool (*lessThan)(QSGNode *, QSGNode *);
|
||||
lessThan = m_sort_front_to_back ? nodeLessThanWithRenderOrder : nodeLessThan;
|
||||
int start = 0;
|
||||
for (int i = 0; i < m_renderGroups.size(); ++i) {
|
||||
int end = m_renderGroups.at(i).opaqueEnd;
|
||||
if (end != start)
|
||||
qSort(&m_opaqueNodes.first() + start, &m_opaqueNodes.first() + end, lessThan);
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
m_needs_sorting = false;
|
||||
}
|
||||
|
@ -277,60 +301,64 @@ void QSGDefaultRenderer::render()
|
|||
m_renderOrderMatrix.setToIdentity();
|
||||
m_renderOrderMatrix.scale(1, 1, qreal(1) / m_currentRenderOrder);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(true);
|
||||
int opaqueStart = 0;
|
||||
int transparentStart = 0;
|
||||
for (int i = 0; i < m_renderGroups.size(); ++i) {
|
||||
int opaqueEnd = m_renderGroups.at(i).opaqueEnd;
|
||||
int transparentEnd = m_renderGroups.at(i).transparentEnd;
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(true);
|
||||
#ifdef QML_RUNTIME_TESTING
|
||||
if (m_render_opaque_nodes)
|
||||
if (m_render_opaque_nodes)
|
||||
#endif
|
||||
{
|
||||
{
|
||||
#if defined (QML_RUNTIME_TESTING)
|
||||
if (dumpTree)
|
||||
qDebug() << "Opaque Nodes:";
|
||||
if (dumpTree)
|
||||
qDebug() << "Opaque Nodes:";
|
||||
#endif
|
||||
renderNodes(m_opaqueNodes);
|
||||
if (opaqueEnd != opaqueStart)
|
||||
renderNodes(&m_opaqueNodes.first() + opaqueStart, opaqueEnd - opaqueStart);
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(false);
|
||||
#ifdef QML_RUNTIME_TESTING
|
||||
if (m_render_alpha_nodes)
|
||||
#endif
|
||||
{
|
||||
#if defined (QML_RUNTIME_TESTING)
|
||||
if (dumpTree)
|
||||
qDebug() << "Alpha Nodes:";
|
||||
#endif
|
||||
if (transparentEnd != transparentStart)
|
||||
renderNodes(&m_transparentNodes.first() + transparentStart, transparentEnd - transparentStart);
|
||||
}
|
||||
|
||||
opaqueStart = opaqueEnd;
|
||||
transparentStart = transparentEnd;
|
||||
}
|
||||
|
||||
#ifdef RENDERER_DEBUG
|
||||
int debugtimeOpaque = debugTimer.elapsed();
|
||||
int opaqueNodes = geometryNodesDrawn;
|
||||
int opaqueMaterialChanges = materialChanges;
|
||||
int debugtimeRender = debugTimer.elapsed();
|
||||
#endif
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(false);
|
||||
#ifdef QML_RUNTIME_TESTING
|
||||
if (m_render_alpha_nodes)
|
||||
#endif
|
||||
{
|
||||
#if defined (QML_RUNTIME_TESTING)
|
||||
if (dumpTree)
|
||||
qDebug() << "Alpha Nodes:";
|
||||
#endif
|
||||
renderNodes(m_transparentNodes);
|
||||
}
|
||||
|
||||
#ifdef RENDERER_DEBUG
|
||||
int debugtimeAlpha = debugTimer.elapsed();
|
||||
#endif
|
||||
|
||||
|
||||
if (m_currentProgram)
|
||||
m_currentProgram->deactivate();
|
||||
|
||||
#ifdef RENDERER_DEBUG
|
||||
if (debugTimer.elapsed() > DEBUG_THRESHOLD) {
|
||||
printf(" --- Renderer breakdown:\n"
|
||||
" - setup=%d, clear=%d, building=%d, sorting=%d, opaque=%d, alpha=%d\n"
|
||||
" - material changes: opaque=%d, alpha=%d, total=%d\n"
|
||||
" - geometry ndoes: opaque=%d, alpha=%d, total=%d\n",
|
||||
" - setup=%d, clear=%d, building=%d, sorting=%d, render=%d\n"
|
||||
" - material changes: total=%d\n"
|
||||
" - geometry nodes: total=%d\n",
|
||||
debugtimeSetup,
|
||||
debugtimeClear - debugtimeSetup,
|
||||
debugtimeLists - debugtimeClear,
|
||||
debugtimeSorting - debugtimeLists,
|
||||
debugtimeOpaque - debugtimeSorting,
|
||||
debugtimeAlpha - debugtimeOpaque,
|
||||
opaqueMaterialChanges, materialChanges - opaqueMaterialChanges, materialChanges,
|
||||
opaqueNodes, geometryNodesDrawn - opaqueNodes, geometryNodesDrawn);
|
||||
debugtimeRender - debugtimeSorting,
|
||||
materialChanges,
|
||||
geometryNodesDrawn);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -365,10 +393,21 @@ void QSGDefaultRenderer::buildLists(QSGNode *node)
|
|||
geomNode->setRenderOrder(m_currentRenderOrder - 1);
|
||||
m_transparentNodes.add(geomNode);
|
||||
} else {
|
||||
if (m_render_node_added) {
|
||||
// Start new group of nodes so that this opaque node is render on top of the
|
||||
// render node.
|
||||
RenderGroup group = { m_opaqueNodes.size(), m_transparentNodes.size() };
|
||||
m_renderGroups.add(group);
|
||||
m_render_node_added = false;
|
||||
}
|
||||
geomNode->setRenderOrder(m_currentRenderOrder);
|
||||
m_opaqueNodes.add(geomNode);
|
||||
m_currentRenderOrder += 2;
|
||||
}
|
||||
} else if (node->type() == QSGNode::RenderNodeType) {
|
||||
QSGRenderNode *renderNode = static_cast<QSGRenderNode *>(node);
|
||||
m_transparentNodes.add(renderNode);
|
||||
m_render_node_added = true;
|
||||
}
|
||||
|
||||
if (!node->firstChild())
|
||||
|
@ -384,6 +423,7 @@ void QSGDefaultRenderer::buildLists(QSGNode *node)
|
|||
QVarLengthArray<int, 16> beginIndices;
|
||||
QVarLengthArray<int, 16> endIndices;
|
||||
int baseCount = m_transparentNodes.size();
|
||||
int baseGroupCount = m_renderGroups.size();
|
||||
int count = 0;
|
||||
for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) {
|
||||
beginIndices.append(m_transparentNodes.size());
|
||||
|
@ -393,21 +433,22 @@ void QSGDefaultRenderer::buildLists(QSGNode *node)
|
|||
}
|
||||
|
||||
int childNodeCount = m_transparentNodes.size() - baseCount;
|
||||
if (childNodeCount) {
|
||||
// Don't reorder if new render groups were added.
|
||||
if (m_renderGroups.size() == baseGroupCount && childNodeCount) {
|
||||
m_tempNodes.reset();
|
||||
m_tempNodes.reserve(childNodeCount);
|
||||
while (childNodeCount) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (beginIndices[i] != endIndices[i])
|
||||
m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++)));
|
||||
m_heap.insert(IndexNodePair(i, m_transparentNodes.at(beginIndices[i]++)));
|
||||
}
|
||||
while (!m_heap.isEmpty()) {
|
||||
IndexGeometryNodePair pair = m_heap.pop();
|
||||
IndexNodePair pair = m_heap.pop();
|
||||
m_tempNodes.add(pair.second);
|
||||
--childNodeCount;
|
||||
int i = pair.first;
|
||||
if (beginIndices[i] != endIndices[i] && !nodeLessThan(m_transparentNodes.at(beginIndices[i]), pair.second))
|
||||
m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++)));
|
||||
m_heap.insert(IndexNodePair(i, m_transparentNodes.at(beginIndices[i]++)));
|
||||
}
|
||||
}
|
||||
Q_ASSERT(m_tempNodes.size() == m_transparentNodes.size() - baseCount);
|
||||
|
@ -420,105 +461,182 @@ void QSGDefaultRenderer::buildLists(QSGNode *node)
|
|||
}
|
||||
}
|
||||
|
||||
void QSGDefaultRenderer::renderNodes(const QDataBuffer<QSGGeometryNode *> &list)
|
||||
void QSGDefaultRenderer::renderNodes(QSGNode *const *nodes, int count)
|
||||
{
|
||||
const float scale = 1.0f / m_currentRenderOrder;
|
||||
int count = list.size();
|
||||
int currentRenderOrder = 0x80000000;
|
||||
m_current_projection_matrix.setColumn(2, scale * projectionMatrix().column(2));
|
||||
ClipType currentClipType = NoClip;
|
||||
QMatrix4x4 projection = projectionMatrix();
|
||||
m_current_projection_matrix.setColumn(2, scale * projection.column(2));
|
||||
|
||||
//int clipChangeCount = 0;
|
||||
//int programChangeCount = 0;
|
||||
//int materialChangeCount = 0;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
QSGGeometryNode *geomNode = list.at(i);
|
||||
if (nodes[i]->type() == QSGNode::RenderNodeType) {
|
||||
QSGRenderNode *renderNode = static_cast<QSGRenderNode *>(nodes[i]);
|
||||
|
||||
QSGMaterialShader::RenderState::DirtyStates updates;
|
||||
|
||||
#if defined (QML_RUNTIME_TESTING)
|
||||
static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree"));
|
||||
if (dumpTree)
|
||||
qDebug() << geomNode;
|
||||
#endif
|
||||
|
||||
bool changeMatrix = m_currentMatrix != geomNode->matrix();
|
||||
|
||||
if (changeMatrix) {
|
||||
m_currentMatrix = geomNode->matrix();
|
||||
if (m_currentMatrix)
|
||||
m_current_model_view_matrix = *m_currentMatrix;
|
||||
else
|
||||
m_current_model_view_matrix.setToIdentity();
|
||||
m_current_determinant = m_current_model_view_matrix.determinant();
|
||||
updates |= QSGMaterialShader::RenderState::DirtyMatrix;
|
||||
}
|
||||
|
||||
bool changeOpacity = m_current_opacity != geomNode->inheritedOpacity();
|
||||
if (changeOpacity) {
|
||||
updates |= QSGMaterialShader::RenderState::DirtyOpacity;
|
||||
m_current_opacity = geomNode->inheritedOpacity();
|
||||
}
|
||||
|
||||
Q_ASSERT(geomNode->activeMaterial());
|
||||
|
||||
QSGMaterial *material = geomNode->activeMaterial();
|
||||
QSGMaterialShader *program = m_context->prepareMaterial(material);
|
||||
Q_ASSERT(program->program()->isLinked());
|
||||
|
||||
bool changeClip = geomNode->clipList() != m_currentClip;
|
||||
QSGRenderer::ClipType clipType = QSGRenderer::NoClip;
|
||||
if (changeClip) {
|
||||
// The clip function relies on there not being any depth testing..
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
clipType = updateStencilClip(geomNode->clipList());
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
m_currentClip = geomNode->clipList();
|
||||
#ifdef FORCE_NO_REORDER
|
||||
glDepthMask(false);
|
||||
#else
|
||||
glDepthMask((material->flags() & QSGMaterial::Blending) == 0 && m_current_opacity == 1);
|
||||
#endif
|
||||
//++clipChangeCount;
|
||||
}
|
||||
|
||||
bool changeProgram = (changeClip && clipType == QSGRenderer::StencilClip) || m_currentProgram != program;
|
||||
if (changeProgram) {
|
||||
if (m_currentProgram)
|
||||
m_currentProgram->deactivate();
|
||||
m_currentProgram = program;
|
||||
m_currentProgram->activate();
|
||||
//++programChangeCount;
|
||||
updates |= (QSGMaterialShader::RenderState::DirtyMatrix | QSGMaterialShader::RenderState::DirtyOpacity);
|
||||
m_currentMaterial = 0;
|
||||
m_currentProgram = 0;
|
||||
m_currentMatrix = 0;
|
||||
currentRenderOrder = 0x80000000;
|
||||
|
||||
bool changeClip = renderNode->clipList() != m_currentClip;
|
||||
// The clip function relies on there not being any depth testing..
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
if (changeClip) {
|
||||
currentClipType = updateStencilClip(renderNode->clipList());
|
||||
m_currentClip = renderNode->clipList();
|
||||
//++clipChangeCount;
|
||||
}
|
||||
|
||||
glDepthMask(false);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
QSGRenderNode::RenderState state;
|
||||
state.projectionMatrix = &projection;
|
||||
state.scissorEnabled = currentClipType & ScissorClip;
|
||||
state.stencilEnabled = currentClipType & StencilClip;
|
||||
state.scissorRect = m_current_scissor_rect;
|
||||
state.stencilValue = m_current_stencil_value;
|
||||
|
||||
renderNode->render(state);
|
||||
|
||||
QSGRenderNode::StateFlags changes = renderNode->changedStates();
|
||||
if (changes & QSGRenderNode::ViewportState) {
|
||||
QRect r = viewportRect();
|
||||
glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
|
||||
}
|
||||
if (changes & QSGRenderNode::StencilState) {
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
glStencilMask(0xff);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
if (changes & (QSGRenderNode::StencilState | QSGRenderNode::ScissorState)) {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
m_currentClip = 0;
|
||||
currentClipType = NoClip;
|
||||
}
|
||||
if (changes & QSGRenderNode::DepthState) {
|
||||
#if defined(QT_OPENGL_ES)
|
||||
glClearDepthf(0);
|
||||
#else
|
||||
glClearDepth(0);
|
||||
#endif
|
||||
if (m_clear_mode & QSGRenderer::ClearDepthBuffer) {
|
||||
glDepthMask(true);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
glDepthMask(false);
|
||||
glDepthFunc(GL_GREATER);
|
||||
}
|
||||
if (changes & QSGRenderNode::ColorState)
|
||||
bindable()->reactivate();
|
||||
if (changes & QSGRenderNode::BlendState) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
if (changes & QSGRenderNode::CullState) {
|
||||
glFrontFace(isMirrored() ? GL_CW : GL_CCW);
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
m_current_model_view_matrix.setToIdentity();
|
||||
m_current_determinant = 1;
|
||||
} else if (nodes[i]->type() == QSGNode::GeometryNodeType) {
|
||||
QSGGeometryNode *geomNode = static_cast<QSGGeometryNode *>(nodes[i]);
|
||||
|
||||
QSGMaterialShader::RenderState::DirtyStates updates;
|
||||
|
||||
#if defined (QML_RUNTIME_TESTING)
|
||||
static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree"));
|
||||
if (dumpTree)
|
||||
qDebug() << geomNode;
|
||||
#endif
|
||||
|
||||
bool changeMatrix = m_currentMatrix != geomNode->matrix();
|
||||
|
||||
if (changeMatrix) {
|
||||
m_currentMatrix = geomNode->matrix();
|
||||
if (m_currentMatrix)
|
||||
m_current_model_view_matrix = *m_currentMatrix;
|
||||
else
|
||||
m_current_model_view_matrix.setToIdentity();
|
||||
m_current_determinant = m_current_model_view_matrix.determinant();
|
||||
updates |= QSGMaterialShader::RenderState::DirtyMatrix;
|
||||
}
|
||||
|
||||
bool changeOpacity = m_current_opacity != geomNode->inheritedOpacity();
|
||||
if (changeOpacity) {
|
||||
updates |= QSGMaterialShader::RenderState::DirtyOpacity;
|
||||
m_current_opacity = geomNode->inheritedOpacity();
|
||||
}
|
||||
|
||||
Q_ASSERT(geomNode->activeMaterial());
|
||||
|
||||
QSGMaterial *material = geomNode->activeMaterial();
|
||||
QSGMaterialShader *program = m_context->prepareMaterial(material);
|
||||
Q_ASSERT(program->program()->isLinked());
|
||||
|
||||
bool changeClip = geomNode->clipList() != m_currentClip;
|
||||
if (changeClip) {
|
||||
// The clip function relies on there not being any depth testing..
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
currentClipType = updateStencilClip(geomNode->clipList());
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
m_currentClip = geomNode->clipList();
|
||||
#ifdef FORCE_NO_REORDER
|
||||
glDepthMask(false);
|
||||
#else
|
||||
glDepthMask((material->flags() & QSGMaterial::Blending) == 0 && m_current_opacity == 1);
|
||||
#endif
|
||||
//++clipChangeCount;
|
||||
}
|
||||
|
||||
bool changeProgram = (changeClip && (currentClipType & StencilClip)) || m_currentProgram != program;
|
||||
if (changeProgram) {
|
||||
if (m_currentProgram)
|
||||
m_currentProgram->deactivate();
|
||||
m_currentProgram = program;
|
||||
m_currentProgram->activate();
|
||||
//++programChangeCount;
|
||||
updates |= (QSGMaterialShader::RenderState::DirtyMatrix | QSGMaterialShader::RenderState::DirtyOpacity);
|
||||
|
||||
#ifdef RENDERER_DEBUG
|
||||
materialChanges++;
|
||||
materialChanges++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool changeRenderOrder = currentRenderOrder != geomNode->renderOrder();
|
||||
if (changeRenderOrder) {
|
||||
currentRenderOrder = geomNode->renderOrder();
|
||||
m_current_projection_matrix.setColumn(3, projectionMatrix().column(3)
|
||||
+ currentRenderOrder
|
||||
* m_current_projection_matrix.column(2));
|
||||
updates |= QSGMaterialShader::RenderState::DirtyMatrix;
|
||||
}
|
||||
bool changeRenderOrder = currentRenderOrder != geomNode->renderOrder();
|
||||
if (changeRenderOrder) {
|
||||
currentRenderOrder = geomNode->renderOrder();
|
||||
m_current_projection_matrix.setColumn(3, projection.column(3)
|
||||
+ currentRenderOrder
|
||||
* m_current_projection_matrix.column(2));
|
||||
updates |= QSGMaterialShader::RenderState::DirtyMatrix;
|
||||
}
|
||||
|
||||
if (changeProgram || m_currentMaterial != material) {
|
||||
program->updateState(state(updates), material, changeProgram ? 0 : m_currentMaterial);
|
||||
m_currentMaterial = material;
|
||||
//++materialChangeCount;
|
||||
}
|
||||
if (changeProgram || m_currentMaterial != material) {
|
||||
program->updateState(state(updates), material, changeProgram ? 0 : m_currentMaterial);
|
||||
m_currentMaterial = material;
|
||||
//++materialChangeCount;
|
||||
}
|
||||
|
||||
//glDepthRange((geomNode->renderOrder() + 0.1) * scale, (geomNode->renderOrder() + 0.9) * scale);
|
||||
//glDepthRange((geomNode->renderOrder() + 0.1) * scale, (geomNode->renderOrder() + 0.9) * scale);
|
||||
|
||||
const QSGGeometry *g = geomNode->geometry();
|
||||
draw(program, g);
|
||||
const QSGGeometry *g = geomNode->geometry();
|
||||
draw(program, g);
|
||||
|
||||
#ifdef RENDERER_DEBUG
|
||||
geometryNodesDrawn++;
|
||||
geometryNodesDrawn++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//qDebug("Clip: %i, shader program: %i, material: %i times changed while drawing %s items",
|
||||
// clipChangeCount, programChangeCount, materialChangeCount,
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "qsgrenderer_p.h"
|
||||
|
||||
#include <QtGui/private/qdatabuffer_p.h>
|
||||
#include "qsgrendernode_p.h"
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
|
@ -54,28 +55,28 @@ class QSGDefaultRenderer : public QSGRenderer
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class IndexGeometryNodePair : public QPair<int, QSGGeometryNode *>
|
||||
class IndexNodePair : public QPair<int, QSGNode *>
|
||||
{
|
||||
public:
|
||||
IndexGeometryNodePair(int i, QSGGeometryNode *n);
|
||||
bool operator < (const IndexGeometryNodePair &other) const;
|
||||
IndexNodePair(int i, QSGNode *n);
|
||||
bool operator < (const IndexNodePair &other) const;
|
||||
};
|
||||
|
||||
|
||||
// Minimum heap.
|
||||
class IndexGeometryNodePairHeap
|
||||
class IndexNodePairHeap
|
||||
{
|
||||
public:
|
||||
IndexGeometryNodePairHeap();
|
||||
void insert(const IndexGeometryNodePair &x);
|
||||
const IndexGeometryNodePair &top() const { return v.first(); }
|
||||
IndexGeometryNodePair pop();
|
||||
IndexNodePairHeap();
|
||||
void insert(const IndexNodePair &x);
|
||||
const IndexNodePair &top() const { return v.first(); }
|
||||
IndexNodePair pop();
|
||||
bool isEmpty() const { return v.isEmpty(); }
|
||||
private:
|
||||
static int parent(int i) { return (i - 1) >> 1; }
|
||||
static int left(int i) { return (i << 1) | 1; }
|
||||
static int right(int i) { return (i + 1) << 1; }
|
||||
QDataBuffer<IndexGeometryNodePair> v;
|
||||
QDataBuffer<IndexNodePair> v;
|
||||
};
|
||||
|
||||
QSGDefaultRenderer(QSGContext *context);
|
||||
|
@ -89,21 +90,24 @@ public:
|
|||
|
||||
private:
|
||||
void buildLists(QSGNode *node);
|
||||
void renderNodes(const QDataBuffer<QSGGeometryNode *> &list);
|
||||
void renderNodes(QSGNode *const *nodes, int count);
|
||||
|
||||
const QSGClipNode *m_currentClip;
|
||||
QSGMaterial *m_currentMaterial;
|
||||
QSGMaterialShader *m_currentProgram;
|
||||
const QMatrix4x4 *m_currentMatrix;
|
||||
QMatrix4x4 m_renderOrderMatrix;
|
||||
QDataBuffer<QSGGeometryNode *> m_opaqueNodes;
|
||||
QDataBuffer<QSGGeometryNode *> m_transparentNodes;
|
||||
QDataBuffer<QSGGeometryNode *> m_tempNodes;
|
||||
IndexGeometryNodePairHeap m_heap;
|
||||
QDataBuffer<QSGNode *> m_opaqueNodes;
|
||||
QDataBuffer<QSGNode *> m_transparentNodes;
|
||||
QDataBuffer<QSGNode *> m_tempNodes;
|
||||
struct RenderGroup { int opaqueEnd, transparentEnd; };
|
||||
QDataBuffer<RenderGroup> m_renderGroups;
|
||||
IndexNodePairHeap m_heap;
|
||||
|
||||
bool m_rebuild_lists;
|
||||
bool m_needs_sorting;
|
||||
bool m_sort_front_to_back;
|
||||
bool m_render_node_added;
|
||||
int m_currentRenderOrder;
|
||||
|
||||
#ifdef QML_RUNTIME_TESTING
|
||||
|
|
|
@ -90,7 +90,7 @@ QSGNode::QSGNode()
|
|||
, m_lastChild(0)
|
||||
, m_nextSibling(0)
|
||||
, m_previousSibling(0)
|
||||
, m_subtreeGeometryCount(0)
|
||||
, m_subtreeRenderableCount(0)
|
||||
, m_nodeFlags(OwnedByParent)
|
||||
, m_dirtyState(0)
|
||||
{
|
||||
|
@ -104,7 +104,7 @@ QSGNode::QSGNode(NodeType type)
|
|||
, m_lastChild(0)
|
||||
, m_nextSibling(0)
|
||||
, m_previousSibling(0)
|
||||
, m_subtreeGeometryCount(type == GeometryNodeType ? 1 : 0)
|
||||
, m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0)
|
||||
, m_nodeFlags(OwnedByParent)
|
||||
, m_dirtyState(0)
|
||||
{
|
||||
|
@ -173,7 +173,7 @@ QSGNode::~QSGNode()
|
|||
|
||||
bool QSGNode::isSubtreeBlocked() const
|
||||
{
|
||||
return m_subtreeGeometryCount == 0;
|
||||
return m_subtreeRenderableCount == 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -471,16 +471,16 @@ void QSGNode::markDirty(DirtyState bits)
|
|||
|
||||
DirtyState subtreeBits = DirtyState((bits & DirtyPropagationMask) << 16);
|
||||
|
||||
int geometryCountDiff = 0;
|
||||
int renderableCountDiff = 0;
|
||||
if (bits & DirtyNodeAdded)
|
||||
geometryCountDiff += m_subtreeGeometryCount;
|
||||
renderableCountDiff += m_subtreeRenderableCount;
|
||||
if (bits & DirtyNodeRemoved)
|
||||
geometryCountDiff -= m_subtreeGeometryCount;
|
||||
renderableCountDiff -= m_subtreeRenderableCount;
|
||||
|
||||
QSGNode *p = m_parent;
|
||||
while (p) {
|
||||
p->m_dirtyState |= subtreeBits;
|
||||
p->m_subtreeGeometryCount += geometryCountDiff;
|
||||
p->m_subtreeRenderableCount += renderableCountDiff;
|
||||
if (p->type() == RootNodeType)
|
||||
static_cast<QSGRootNode *>(p)->notifyNodeChange(this, bits);
|
||||
p = p->m_parent;
|
||||
|
|
|
@ -71,6 +71,9 @@ public:
|
|||
TransformNodeType,
|
||||
ClipNodeType,
|
||||
OpacityNodeType,
|
||||
#ifndef Q_QDOC
|
||||
RenderNodeType, // internal
|
||||
#endif
|
||||
UserNodeType = 1024
|
||||
};
|
||||
|
||||
|
@ -165,7 +168,7 @@ private:
|
|||
QSGNode *m_lastChild;
|
||||
QSGNode *m_nextSibling;
|
||||
QSGNode *m_previousSibling;
|
||||
int m_subtreeGeometryCount;
|
||||
int m_subtreeRenderableCount;
|
||||
|
||||
Flags m_nodeFlags;
|
||||
DirtyState m_dirtyState;
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include "qsgnodeupdater_p.h"
|
||||
#include "qsgnode.h"
|
||||
#include "qsgrendernode_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
@ -204,6 +206,26 @@ void QSGNodeUpdater::leaveGeometryNode(QSGGeometryNode *g)
|
|||
#endif
|
||||
}
|
||||
|
||||
void QSGNodeUpdater::enterRenderNode(QSGRenderNode *r)
|
||||
{
|
||||
#ifdef QSG_UPDATER_DEBUG
|
||||
qDebug() << "enter render:" << r;
|
||||
#endif
|
||||
|
||||
r->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last();
|
||||
r->m_clip_list = m_current_clip;
|
||||
r->setInheritedOpacity(m_opacity_stack.last());
|
||||
}
|
||||
|
||||
void QSGNodeUpdater::leaveRenderNode(QSGRenderNode *r)
|
||||
{
|
||||
#ifdef QSG_UPDATER_DEBUG
|
||||
qDebug() << "leave render" << r;
|
||||
#else
|
||||
Q_UNUSED(r)
|
||||
#endif
|
||||
}
|
||||
|
||||
void QSGNodeUpdater::enterOpacityNode(QSGOpacityNode *o)
|
||||
{
|
||||
if (o->dirtyState() & QSGNode::DirtyOpacity)
|
||||
|
@ -263,6 +285,12 @@ void QSGNodeUpdater::visitNode(QSGNode *n)
|
|||
visitChildren(g);
|
||||
leaveGeometryNode(g);
|
||||
break; }
|
||||
case QSGNode::RenderNodeType: {
|
||||
QSGRenderNode *r = static_cast<QSGRenderNode *>(n);
|
||||
enterRenderNode(r);
|
||||
visitChildren(r);
|
||||
leaveRenderNode(r);
|
||||
break; }
|
||||
case QSGNode::ClipNodeType: {
|
||||
QSGClipNode *c = static_cast<QSGClipNode *>(n);
|
||||
enterClipNode(c);
|
||||
|
|
|
@ -42,11 +42,21 @@
|
|||
#ifndef NODEUPDATER_P_H
|
||||
#define NODEUPDATER_P_H
|
||||
|
||||
#include "qsgnode.h"
|
||||
#include <QtQuick/qtquickglobal.h>
|
||||
#include <QtGui/private/qdatabuffer_p.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QSGNode;
|
||||
class QSGTransformNode;
|
||||
class QSGClipNode;
|
||||
class QSGOpacityNode;
|
||||
class QSGGeometryNode;
|
||||
class QMatrix4x4;
|
||||
class QSGRenderNode;
|
||||
|
||||
class Q_QUICK_EXPORT QSGNodeUpdater
|
||||
{
|
||||
public:
|
||||
|
@ -68,6 +78,8 @@ protected:
|
|||
void leaveOpacityNode(QSGOpacityNode *o);
|
||||
void enterGeometryNode(QSGGeometryNode *);
|
||||
void leaveGeometryNode(QSGGeometryNode *);
|
||||
void enterRenderNode(QSGRenderNode *);
|
||||
void leaveRenderNode(QSGRenderNode *);
|
||||
|
||||
void visitNode(QSGNode *n);
|
||||
void visitChildren(QSGNode *n);
|
||||
|
@ -84,4 +96,6 @@ protected:
|
|||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // NODEUPDATER_P_H
|
||||
|
|
|
@ -138,6 +138,7 @@ QSGRenderer::QSGRenderer(QSGContext *context)
|
|||
, m_clear_mode(ClearColorBuffer | ClearDepthBuffer)
|
||||
, m_current_opacity(1)
|
||||
, m_current_determinant(1)
|
||||
, m_current_stencil_value(0)
|
||||
, m_context(context)
|
||||
, m_root_node(0)
|
||||
, m_node_updater(0)
|
||||
|
@ -425,13 +426,12 @@ QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
|
|||
return NoClip;
|
||||
}
|
||||
|
||||
bool stencilEnabled = false;
|
||||
bool scissorEnabled = false;
|
||||
ClipType clipType = NoClip;
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
int clipDepth = 0;
|
||||
QRect clipRect;
|
||||
m_current_stencil_value = 0;
|
||||
m_current_scissor_rect = QRect();
|
||||
while (clip) {
|
||||
QMatrix4x4 m = m_current_projection_matrix;
|
||||
if (clip->matrix())
|
||||
|
@ -470,17 +470,17 @@ QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
|
|||
GLint ix2 = qRound((fx2 + 1) * m_device_rect.width() * qreal(0.5));
|
||||
GLint iy2 = qRound((fy2 + 1) * m_device_rect.height() * qreal(0.5));
|
||||
|
||||
if (!scissorEnabled) {
|
||||
clipRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
|
||||
if (!(clipType & ScissorClip)) {
|
||||
m_current_scissor_rect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
scissorEnabled = true;
|
||||
clipType |= ScissorClip;
|
||||
} else {
|
||||
clipRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
|
||||
m_current_scissor_rect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
|
||||
}
|
||||
|
||||
glScissor(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());
|
||||
glScissor(m_current_scissor_rect.x(), m_current_scissor_rect.y(),
|
||||
m_current_scissor_rect.width(), m_current_scissor_rect.height());
|
||||
} else {
|
||||
if (!stencilEnabled) {
|
||||
if (!(clipType & StencilClip)) {
|
||||
if (!m_clip_program.isLinked()) {
|
||||
m_clip_program.addShaderFromSourceCode(QOpenGLShader::Vertex,
|
||||
"attribute highp vec4 vCoord; \n"
|
||||
|
@ -497,20 +497,28 @@ QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
|
|||
m_clip_matrix_id = m_clip_program.uniformLocation("matrix");
|
||||
}
|
||||
|
||||
glStencilMask(0xff); // write mask
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
if (m_vertex_buffer_bound) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
m_vertex_buffer_bound = false;
|
||||
}
|
||||
if (m_index_buffer_bound) {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
m_index_buffer_bound = false;
|
||||
}
|
||||
|
||||
m_clip_program.bind();
|
||||
m_clip_program.enableAttributeArray(0);
|
||||
|
||||
stencilEnabled = true;
|
||||
clipType |= StencilClip;
|
||||
}
|
||||
|
||||
glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
|
||||
glStencilFunc(GL_EQUAL, m_current_stencil_value, 0xff); // stencil test, ref, test mask
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass
|
||||
|
||||
const QSGGeometry *g = clip->geometry();
|
||||
|
@ -525,26 +533,22 @@ QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
|
|||
glDrawArrays(g->drawingMode(), 0, g->vertexCount());
|
||||
}
|
||||
|
||||
++clipDepth;
|
||||
++m_current_stencil_value;
|
||||
}
|
||||
|
||||
clip = clip->clipList();
|
||||
}
|
||||
|
||||
if (stencilEnabled) {
|
||||
if (clipType & StencilClip) {
|
||||
m_clip_program.disableAttributeArray(0);
|
||||
glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
|
||||
glStencilFunc(GL_EQUAL, m_current_stencil_value, 0xff); // stencil test, ref, test mask
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass
|
||||
glStencilMask(0); // write mask
|
||||
bindable()->reactivate();
|
||||
} else {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
if (!scissorEnabled)
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
return stencilEnabled ? StencilClip : ScissorClip;
|
||||
return clipType;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -70,12 +70,13 @@ class Q_QUICK_EXPORT QSGRenderer : public QObject, public QOpenGLFunctions
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ClipType
|
||||
enum ClipTypeBit
|
||||
{
|
||||
NoClip,
|
||||
ScissorClip,
|
||||
StencilClip
|
||||
NoClip = 0x00,
|
||||
ScissorClip = 0x01,
|
||||
StencilClip = 0x02
|
||||
};
|
||||
Q_DECLARE_FLAGS(ClipType, ClipTypeBit)
|
||||
|
||||
enum ClearModeBit
|
||||
{
|
||||
|
@ -155,6 +156,8 @@ protected:
|
|||
QMatrix4x4 m_current_model_view_matrix;
|
||||
qreal m_current_opacity;
|
||||
qreal m_current_determinant;
|
||||
QRect m_current_scissor_rect;
|
||||
int m_current_stencil_value;
|
||||
|
||||
QSGContext *m_context;
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtDeclarative module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qsgrendernode_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QSGRenderNode::QSGRenderNode()
|
||||
: QSGNode(RenderNodeType)
|
||||
, m_matrix(0)
|
||||
, m_clip_list(0)
|
||||
, m_opacity(1)
|
||||
{
|
||||
}
|
||||
|
||||
void QSGRenderNode::setInheritedOpacity(qreal opacity)
|
||||
{
|
||||
Q_ASSERT(opacity >= 0 && opacity <= 1);
|
||||
m_opacity = opacity;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QSGRenderNode::StateFlags QSGRenderNode::changedStates()
|
||||
|
||||
This function should return a mask where each bit represents OpenGL states changed by
|
||||
the \l render() function:
|
||||
\list
|
||||
\o DepthState - depth write mask, depth test enabled, depth comparison function
|
||||
\o StencilState - stencil write masks, stencil test enabled, stencil operations,
|
||||
stencil comparison functions
|
||||
\o ScissorState - scissor enabled, scissor test enabled
|
||||
\o ColorState - clear color, color write mask
|
||||
\o BlendState - blend enabled, blend function
|
||||
\o CullState - front face, cull face enabled
|
||||
\o ViewportState - viewport
|
||||
\endlist
|
||||
|
||||
The function is called by the renderer so it can reset the OpenGL states after rendering this
|
||||
node.
|
||||
|
||||
\internal
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QSGRenderNode::render(const RenderState &state)
|
||||
|
||||
This function is called by the renderer and should paint this node with OpenGL commands.
|
||||
|
||||
The states necessary for clipping has already been set before the function is called.
|
||||
The clip is a combination of a stencil clip and scissor clip. Information about the clip is
|
||||
found in \a state.
|
||||
|
||||
The effective opacity can be retrieved with \l inheritedOpacity().
|
||||
|
||||
The projection matrix is available through \a state, while the model-view matrix can be
|
||||
fetched with \l matrix(). The combined matrix is then the projection matrix times the
|
||||
model-view matrix.
|
||||
|
||||
The following states are set before this function is called:
|
||||
\list
|
||||
\o glDepthMask(false)
|
||||
\o glDisable(GL_DEPTH_TEST)
|
||||
\o glStencilMask(0)
|
||||
\o glEnable(GL_STENCIL_TEST)/glDisable(GL_STENCIL_TEST) depending on clip
|
||||
\o glStencilFunc(GL_EQUAL, state.stencilValue, 0xff) depending on clip
|
||||
\o glEnable(GL_SCISSOR_TEST)/glDisable(GL_SCISSOR_TEST) depending on clip
|
||||
\o glScissor(state.scissorRect.x(), state.scissorRect.y(),
|
||||
state.scissorRect.width(), state.scissorRect.height()) depending on clip
|
||||
\o glEnable(GL_BLEND)
|
||||
\o glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
|
||||
\o glDisable(GL_CULL_FACE)
|
||||
\endlist
|
||||
|
||||
States that are not listed above, but are included in \l StateFlags, can have arbitrary
|
||||
values.
|
||||
|
||||
\l changedStates() should return which states this function has changed. If a state is not
|
||||
covered by \l StateFlags, the state should be set to the default value according to the
|
||||
OpenGL specification.
|
||||
|
||||
\internal
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -0,0 +1,114 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtDeclarative module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QSGRENDERNODE_P_H
|
||||
#define QSGRENDERNODE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qsgnode.h"
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_QUICK_EXPORT QSGRenderNode : public QSGNode
|
||||
{
|
||||
public:
|
||||
enum StateFlag
|
||||
{
|
||||
DepthState = 0x01, // depth mask, depth test enable, depth func, clear depth
|
||||
StencilState = 0x02, // stencil mask, stencil test enable, stencil op, stencil func, clear stencil
|
||||
ScissorState = 0x04, // scissor enable, scissor test enable
|
||||
ColorState = 0x08, // clear color, color mask
|
||||
BlendState = 0x10, // blend enable, blend func
|
||||
CullState = 0x20, // front face, cull face enable
|
||||
ViewportState = 0x40 // viewport
|
||||
};
|
||||
Q_DECLARE_FLAGS(StateFlags, StateFlag)
|
||||
|
||||
struct RenderState
|
||||
{
|
||||
// The model-view matrix can be retrieved with matrix().
|
||||
// The opacity can be retrieved with inheritedOpacity().
|
||||
const QMatrix4x4 *projectionMatrix;
|
||||
QRect scissorRect;
|
||||
int stencilValue;
|
||||
|
||||
bool stencilEnabled;
|
||||
bool scissorEnabled;
|
||||
};
|
||||
|
||||
QSGRenderNode();
|
||||
|
||||
virtual StateFlags changedStates() = 0;
|
||||
virtual void render(const RenderState &state) = 0;
|
||||
|
||||
const QMatrix4x4 *matrix() const { return m_matrix; }
|
||||
const QSGClipNode *clipList() const { return m_clip_list; }
|
||||
|
||||
void setInheritedOpacity(qreal opacity);
|
||||
qreal inheritedOpacity() const { return m_opacity; }
|
||||
|
||||
private:
|
||||
friend class QSGNodeUpdater;
|
||||
|
||||
const QMatrix4x4 *m_matrix;
|
||||
const QSGClipNode *m_clip_list;
|
||||
qreal m_opacity;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif
|
|
@ -8,6 +8,7 @@ HEADERS += \
|
|||
$$PWD/coreapi/qsgnode.h \
|
||||
$$PWD/coreapi/qsgnodeupdater_p.h \
|
||||
$$PWD/coreapi/qsgrenderer_p.h \
|
||||
$$PWD/coreapi/qsgrendernode_p.h \
|
||||
$$PWD/coreapi/qsggeometry_p.h
|
||||
|
||||
SOURCES += \
|
||||
|
@ -16,7 +17,8 @@ SOURCES += \
|
|||
$$PWD/coreapi/qsgmaterial.cpp \
|
||||
$$PWD/coreapi/qsgnode.cpp \
|
||||
$$PWD/coreapi/qsgnodeupdater.cpp \
|
||||
$$PWD/coreapi/qsgrenderer.cpp
|
||||
$$PWD/coreapi/qsgrenderer.cpp \
|
||||
$$PWD/coreapi/qsgrendernode.cpp
|
||||
|
||||
# Util API
|
||||
HEADERS += \
|
||||
|
|
|
@ -4,6 +4,7 @@ PUBLICTESTS += \
|
|||
examples \
|
||||
geometry \
|
||||
nodes \
|
||||
rendernode \
|
||||
qdeclarativepixmapcache
|
||||
|
||||
# This test requires the qtconcurrent module
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import QtQuick 2.0
|
||||
import Test 1.0
|
||||
|
||||
Rectangle {
|
||||
width: 200
|
||||
height: 200
|
||||
color: "black"
|
||||
Rectangle {
|
||||
width: 200
|
||||
height: 100
|
||||
anchors.centerIn: parent
|
||||
clip: true
|
||||
color: "white"
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 100
|
||||
anchors.centerIn: parent
|
||||
rotation: 45
|
||||
color: "blue"
|
||||
clip: true
|
||||
MessUpItem {
|
||||
anchors.fill: parent
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -50
|
||||
color: "red"
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import QtQuick 2.0
|
||||
import Test 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
width: 200
|
||||
height: 200
|
||||
color: "black"
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 100
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
color: "red"
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 100
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
color: "red"
|
||||
}
|
||||
|
||||
ClearItem {
|
||||
width: 100
|
||||
height: 100
|
||||
anchors.centerIn: parent
|
||||
color: "white"
|
||||
clip: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 100
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
color: "blue"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 100
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
color: "blue"
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
CONFIG += testcase
|
||||
TARGET = tst_rendernode
|
||||
SOURCES += tst_rendernode.cpp
|
||||
|
||||
macx:CONFIG -= app_bundle
|
||||
|
||||
testDataFiles.files = data
|
||||
testDataFiles.path = .
|
||||
DEPLOYMENT += testDataFiles
|
||||
|
||||
include(../../shared/util.pri)
|
||||
|
||||
CONFIG += parallel_test
|
||||
QT += core-private gui-private v8-private declarative-private quick-private testlib
|
||||
|
||||
OTHER_FILES += \
|
||||
data/RenderOrder.qml \
|
||||
data/MessUpState.qml \
|
|
@ -0,0 +1,242 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qtest.h>
|
||||
|
||||
#include <QtQuick/qquickitem.h>
|
||||
#include <QtQuick/qquickview.h>
|
||||
#include <QtGui/qopenglcontext.h>
|
||||
#include <private/qsgrendernode_p.h>
|
||||
|
||||
#include "../../shared/util.h"
|
||||
|
||||
class tst_rendernode: public QDeclarativeDataTest
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
tst_rendernode();
|
||||
|
||||
QImage runTest(const QString &url)
|
||||
{
|
||||
QQuickView view;
|
||||
view.setSource(QUrl(url));
|
||||
|
||||
view.show();
|
||||
QTest::qWaitForWindowShown(&view);
|
||||
|
||||
return view.grabFrameBuffer();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void renderOrder();
|
||||
void messUpState();
|
||||
};
|
||||
|
||||
class ClearNode : public QSGRenderNode
|
||||
{
|
||||
public:
|
||||
virtual StateFlags changedStates()
|
||||
{
|
||||
return ColorState;
|
||||
}
|
||||
|
||||
virtual void render(const RenderState &)
|
||||
{
|
||||
// If clip has been set, scissoring will make sure the right area is cleared.
|
||||
glClearColor(color.redF(), color.greenF(), color.blueF(), 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
QColor color;
|
||||
};
|
||||
|
||||
class ClearItem : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
||||
public:
|
||||
ClearItem() : m_color(Qt::black)
|
||||
{
|
||||
setFlag(ItemHasContents, true);
|
||||
}
|
||||
|
||||
QColor color() const { return m_color; }
|
||||
void setColor(const QColor &color)
|
||||
{
|
||||
if (color == m_color)
|
||||
return;
|
||||
m_color = color;
|
||||
emit colorChanged();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
|
||||
{
|
||||
ClearNode *node = static_cast<ClearNode *>(oldNode);
|
||||
if (!node)
|
||||
node = new ClearNode;
|
||||
node->color = m_color;
|
||||
return node;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void colorChanged();
|
||||
|
||||
private:
|
||||
QColor m_color;
|
||||
};
|
||||
|
||||
class MessUpNode : public QSGRenderNode
|
||||
{
|
||||
public:
|
||||
virtual StateFlags changedStates()
|
||||
{
|
||||
return StateFlags(DepthState) | StencilState | ScissorState | ColorState | BlendState
|
||||
| CullState | ViewportState;
|
||||
}
|
||||
|
||||
virtual void render(const RenderState &)
|
||||
{
|
||||
// Don't draw anything, just mess up the state
|
||||
glViewport(10, 10, 10, 10);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDepthMask(true);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_EQUAL);
|
||||
#if defined(QT_OPENGL_ES)
|
||||
glClearDepthf(1);
|
||||
#else
|
||||
glClearDepth(1);
|
||||
#endif
|
||||
glClearStencil(42);
|
||||
glClearColor(1.0f, 0.5f, 1.0f, 0.0f);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(190, 190, 10, 10);
|
||||
glStencilFunc(GL_EQUAL, 28, 0xff);
|
||||
glBlendFunc(GL_ZERO, GL_ZERO);
|
||||
GLint frontFace;
|
||||
glGetIntegerv(GL_FRONT_FACE, &frontFace);
|
||||
glFrontFace(frontFace == GL_CW ? GL_CCW : GL_CW);
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
};
|
||||
|
||||
class MessUpItem : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MessUpItem()
|
||||
{
|
||||
setFlag(ItemHasContents, true);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
|
||||
{
|
||||
MessUpNode *node = static_cast<MessUpNode *>(oldNode);
|
||||
if (!node)
|
||||
node = new MessUpNode;
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
tst_rendernode::tst_rendernode()
|
||||
{
|
||||
qmlRegisterType<ClearItem>("Test", 1, 0, "ClearItem");
|
||||
qmlRegisterType<MessUpItem>("Test", 1, 0, "MessUpItem");
|
||||
}
|
||||
|
||||
static void fuzzyCompareColor(QRgb x, QRgb y)
|
||||
{
|
||||
QVERIFY(qAbs(qRed(x) - qRed(y)) < 4);
|
||||
QVERIFY(qAbs(qGreen(x) - qGreen(y)) < 4);
|
||||
QVERIFY(qAbs(qBlue(x) - qBlue(y)) < 4);
|
||||
}
|
||||
|
||||
void tst_rendernode::renderOrder()
|
||||
{
|
||||
QImage fb = runTest(testFile("RenderOrder.qml"));
|
||||
int x1 = fb.width() / 8;
|
||||
int x2 = fb.width() * 3 / 8;
|
||||
int x3 = fb.width() * 5 / 8;
|
||||
int x4 = fb.width() * 7 / 8;
|
||||
int y1 = fb.height() / 8;
|
||||
int y2 = fb.height() * 3 / 8;
|
||||
int y3 = fb.height() * 5 / 8;
|
||||
int y4 = fb.height() * 7 / 8;
|
||||
|
||||
fuzzyCompareColor(fb.pixel(x1, y1), qRgb(0x7f, 0x00, 0x00));
|
||||
QCOMPARE(fb.pixel(x2, y2), qRgb(0xff, 0xff, 0xff));
|
||||
QCOMPARE(fb.pixel(x3, y2), qRgb(0x00, 0x00, 0xff));
|
||||
QCOMPARE(fb.pixel(x4, y1), qRgb(0x00, 0x00, 0xff));
|
||||
QCOMPARE(fb.pixel(x1, y4), qRgb(0xff, 0x00, 0x00));
|
||||
QCOMPARE(fb.pixel(x2, y3), qRgb(0xff, 0xff, 0xff));
|
||||
fuzzyCompareColor(fb.pixel(x3, y3), qRgb(0x7f, 0x7f, 0xff));
|
||||
fuzzyCompareColor(fb.pixel(x4, y4), qRgb(0x00, 0x00, 0x7f));
|
||||
}
|
||||
|
||||
void tst_rendernode::messUpState()
|
||||
{
|
||||
QImage fb = runTest(testFile("MessUpState.qml"));
|
||||
int x1 = 0;
|
||||
int x2 = fb.width() / 2;
|
||||
int x3 = fb.width() - 1;
|
||||
int y1 = 0;
|
||||
int y2 = fb.height() * 3 / 16;
|
||||
int y3 = fb.height() / 2;
|
||||
int y4 = fb.height() * 13 / 16;
|
||||
int y5 = fb.height() - 1;
|
||||
|
||||
QCOMPARE(fb.pixel(x1, y3), qRgb(0xff, 0xff, 0xff));
|
||||
QCOMPARE(fb.pixel(x3, y3), qRgb(0xff, 0xff, 0xff));
|
||||
|
||||
QCOMPARE(fb.pixel(x2, y1), qRgb(0x00, 0x00, 0x00));
|
||||
QCOMPARE(fb.pixel(x2, y2), qRgb(0x00, 0x00, 0x00));
|
||||
fuzzyCompareColor(fb.pixel(x2, y3), qRgb(0x7f, 0x00, 0x7f));
|
||||
QCOMPARE(fb.pixel(x2, y4), qRgb(0x00, 0x00, 0x00));
|
||||
QCOMPARE(fb.pixel(x2, y5), qRgb(0x00, 0x00, 0x00));
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_rendernode)
|
||||
|
||||
#include "tst_rendernode.moc"
|
Loading…
Reference in New Issue