diff options
author | Simon Hausmann <[email protected]> | 2014-01-17 07:11:26 +0100 |
---|---|---|
committer | Simon Hausmann <[email protected]> | 2014-01-17 07:12:23 +0100 |
commit | f596553e03c3969f6e7cb5344c05591da6e70dfb (patch) | |
tree | 8aef667749e1adc015cbc9c5ec5b5af9c6d4c15c /src | |
parent | e32845b137834ef46d68345a0029d4af7c1d85bb (diff) | |
parent | 7030adff1869e850a7b983e88d7a773d5d594886 (diff) |
Merge remote-tracking branch 'origin/stable' into dev
Conflicts:
.qmake.conf
src/imports/dialogs/DefaultFileDialog.qml
src/imports/widgets/qquickqfiledialog.cpp
Change-Id: I00de6dd05cb773f01254061d585a82c90b229acd
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/accessible/quick/qaccessiblequickitem.cpp | 5 | ||||
-rw-r--r-- | src/qml/compiler/qv4regalloc.cpp | 162 | ||||
-rw-r--r-- | src/qml/compiler/qv4ssa.cpp | 148 | ||||
-rw-r--r-- | src/qml/compiler/qv4ssa_p.h | 6 | ||||
-rw-r--r-- | src/qml/debugger/qqmlprofilerservice.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4context.cpp | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4context_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 33 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functionobject.cpp | 3 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4mm.cpp | 15 | ||||
-rw-r--r-- | src/qml/qml/qqmlapplicationengine.cpp | 8 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 4 | ||||
-rw-r--r-- | src/qmltest/quicktest.cpp | 3 | ||||
-rw-r--r-- | src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc | 15 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 6 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 85 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h | 8 | ||||
-rw-r--r-- | src/quick/scenegraph/scenegraph.pri | 2 |
18 files changed, 327 insertions, 185 deletions
diff --git a/src/plugins/accessible/quick/qaccessiblequickitem.cpp b/src/plugins/accessible/quick/qaccessiblequickitem.cpp index 9e8c2a6020..3521d4f98e 100644 --- a/src/plugins/accessible/quick/qaccessiblequickitem.cpp +++ b/src/plugins/accessible/quick/qaccessiblequickitem.cpp @@ -185,11 +185,14 @@ QAccessible::State QAccessibleQuickItem::state() const case QAccessible::PageTab: case QAccessible::EditableText: case QAccessible::SpinBox: - case QAccessible::ComboBox: case QAccessible::Terminal: case QAccessible::ScrollBar: state.focusable = true; break; + case QAccessible::ComboBox: + state.focusable = true; + state.editable = item()->property("editable").toBool(); + break; default: break; } diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp index 65685f1148..7996b134a1 100644 --- a/src/qml/compiler/qv4regalloc.cpp +++ b/src/qml/compiler/qv4regalloc.cpp @@ -127,7 +127,7 @@ public: return _defs[t].isPhiTarget; } - QList<int> calls() const { return _calls; } + const QList<int> &calls() const { return _calls; } QList<Temp> hints(const Temp &t) const { return _hints[t]; } void addHint(const Temp &t, int physicalRegister) { @@ -661,13 +661,14 @@ using namespace QT_PREPEND_NAMESPACE(QQmlJS); namespace { class ResolutionPhase: protected StmtVisitor, protected ExprVisitor { - QVector<LifeTimeInterval> _intervals; + const QVector<LifeTimeInterval> &_intervals; + QVector<const LifeTimeInterval *> _unprocessed; Function *_function; #if !defined(QT_NO_DEBUG) RegAllocInfo *_info; #endif const QHash<V4IR::Temp, int> &_assignedSpillSlots; - QHash<V4IR::Temp, LifeTimeInterval> _intervalForTemp; + QHash<V4IR::Temp, const LifeTimeInterval *> _intervalForTemp; const QVector<int> &_intRegs; const QVector<int> &_fpRegs; @@ -675,8 +676,8 @@ class ResolutionPhase: protected StmtVisitor, protected ExprVisitor { QVector<Move *> _loads; QVector<Move *> _stores; - QHash<BasicBlock *, QList<LifeTimeInterval> > _liveAtStart; - QHash<BasicBlock *, QList<LifeTimeInterval> > _liveAtEnd; + QHash<BasicBlock *, QList<const LifeTimeInterval *> > _liveAtStart; + QHash<BasicBlock *, QList<const LifeTimeInterval *> > _liveAtEnd; public: ResolutionPhase(const QVector<LifeTimeInterval> &intervals, Function *function, RegAllocInfo *info, @@ -694,6 +695,13 @@ public: #if defined(QT_NO_DEBUG) Q_UNUSED(info) #endif + + _unprocessed.resize(_intervals.size()); + for (int i = 0, ei = _intervals.size(); i != ei; ++i) + _unprocessed[i] = &_intervals[i]; + + _liveAtStart.reserve(function->basicBlocks.size()); + _liveAtEnd.reserve(function->basicBlocks.size()); } void run() { @@ -707,6 +715,7 @@ private: { foreach (BasicBlock *bb, _function->basicBlocks) { QVector<Stmt *> newStatements; + newStatements.reserve(bb->statements.size() + 7); bool seenFirstNonPhiStmt = false; for (int i = 0, ei = bb->statements.size(); i != ei; ++i) { @@ -757,22 +766,22 @@ private: } - void activate(const LifeTimeInterval &i) + void activate(const LifeTimeInterval *i) { - Q_ASSERT(!i.isFixedInterval()); - _intervalForTemp[i.temp()] = i; + Q_ASSERT(!i->isFixedInterval()); + _intervalForTemp[i->temp()] = i; - if (i.reg() != LifeTimeInterval::Invalid) { + if (i->reg() != LifeTimeInterval::Invalid) { // check if we need to generate spill/unspill instructions - if (i.start() == _currentStmt->id) { - if (i.isSplitFromInterval()) { - int pReg = platformRegister(i); - _loads.append(generateUnspill(i.temp(), pReg)); + if (i->start() == _currentStmt->id) { + if (i->isSplitFromInterval()) { + int pReg = platformRegister(*i); + _loads.append(generateUnspill(i->temp(), pReg)); } else { - int pReg = platformRegister(i); - int spillSlot = _assignedSpillSlots.value(i.temp(), -1); + int pReg = platformRegister(*i); + int spillSlot = _assignedSpillSlots.value(i->temp(), -1); if (spillSlot != -1) - _stores.append(generateSpill(spillSlot, i.temp().type, pReg)); + _stores.append(generateSpill(spillSlot, i->temp().type, pReg)); } } } @@ -782,37 +791,37 @@ private: { if (Phi *phi = _currentStmt->asPhi()) { // for phi nodes, only activate the range belonging to that node - for (int it = 0, eit = _intervals.size(); it != eit; ++it) { - const LifeTimeInterval &i = _intervals.at(it); - if (i.start() > _currentStmt->id) + for (int it = 0, eit = _unprocessed.size(); it != eit; ++it) { + const LifeTimeInterval *i = _unprocessed.at(it); + if (i->start() > _currentStmt->id) break; - if (i.temp() == *phi->targetTemp) { + if (i->temp() == *phi->targetTemp) { activate(i); - _intervals.remove(it); + _unprocessed.remove(it); break; } } return; } - while (!_intervals.isEmpty()) { - const LifeTimeInterval &i = _intervals.first(); - if (i.start() > _currentStmt->id) + while (!_unprocessed.isEmpty()) { + const LifeTimeInterval *i = _unprocessed.first(); + if (i->start() > _currentStmt->id) break; activate(i); - _intervals.removeFirst(); + _unprocessed.removeFirst(); } } void cleanOldIntervals() { const int id = _currentStmt->id; - QMutableHashIterator<Temp, LifeTimeInterval> it(_intervalForTemp); + QMutableHashIterator<Temp, const LifeTimeInterval *> it(_intervalForTemp); while (it.hasNext()) { - const LifeTimeInterval &i = it.next().value(); - if (i.end() < id || i.isFixedInterval()) + const LifeTimeInterval *i = it.next().value(); + if (i->end() < id || i->isFixedInterval()) it.remove(); } } @@ -820,19 +829,15 @@ private: void resolve() { foreach (BasicBlock *bb, _function->basicBlocks) { - foreach (BasicBlock *bbOut, bb->out) { -#ifdef DEBUG_REGALLOC - Optimizer::showMeTheCode(_function); -#endif // DEBUG_REGALLOC - + foreach (BasicBlock *bbOut, bb->out) resolveEdge(bb, bbOut); - } } } void resolveEdge(BasicBlock *predecessor, BasicBlock *successor) { #ifdef DEBUG_REGALLOC + Optimizer::showMeTheCode(_function); qDebug() << "Resolving edge" << predecessor->index << "->" << successor->index; #endif // DEBUG_REGALLOC @@ -851,17 +856,21 @@ private: Q_ASSERT(successorStart > 0); - foreach (const LifeTimeInterval &it, _liveAtStart[successor]) { - bool lifeTimeHole = false; - if (it.end() < successorStart) + foreach (const LifeTimeInterval *it, _liveAtStart[successor]) { + if (it->end() < successorStart) continue; + + bool lifeTimeHole = false; + bool isPhiTarget = false; Expr *moveFrom = 0; - if (it.start() == successorStart) { + + if (it->start() == successorStart) { foreach (Stmt *s, successor->statements) { if (!s || s->id < 1) continue; if (Phi *phi = s->asPhi()) { - if (*phi->targetTemp == it.temp()) { + if (*phi->targetTemp == it->temp()) { + isPhiTarget = true; Expr *opd = phi->d->incoming[successor->in.indexOf(predecessor)]; if (opd->asConst()) { moveFrom = opd; @@ -869,12 +878,12 @@ private: Temp *t = opd->asTemp(); Q_ASSERT(t); - foreach (const LifeTimeInterval &it2, _liveAtEnd[predecessor]) { - if (it2.temp() == *t - && it2.reg() != LifeTimeInterval::Invalid - && it2.covers(predecessorEnd)) { + foreach (const LifeTimeInterval *it2, _liveAtEnd[predecessor]) { + if (it2->temp() == *t + && it2->reg() != LifeTimeInterval::Invalid + && it2->covers(predecessorEnd)) { moveFrom = createTemp(Temp::PhysicalRegister, - platformRegister(it2), t->type); + platformRegister(*it2), t->type); break; } } @@ -889,18 +898,18 @@ private: } } } else { - foreach (const LifeTimeInterval &predIt, _liveAtEnd[predecessor]) { - if (predIt.temp() == it.temp()) { - if (predIt.reg() != LifeTimeInterval::Invalid - && predIt.covers(predecessorEnd)) { - moveFrom = createTemp(Temp::PhysicalRegister, platformRegister(predIt), - predIt.temp().type); + foreach (const LifeTimeInterval *predIt, _liveAtEnd[predecessor]) { + if (predIt->temp() == it->temp()) { + if (predIt->reg() != LifeTimeInterval::Invalid + && predIt->covers(predecessorEnd)) { + moveFrom = createTemp(Temp::PhysicalRegister, platformRegister(*predIt), + predIt->temp().type); } else { - int spillSlot = _assignedSpillSlots.value(predIt.temp(), -1); + int spillSlot = _assignedSpillSlots.value(predIt->temp(), -1); if (spillSlot == -1) lifeTimeHole = true; else - moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt.temp().type); + moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt->temp().type); } break; } @@ -909,20 +918,20 @@ private: if (!moveFrom) { Q_UNUSED(lifeTimeHole); #if !defined(QT_NO_DEBUG) - Q_ASSERT(!_info->isPhiTarget(it.temp()) || it.isSplitFromInterval() || lifeTimeHole); - if (_info->def(it.temp()) != successorStart && !it.isSplitFromInterval()) { + Q_ASSERT(!_info->isPhiTarget(it->temp()) || it->isSplitFromInterval() || lifeTimeHole); + if (_info->def(it->temp()) != successorStart && !it->isSplitFromInterval()) { const int successorEnd = successor->statements.last()->id; const int idx = successor->in.indexOf(predecessor); - foreach (const Use &use, _info->uses(it.temp())) { + foreach (const Use &use, _info->uses(it->temp())) { if (use.pos == static_cast<unsigned>(successorStart)) { // only check the current edge, not all other possible ones. This is // important for phi nodes: they have uses that are only valid when // coming in over a specific edge. foreach (Stmt *s, successor->statements) { if (Phi *phi = s->asPhi()) { - Q_ASSERT(it.temp().index != phi->targetTemp->index); + Q_ASSERT(it->temp().index != phi->targetTemp->index); Q_ASSERT(phi->d->incoming[idx]->asTemp() == 0 - || it.temp().index != phi->d->incoming[idx]->asTemp()->index); + || it->temp().index != phi->d->incoming[idx]->asTemp()->index); } else { // TODO: check that the first non-phi statement does not use // the temp. @@ -941,13 +950,18 @@ private: } Temp *moveTo; - if (it.reg() == LifeTimeInterval::Invalid || !it.covers(successorStart)) { - int spillSlot = _assignedSpillSlots.value(it.temp(), -1); + if (it->reg() == LifeTimeInterval::Invalid || !it->covers(successorStart)) { + if (!isPhiTarget) // if it->temp() is a phi target, skip it. + continue; + const int spillSlot = _assignedSpillSlots.value(it->temp(), -1); if (spillSlot == -1) continue; // it has a life-time hole here. - moveTo = createTemp(Temp::StackSlot, spillSlot, it.temp().type); + moveTo = createTemp(Temp::StackSlot, spillSlot, it->temp().type); } else { - moveTo = createTemp(Temp::PhysicalRegister, platformRegister(it), it.temp().type); + moveTo = createTemp(Temp::PhysicalRegister, platformRegister(*it), it->temp().type); + const int spillSlot = _assignedSpillSlots.value(it->temp(), -1); + if (isPhiTarget && spillSlot != -1) + mapping.add(moveFrom, createTemp(Temp::StackSlot, spillSlot, it->temp().type)); } // add move to mapping @@ -1008,10 +1022,10 @@ protected: if (t->kind != Temp::VirtualRegister) return; - const LifeTimeInterval &i = _intervalForTemp[*t]; - Q_ASSERT(i.isValid()); - if (i.reg() != LifeTimeInterval::Invalid && i.covers(_currentStmt->id)) { - int pReg = platformRegister(i); + const LifeTimeInterval *i = _intervalForTemp[*t]; + Q_ASSERT(i->isValid()); + if (i->reg() != LifeTimeInterval::Invalid && i->covers(_currentStmt->id)) { + int pReg = platformRegister(*i); t->kind = Temp::PhysicalRegister; t->index = pReg; } else { @@ -1081,7 +1095,7 @@ void RegisterAllocator::run(Function *function, const Optimizer &opt) { QTextStream qout(stdout, QIODevice::WriteOnly); qout << "Ranges:" << endl; - QList<LifeTimeInterval> intervals = _unhandled; + QVector<LifeTimeInterval> intervals = _unhandled; std::sort(intervals.begin(), intervals.end(), LifeTimeInterval::lessThanForTemp); foreach (const LifeTimeInterval &r, intervals) { r.dump(qout); @@ -1117,7 +1131,7 @@ void RegisterAllocator::run(Function *function, const Optimizer &opt) #endif // DEBUG_REGALLOC } -static inline LifeTimeInterval createFixedInterval(int reg, bool isFP) +static inline LifeTimeInterval createFixedInterval(int reg, bool isFP, int rangeCount) { Temp t; t.init(Temp::PhysicalRegister, reg, 0); @@ -1126,6 +1140,7 @@ static inline LifeTimeInterval createFixedInterval(int reg, bool isFP) i.setTemp(t); i.setReg(reg); i.setFixedInterval(true); + i.reserveRanges(rangeCount); return i; } @@ -1134,12 +1149,13 @@ void RegisterAllocator::prepareRanges() const int regCount = _normalRegisters.size(); _fixedRegisterRanges.resize(regCount); for (int reg = 0; reg < regCount; ++reg) - _fixedRegisterRanges[reg] = createFixedInterval(reg, false); + _fixedRegisterRanges[reg] = createFixedInterval(reg, false, _info->calls().size()); const int fpRegCount = _fpRegisters.size(); _fixedFPRegisterRanges.resize(fpRegCount); - for (int fpReg = 0; fpReg < fpRegCount; ++fpReg) - _fixedFPRegisterRanges[fpReg] = createFixedInterval(fpReg, true); + for (int fpReg = 0; fpReg < fpRegCount; ++fpReg) { + _fixedFPRegisterRanges[fpReg] = createFixedInterval(fpReg, true, _info->calls().size()); + } foreach (int callPosition, _info->calls()) { for (int reg = 0; reg < regCount; ++reg) @@ -1451,9 +1467,9 @@ int RegisterAllocator::nextIntersection(const LifeTimeInterval ¤t, return -1; for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) { - const LifeTimeInterval::Range ¤tRange = currentRanges[currentIt]; + const LifeTimeInterval::Range currentRange = currentRanges.at(currentIt); for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) { - const LifeTimeInterval::Range &anotherRange = anotherRanges[anotherIt]; + const LifeTimeInterval::Range anotherRange = anotherRanges.at(anotherIt); if (anotherRange.start > currentRange.end) break; int intersectPos = intersectionPosition(currentRange, anotherRange); @@ -1588,7 +1604,7 @@ void RegisterAllocator::dump() const { qout << "Ranges:" << endl; - QList<LifeTimeInterval> handled = _handled; + QVector<LifeTimeInterval> handled = _handled; std::sort(handled.begin(), handled.end(), LifeTimeInterval::lessThanForTemp); foreach (const LifeTimeInterval &r, handled) { r.dump(qout); diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 31a1e4cdc4..7113dc7c26 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -308,9 +308,9 @@ public: BasicBlock *operator*() const { if (set.blockNumbers) - return set.allBlocks[*numberIt]; + return set.allBlocks.at(*numberIt); else - return set.allBlocks[flagIt]; + return set.allBlocks.at(flagIt); } bool operator==(const const_iterator &other) const @@ -409,7 +409,7 @@ class DominatorTree { typedef int BasicBlockIndex; enum { InvalidBasicBlockIndex = -1 }; - const QVector<BasicBlock *> nodes; + QVector<BasicBlock *> nodes; int N; std::vector<int> dfnum; // BasicBlock index -> dfnum std::vector<int> vertex; @@ -472,9 +472,8 @@ class DominatorTree { #endif // SHOW_SSA } - BasicBlockIndex ancestorWithLowestSemi(BasicBlockIndex v) { - std::vector<BasicBlockIndex> worklist; - worklist.reserve(vertex.capacity() / 2); + BasicBlockIndex ancestorWithLowestSemi(BasicBlockIndex v, std::vector<BasicBlockIndex> &worklist) { + worklist.clear(); for (BasicBlockIndex it = v; it != InvalidBasicBlockIndex; it = ancestor[it]) worklist.push_back(it); @@ -517,6 +516,9 @@ class DominatorTree { DFS(nodes.first()->index); Q_ASSERT(N == nodes.size()); // fails with unreachable nodes, but those should have been removed before. + std::vector<BasicBlockIndex> worklist; + worklist.reserve(vertex.capacity() / 2); + for (int i = N - 1; i > 0; --i) { BasicBlockIndex n = vertex[i]; BasicBlockIndex p = parent[n]; @@ -527,7 +529,7 @@ class DominatorTree { if (dfnum[v->index] <= dfnum[n]) ss = v->index; else - ss = semi[ancestorWithLowestSemi(v->index)]; + ss = semi[ancestorWithLowestSemi(v->index, worklist)]; if (dfnum[ss] < dfnum[s]) s = ss; } @@ -536,7 +538,7 @@ class DominatorTree { link(p, n); if (bucket.contains(p)) { foreach (BasicBlockIndex v, bucket[p]) { - BasicBlockIndex y = ancestorWithLowestSemi(v); + BasicBlockIndex y = ancestorWithLowestSemi(v, worklist); BasicBlockIndex semi_v = semi[v]; if (semi[y] == semi_v) idom[v] = semi_v; @@ -602,7 +604,7 @@ class DominatorTree { std::vector<BasicBlockIndex> worklist; worklist.reserve(nodes.size() * 2); for (int i = 0, ei = nodes.size(); i != ei; ++i) { - BasicBlockIndex nodeIndex = nodes[i]->index; + BasicBlockIndex nodeIndex = nodes.at(i)->index; worklist.push_back(nodeIndex); NodeProgress &np = nodeStatus[nodeIndex]; np.children = children[nodeIndex]; @@ -633,7 +635,7 @@ class DominatorTree { if (np.todo.empty()) { BasicBlockSet &S = DF[node]; S.init(nodes); - foreach (BasicBlock *y, nodes[node]->out) + foreach (BasicBlock *y, nodes.at(node)->out) if (idom[y->index] != node) S.insert(y); foreach (BasicBlockIndex child, np.children) { @@ -695,10 +697,6 @@ public: computeDF(); } -// QSet<BasicBlock *> operator[](BasicBlock *n) const { -// return DF[n->index]; -// } - const BasicBlockSet &dominatorFrontier(BasicBlock *n) const { return DF[n->index]; } @@ -707,14 +705,57 @@ public: return nodes[idom[bb->index]]; } + void dumpImmediateDominators() const + { + qDebug() << "Immediate dominators for" << idom.size() << "nodes:"; + for (size_t i = 0, ei = idom.size(); i != ei; ++i) + if (idom[i] == InvalidBasicBlockIndex) + qDebug("\tnone -> L%d", int(i)); + else + qDebug("\tL%d -> L%d", idom[i], int(i)); + } + + void updateImmediateDominator(BasicBlock *bb, BasicBlock *newDominator) + { + Q_ASSERT(bb->index >= 0); + + int blockIndex; + if (static_cast<std::vector<BasicBlockIndex>::size_type>(bb->index) >= idom.size()) { + // This is a new block, probably introduced by edge splitting. So, we'll have to grow + // the array before inserting the immediate dominator. + nodes.append(bb); + idom.resize(nodes.size(), InvalidBasicBlockIndex); + blockIndex = nodes.size() - 1; + } else { + blockIndex = getBlockIndex(bb); + } + + idom[blockIndex] = getBlockIndex(newDominator); + } + bool dominates(BasicBlock *dominator, BasicBlock *dominated) const { // The index of the basic blocks might have changed, or the nodes array might have changed, - // or the block got deleted, so get the index from our copy of the array. - return dominates(nodes.indexOf(dominator), nodes.indexOf(dominated)); + // so get the index from our copy of the array. + return dominates(getBlockIndex(dominator), getBlockIndex(dominated)); } private: + int getBlockIndex(BasicBlock *bb) const { + if (!bb) + return InvalidBasicBlockIndex; + + if (bb->index >= 0 && bb->index < nodes.size()) { + if (nodes.at(bb->index) == bb) + return bb->index; + } + + return nodes.indexOf(bb); + } + bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const { + // dominator can be Invalid when the dominated block has no dominator (i.e. the start node) + Q_ASSERT(dominated != InvalidBasicBlockIndex); + for (BasicBlockIndex it = dominated; it != InvalidBasicBlockIndex; it = idom[it]) { if (it == dominator) return true; @@ -754,6 +795,8 @@ public: VariableCollector(Function *function) : variablesCanEscape(function->variablesCanEscape()) { + _defsites.reserve(function->tempCount); + #if defined(SHOW_SSA) qout << "Variables collected:" << endl; #endif // SHOW_SSA @@ -1018,6 +1061,9 @@ public: , tempCount(0) , processed(f->basicBlocks) { + localMapping.reserve(f->tempCount); + vregMapping.reserve(f->tempCount); + todo.reserve(f->basicBlocks.size()); } void run() { @@ -2463,11 +2509,10 @@ protected: } }; -void splitCriticalEdges(Function *f) +void splitCriticalEdges(Function *f, DominatorTree &df) { - const QVector<BasicBlock *> oldBBs = f->basicBlocks; - - foreach (BasicBlock *bb, oldBBs) { + for (int i = 0, ei = f->basicBlocks.size(); i != ei; ++i) { + BasicBlock *bb = f->basicBlocks[i]; if (bb->in.size() > 1) { for (int inIdx = 0, eInIdx = bb->in.size(); inIdx != eInIdx; ++inIdx) { BasicBlock *inBB = bb->in[inIdx]; @@ -2511,6 +2556,9 @@ void splitCriticalEdges(Function *f) } else { Q_ASSERT(!"Unknown terminator!"); } + + // Set the immediate dominator of the new block to inBB + df.updateImmediateDominator(newBB, inBB); } } } @@ -2660,6 +2708,12 @@ public: showMeTheCode(function); schedule(function->basicBlocks.first()); +#if defined(SHOW_SSA) + qDebug() << "Block sequence:"; + foreach (BasicBlock *bb, sequence) + qDebug("\tL%d", bb->index); +#endif // SHOW_SSA + Q_ASSERT(function->basicBlocks.size() == sequence.size()); function->basicBlocks = sequence; return loopsStartEnd; @@ -2895,7 +2949,8 @@ namespace { /// and removes unreachable staements from the worklist, so that optimiseSSA won't consider them /// anymore. /// Important: this assumes that there are no critical edges in the control-flow graph! -void purgeBB(BasicBlock *bb, Function *func, DefUsesCalculator &defUses, QVector<Stmt *> &W) +void purgeBB(BasicBlock *bb, Function *func, DefUsesCalculator &defUses, QVector<Stmt *> &W, + DominatorTree &df) { // TODO: change this to mark the block as deleted, but leave it alone so that other references // won't be dangling pointers. @@ -2945,11 +3000,15 @@ void purgeBB(BasicBlock *bb, Function *func, DefUsesCalculator &defUses, QVector } } - // if a successor has no incoming edges after unlinking the current basic block, then - // it is unreachable, and can be purged too - if (out->in.isEmpty()) + if (out->in.isEmpty()) { + // if a successor has no incoming edges after unlinking the current basic block, then + // it is unreachable, and can be purged too toPurge.append(out); - + } else if (out->in.size() == 1) { + // if the successor now has only one incoming edge, we that edge is the new + // immediate dominator + df.updateImmediateDominator(out, out->in.first()); + } } // unlink all defs/uses from the statements in the basic block @@ -3032,7 +3091,7 @@ bool tryOptimizingComparison(Expr *&expr) } } // anonymous namespace -void optimizeSSA(Function *function, DefUsesCalculator &defUses) +void optimizeSSA(Function *function, DefUsesCalculator &defUses, DominatorTree &df) { const bool variablesCanEscape = function->variablesCanEscape(); @@ -3298,10 +3357,10 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses) Jump *jump = function->New<Jump>(); if (convertToValue(constantCondition).toBoolean()) { jump->target = cjump->iftrue; - purgeBB(cjump->iffalse, function, defUses, W); + purgeBB(cjump->iffalse, function, defUses, W, df); } else { jump->target = cjump->iffalse; - purgeBB(cjump->iftrue, function, defUses, W); + purgeBB(cjump->iftrue, function, defUses, W, df); } *ref[s] = jump; @@ -3648,9 +3707,6 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine) if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA) { // qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl; -// qout << "Starting edge splitting..." << endl; - splitCriticalEdges(function); -// showMeTheCode(function); // Calculate the dominator tree: DominatorTree df(function->basicBlocks); @@ -3678,10 +3734,15 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine) TypePropagation(defUses).run(function); // showMeTheCode(function); + // Transform the CFG into edge-split SSA. +// qout << "Starting edge splitting..." << endl; + splitCriticalEdges(function, df); +// showMeTheCode(function); + static bool doOpt = qgetenv("QV4_NO_OPT").isEmpty(); if (doOpt) { // qout << "Running SSA optimization..." << endl; - optimizeSSA(function, defUses); + optimizeSSA(function, defUses, df); // showMeTheCode(function); } @@ -3697,6 +3758,7 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine) // showMeTheCode(function); // qout << "Doing block scheduling..." << endl; +// df.dumpImmediateDominators(); startEndLoops = BlockScheduler(function, df).go(); // showMeTheCode(function); @@ -3837,15 +3899,20 @@ static inline bool overlappingStorage(const Temp &t1, const Temp &t2) || (t1.type != DoubleType && t2.type != DoubleType); } -int MoveMapping::isUsedAsSource(Expr *e) const +MoveMapping::Moves MoveMapping::sourceUsages(Expr *e, const Moves &moves) { - if (Temp *t = e->asTemp()) - for (int i = 0, ei = _moves.size(); i != ei; ++i) - if (Temp *from = _moves[i].from->asTemp()) + Moves usages; + + if (Temp *t = e->asTemp()) { + for (int i = 0, ei = moves.size(); i != ei; ++i) { + const Move &move = moves[i]; + if (Temp *from = move.from->asTemp()) if (overlappingStorage(*from, *t)) - return i; + usages.append(move); + } + } - return -1; + return usages; } void MoveMapping::add(Expr *from, Temp *to, int id) { @@ -3924,9 +3991,8 @@ void MoveMapping::dump() const MoveMapping::Action MoveMapping::schedule(const Move &m, QList<Move> &todo, QList<Move> &delayed, QList<Move> &output, QList<Move> &swaps) const { - int useIdx = isUsedAsSource(m.to); - if (useIdx != -1) { - const Move &dependency = _moves[useIdx]; + Moves usages = sourceUsages(m.to, todo) + sourceUsages(m.to, delayed); + foreach (const Move &dependency, usages) { if (!output.contains(dependency)) { if (delayed.contains(dependency)) { // We have a cycle! Break it by swapping instead of assigning. diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index dcbc83ae65..f90fc5b05b 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -93,6 +93,7 @@ public: void setFrom(Stmt *from); void addRange(int from, int to); Ranges ranges() const { return _ranges; } + void reserveRanges(int capacity) { _ranges.reserve(capacity); } int start() const { return _ranges.first().start; } int end() const { return _end; } @@ -165,10 +166,11 @@ class MoveMapping bool operator==(const Move &other) const { return from == other.from && to == other.to; } }; + typedef QList<Move> Moves; - QList<Move> _moves; + Moves _moves; - int isUsedAsSource(Expr *e) const; + static Moves sourceUsages(Expr *e, const Moves &moves); public: void add(Expr *from, Temp *to, int id = 0); diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/qml/debugger/qqmlprofilerservice.cpp index 3c066bd380..af2aea21ae 100644 --- a/src/qml/debugger/qqmlprofilerservice.cpp +++ b/src/qml/debugger/qqmlprofilerservice.cpp @@ -178,7 +178,7 @@ void QQmlProfilerService::sendProfilingData() bool QQmlProfilerService::startProfilingImpl() { bool success = false; - if (!profilingEnabled()) { + if (QQmlDebugService::isDebuggingEnabled() && !profilingEnabled()) { setProfilingEnabled(true); sendStartedProfilingMessageImpl(); success = true; diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 27c3a31d95..36b1512b63 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -91,9 +91,6 @@ CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData c->strictMode = function->strictMode; c->outer = function->scope; -#ifndef QT_NO_DEBUG - assert(c->outer->next != (ExecutionContext *)0x1); -#endif c->activation = 0; @@ -237,9 +234,6 @@ CallContext::CallContext(ExecutionEngine *engine, ObjectRef qml, FunctionObject strictMode = true; outer = function->scope; -#ifndef QT_NO_DEBUG - assert(outer->next != (ExecutionContext *)0x1); -#endif activation = qml.getPointer(); diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 851f024a60..a0930a577e 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -110,7 +110,6 @@ struct Q_QML_EXPORT ExecutionContext : public Managed ExecutionContext *outer; Lookup *lookups; CompiledData::CompilationUnit *compilationUnit; - ExecutionContext *next; // used in the GC struct EvalCode { diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 384254f896..5de8e0de44 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -109,21 +109,28 @@ quintptr getStackLimit() # else void* stackBottom = 0; pthread_attr_t attr; - pthread_getattr_np(pthread_self(), &attr); - size_t stackSize = 0; - pthread_attr_getstack(&attr, &stackBottom, &stackSize); - pthread_attr_destroy(&attr); - -# if defined(Q_OS_ANDROID) - // Bionic pretends that the main thread has a tiny stack; work around it - if (gettid() == getpid()) { - rlimit limit; - getrlimit(RLIMIT_STACK, &limit); - stackBottom = reinterpret_cast<void*>(reinterpret_cast<quintptr>(stackBottom) + stackSize - limit.rlim_cur); + if (pthread_getattr_np(pthread_self(), &attr) == 0) { + size_t stackSize = 0; + pthread_attr_getstack(&attr, &stackBottom, &stackSize); + pthread_attr_destroy(&attr); + +# if defined(Q_OS_ANDROID) + // Bionic pretends that the main thread has a tiny stack; work around it + if (gettid() == getpid()) { + rlimit limit; + getrlimit(RLIMIT_STACK, &limit); + stackBottom = reinterpret_cast<void*>(reinterpret_cast<quintptr>(stackBottom) + stackSize - limit.rlim_cur); + } +# endif + + stackLimit = reinterpret_cast<quintptr>(stackBottom); + } else { + int dummy; + // this is inexact, as part of the stack is used when being called here, + // but let's simply default to 1MB from where the stack is right now + stackLimit = reinterpret_cast<qintptr>(&dummy) - 1024*1024; } -# endif - stackLimit = reinterpret_cast<quintptr>(stackBottom); # endif // This is wrong. StackLimit is the currently committed stack size, not the real end. // only way to get that limit is apparently by using VirtualQuery (Yuck) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index a1673dfc2a..daa3d5b0de 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -131,9 +131,6 @@ void FunctionObject::init(const StringRef n, bool createProto) needsActivation = true; strictMode = false; -#ifndef QT_NO_DEBUG - assert(scope->next != (ExecutionContext *)0x1); -#endif if (createProto) { Scoped<Object> proto(s, scope->engine->newObject(scope->engine->protoClass)); diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp index 625e34de07..d55ddead89 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/jsruntime/qv4mm.cpp @@ -234,12 +234,17 @@ MemoryManager::MemoryManager() # else void* stackBottom = 0; pthread_attr_t attr; - pthread_getattr_np(pthread_self(), &attr); - size_t stackSize = 0; - pthread_attr_getstack(&attr, &stackBottom, &stackSize); - pthread_attr_destroy(&attr); + if (pthread_getattr_np(pthread_self(), &attr) == 0) { + size_t stackSize = 0; + pthread_attr_getstack(&attr, &stackBottom, &stackSize); + pthread_attr_destroy(&attr); - m_d->stackTop = static_cast<quintptr *>(stackBottom) + stackSize/sizeof(quintptr); + m_d->stackTop = static_cast<quintptr *>(stackBottom) + stackSize/sizeof(quintptr); + } else { + // can't scan the native stack so have to rely on exact gc + m_d->stackTop = 0; + m_d->exactGC = true; + } # endif #elif OS(WINCE) if (false && g_stackBase) { diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index b4ace17738..20894944f3 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -60,7 +60,7 @@ QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate() void QQmlApplicationEnginePrivate::cleanUp() { qDeleteAll(objects); -#ifndef QT_NO_TRANSLATIONS +#ifndef QT_NO_TRANSLATION qDeleteAll(translators); #endif } @@ -71,7 +71,7 @@ void QQmlApplicationEnginePrivate::init() q->connect(&statusMapper, SIGNAL(mapped(QObject*)), q, SLOT(_q_finishLoad(QObject*))); q->connect(q, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); -#ifndef QT_NO_TRANSLATIONS +#ifndef QT_NO_TRANSLATION QTranslator* qtTranslator = new QTranslator; if (qtTranslator->load(QLatin1String("qt_") + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) QCoreApplication::installTranslator(qtTranslator); @@ -83,7 +83,7 @@ void QQmlApplicationEnginePrivate::init() void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile) { -#ifndef QT_NO_TRANSLATIONS +#ifndef QT_NO_TRANSLATION if (rootFile.scheme() != QLatin1String("file") && rootFile.scheme() != QLatin1String("qrc")) return; @@ -96,6 +96,8 @@ void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile) } else { delete translator; } +#else + Q_UNUSED(rootFile) #endif } diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 0409b92e89..67b7e789bd 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1432,7 +1432,9 @@ QV4::ReturnedValue ConsoleObject::method_profile(CallContext *ctx) const QByteArray baSource = frame.source.toUtf8(); const QByteArray baFunction = frame.function.toUtf8(); QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData()); - if (QQmlProfilerService::startProfiling()) { + if (!QQmlDebugService::isDebuggingEnabled()) { + logger.warning("Cannot start profiling because debug service is disabled. Start with -qmljsdebugger=port:XXXXX."); + } else if (QQmlProfilerService::startProfiling()) { QV8ProfilerService::instance()->startProfiling(title); logger.debug("Profiling started."); diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index c210c21288..bb6eec3706 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -365,7 +365,8 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD view->resize(200, 200); } view->show(); - QTest::qWaitForWindowExposed(view); + view->requestActivate(); + QTest::qWaitForWindowActive(view); if (view->isExposed()) QTestRootObject::instance()->setWindowShown(true); if (!QTestRootObject::instance()->hasQuit && QTestRootObject::instance()->hasTestCase()) diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc index abef6b765b..f1e13e127c 100644 --- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc +++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc @@ -110,20 +110,7 @@ by the other approaches. A QAbstractItemModel can also automatically notify a QML view when the model data changes. The roles of a QAbstractItemModel subclass can be exposed to QML by -reimplementing QAbstractItemModel::roleNames(). The default role names -set by Qt are: - -\table -\header -\li Qt Role -\li QML Role Name -\row -\li Qt::DisplayRole -\li display -\row -\li Qt::DecorationRole -\li decoration -\endtable +reimplementing QAbstractItemModel::roleNames(). Here is an application with a QAbstractListModel subclass named \c AnimalModel, which exposes the \e type and \e sizes roles. It reimplements diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 110a8b7586..2a507a27b1 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2067,8 +2067,12 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item) if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List - || role == QAccessible::SpinBox) + || role == QAccessible::SpinBox) { result = true; + } else if (role == QAccessible::ComboBox) { + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item); + return iface->state().editable; + } } #endif diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 4e62f85d30..eff8158335 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -106,8 +106,7 @@ bool qsg_sort_batch_is_valid(Batch *a, Batch *b) { return a->first && !b->first; bool qsg_sort_batch_increasing_order(Batch *a, Batch *b) { return a->first->order < b->first->order; } bool qsg_sort_batch_decreasing_order(Batch *a, Batch *b) { return a->first->order > b->first->order; } -QSGMaterial::Flag QSGMaterial_FullMatrix = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrix & ~QSGMaterial::RequiresFullMatrixExceptTranslate); -QSGMaterial::Flag QSGMaterial_FullExceptTranslate = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrixExceptTranslate & ~QSGMaterial::RequiresDeterminant); +QSGMaterial::Flag QSGMaterial_RequiresFullMatrixBit = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrix & ~QSGMaterial::RequiresFullMatrixExceptTranslate); struct QMatrix4x4_Accessor { @@ -827,6 +826,9 @@ static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs) static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs) { qsg_wipeBuffer(&batch->vbo, funcs); +#ifdef QSG_SEPARATE_INDEX_BUFFER + qsg_wipeBuffer(&batch->ibo, funcs); +#endif delete batch; } @@ -879,12 +881,13 @@ void Renderer::map(Buffer *buffer, int byteSize) } } -void Renderer::unmap(Buffer *buffer) +void Renderer::unmap(Buffer *buffer, bool isIndexBuf) { if (buffer->id == 0) glGenBuffers(1, &buffer->id); - glBindBuffer(GL_ARRAY_BUFFER, buffer->id); - glBufferData(GL_ARRAY_BUFFER, buffer->size, buffer->data, m_bufferStrategy); + GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; + glBindBuffer(target, buffer->id); + glBufferData(target, buffer->size, buffer->data, m_bufferStrategy); } BatchRootInfo *Renderer::batchRootInfo(Node *node) @@ -1700,12 +1703,14 @@ void Renderer::uploadBatch(Batch *b) QSGGeometryNode *gn = b->first->node; QSGGeometry *g = gn->geometry(); - QSGMaterial::Flags flags = gn->activeMaterial()->flags(); + bool canMerge = (g->drawingMode() == GL_TRIANGLES || g->drawingMode() == GL_TRIANGLE_STRIP) && b->positionAttribute >= 0 && g->indexType() == GL_UNSIGNED_SHORT - && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0 - && ((flags & QSGMaterial_FullExceptTranslate) == 0 || b->isTranslateOnlyToRoot()) + && (gn->activeMaterial()->flags() & QSGMaterial::CustomCompileStep) == 0 + && (((gn->activeMaterial()->flags() & QSGMaterial::RequiresDeterminant) == 0) + || (((gn->activeMaterial()->flags() & QSGMaterial_RequiresFullMatrixBit) == 0) && b->isTranslateOnlyToRoot()) + ) && b->isSafeToBatch(); b->merged = canMerge; @@ -1753,10 +1758,19 @@ void Renderer::uploadBatch(Batch *b) non-merged. */ int bufferSize = b->vertexCount * g->sizeOfVertex(); - if (b->merged) - bufferSize += b->vertexCount * sizeof(float) + b->indexCount * sizeof(quint16); - else - bufferSize += unmergedIndexSize; + int ibufferSize = 0; + if (b->merged) { + bufferSize += b->vertexCount * sizeof(float); + ibufferSize = b->indexCount * sizeof(quint16); + } else { + ibufferSize = unmergedIndexSize; + } + +#ifdef QSG_SEPARATE_INDEX_BUFFER + map(&b->ibo, ibufferSize); +#else + bufferSize += ibufferSize; +#endif map(&b->vbo, bufferSize); if (Q_UNLIKELY(debug_upload)) qDebug() << " - batch" << b << " first:" << b->first << " root:" @@ -1766,21 +1780,35 @@ void Renderer::uploadBatch(Batch *b) if (b->merged) { char *vertexData = b->vbo.data; char *zData = vertexData + b->vertexCount * g->sizeOfVertex(); +#ifdef QSG_SEPARATE_INDEX_BUFFER + char *indexData = b->ibo.data; +#else char *indexData = zData + b->vertexCount * sizeof(float); +#endif quint16 iOffset = 0; e = b->first; int verticesInSet = 0; int indicesInSet = 0; b->drawSets.reset(); - b->drawSets << DrawSet(0, zData - vertexData, indexData - vertexData); +#ifdef QSG_SEPARATE_INDEX_BUFFER + int drawSetIndices = 0; +#else + int drawSetIndices = indexData - vertexData; +#endif + b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices); while (e) { verticesInSet += e->node->geometry()->vertexCount(); if (verticesInSet > 0xffff) { b->drawSets.last().indexCount = indicesInSet; +#ifdef QSG_SEPARATE_INDEX_BUFFER + drawSetIndices = indexData - b->ibo.data; +#else + drawSetIndices = indexData - b->vbo.data; +#endif b->drawSets << DrawSet(vertexData - b->vbo.data, zData - b->vbo.data, - indexData - b->vbo.data); + drawSetIndices); iOffset = 0; verticesInSet = e->node->geometry()->vertexCount(); indicesInSet = 0; @@ -1791,7 +1819,11 @@ void Renderer::uploadBatch(Batch *b) b->drawSets.last().indexCount = indicesInSet; } else { char *vboData = b->vbo.data; +#ifdef QSG_SEPARATE_INDEX_BUFFER + char *iboData = b->ibo.data; +#else char *iboData = vboData + b->vertexCount * g->sizeOfVertex(); +#endif Element *e = b->first; while (e) { QSGGeometry *g = e->node->geometry(); @@ -1858,6 +1890,9 @@ void Renderer::uploadBatch(Batch *b) } unmap(&b->vbo); +#ifdef QSG_SEPARATE_INDEX_BUFFER + unmap(&b->ibo, true); +#endif if (Q_UNLIKELY(debug_upload)) qDebug() << " --- vertex/index buffers unmapped, batch upload completed..."; @@ -1982,11 +2017,16 @@ void Renderer::renderMergedBatch(const Batch *batch) glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id); char *indexBase = 0; +#ifdef QSG_SEPARATE_INDEX_BUFFER + const Buffer *indexBuf = &batch->ibo; +#else + const Buffer *indexBuf = &batch->vbo; +#endif if (m_context->hasBrokenIndexBufferObjects()) { - indexBase = batch->vbo.data; + indexBase = indexBuf->data; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } else { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vbo.id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf->id); } @@ -2056,12 +2096,17 @@ void Renderer::renderUnmergedBatch(const Batch *batch) glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id); char *indexBase = 0; +#ifdef QSG_SEPARATE_INDEX_BUFFER + const Buffer *indexBuf = &batch->ibo; +#else + const Buffer *indexBuf = &batch->vbo; +#endif if (batch->indexCount) { if (m_context->hasBrokenIndexBufferObjects()) { - indexBase = batch->vbo.data; + indexBase = indexBuf->data; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } else { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vbo.id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf->id); } } @@ -2082,7 +2127,11 @@ void Renderer::renderUnmergedBatch(const Batch *batch) } int vOffset = 0; +#ifdef QSG_SEPARATE_INDEX_BUFFER + char *iOffset = indexBase; +#else char *iOffset = indexBase + batch->vertexCount * gn->geometry()->sizeOfVertex(); +#endif QMatrix4x4 rootMatrix = batch->root ? matrixForRoot(batch->root) : QMatrix4x4(); diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 5404b669a0..001c3b21ab 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -276,6 +276,9 @@ struct Batch mutable uint uploadedThisFrame : 1; // solely for debugging purposes Buffer vbo; +#ifdef QSG_SEPARATE_INDEX_BUFFER + Buffer ibo; +#endif QDataBuffer<DrawSet> drawSets; }; @@ -411,7 +414,7 @@ private: void map(Buffer *buffer, int size); - void unmap(Buffer *buffer); + void unmap(Buffer *buffer, bool isIndexBuf = false); void buildRenderListsFromScratch(); void buildRenderListsForTaggedRoots(); @@ -495,6 +498,9 @@ Batch *Renderer::newBatch() } else { b = new Batch(); memset(&b->vbo, 0, sizeof(Buffer)); +#ifdef QSG_SEPARATE_INDEX_BUFFER + memset(&b->ibo, 0, sizeof(Buffer)); +#endif } b->init(); return b; diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 6f64c881a8..6868e10b90 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -1,5 +1,7 @@ !contains(QT_CONFIG, egl):DEFINES += QT_NO_EGL +# DEFINES += QSG_SEPARATE_INDEX_BUFFER + # Core API HEADERS += \ $$PWD/coreapi/qsgbatchrenderer_p.h \ |