aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/accessible/quick/qaccessiblequickitem.cpp5
-rw-r--r--src/qml/compiler/qv4regalloc.cpp162
-rw-r--r--src/qml/compiler/qv4ssa.cpp148
-rw-r--r--src/qml/compiler/qv4ssa_p.h6
-rw-r--r--src/qml/debugger/qqmlprofilerservice.cpp2
-rw-r--r--src/qml/jsruntime/qv4context.cpp6
-rw-r--r--src/qml/jsruntime/qv4context_p.h1
-rw-r--r--src/qml/jsruntime/qv4engine.cpp33
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp3
-rw-r--r--src/qml/jsruntime/qv4mm.cpp15
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp8
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp4
-rw-r--r--src/qmltest/quicktest.cpp3
-rw-r--r--src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc15
-rw-r--r--src/quick/items/qquickitem.cpp6
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp85
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h8
-rw-r--r--src/quick/scenegraph/scenegraph.pri2
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 &current,
return -1;
for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) {
- const LifeTimeInterval::Range &currentRange = 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 \