QGraphicsScene,项目坐标会影响性能吗?

13

使用下面的代码片段,我创建了一个包含 100,000 个矩形的场景。
性能良好,视图响应没有延迟。

QGraphicsScene * scene = new QGraphicsScene;
for (int y = -50000; y < 50000; y++) {
   scene->addRect(0, y * 25, 40, 20);
}
...
view->setScene(scene);

现在第二个代码片段很糟糕

for (int y = 0; y < 100000; y++) {
   scene->addRect(0, y * 25, 40, 20);
}

在场景元素的前半部分中,视图在鼠标和键事件上的响应有所延迟,而对于后半部分,则似乎没问题?!

前一个场景的sceneRect为(x, y, w, h) = (0, -1250000, 40, 2499995)。
后一个场景的sceneRect为(x, y, w, h) = (0, 0, 40, 2499995)。

我不知道为什么sceneRect会影响性能,因为BSP索引是基于相对项坐标的。

我是否遗漏了什么?我在文档中找不到任何信息, 此外Qt演示 40000 Chips 也将元素分布在(0,0)附近,没有说明选择该位置的原因。

 // Populate scene
 int xx = 0;
 int nitems = 0;
 for (int i = -11000; i < 11000; i += 110) {
     ++xx;
     int yy = 0;
     for (int j = -7000; j < 7000; j += 70) {
         ++yy;
         qreal x = (i + 11000) / 22000.0;
         qreal y = (j + 7000) / 14000.0;
         ...

1
我的第一个猜测是重新计算场景矩形。如果在添加项目之前设置sceneRect会发生什么? - Stephen Chu
@Stephen,结果是一样的。即使场景已经准备好并且是静态的,视图仍然延迟响应。 - Nick Dandoulakis
1
哇,非常令人惊讶。我进行了几次测试,似乎只有在查看1-50,000个框时小部件才会变得卡顿。之后,即50,001-100,000,一切都很流畅。更令人担忧的是,如果我范围为for (int y = -70000; y < 30000; y++),那么对于-70,000/-20,000的滚动是平稳的,对于-20,000/0则会卡顿,然后对于0/30,000又会变得平稳! - B. Decoster
3个回答

6

我有一个解决方案,但请承诺不问我为什么它有效,因为我真的不知道:-)

QGraphicsScene * scene = new QGraphicsScene;
// Define a fake symetrical scene-rectangle
scene->setSceneRect(0, -(25*100000+20), 40, 2 * (25*100000+20) );

for (int y = 0; y < 100000; y++) {
    scene->addRect(0, y * 25, 40, 20);
}
view->setScene(scene);
// Tell the view to display only the actual scene-objects area
view->setSceneRect(0, 0, 40, 25*100000+20);

当然,我真的想知道那种行为的原因。这是Qt中的一个错误吗,还是其他什么问题? - Nick Dandoulakis
赏金归你了Fivo,但案件仍未结案 :) - Nick Dandoulakis
谢谢Nick,虽然我觉得我并没有提供真正的解决方案,只是一个权宜之计。如果你在Qt网站上报告了一个错误,请及时更新任何官方解决方案。;-) - Fivos Vilanakis

0
对于一般情况,默认的索引方法BspTreeIndex就可以胜任。如果您的场景使用了许多动画并且遇到了速度慢的问题,您可以通过调用setItemIndexMethod(NoIndex)来禁用索引。Qt-doc 在插入之前,您需要调用setItemIndexMethod(QGraphicsScene::NoIndex):
scene->setItemIndexMethod(QGraphicsScene::NoIndex);

for (int y = 0; y < 100000; y++) {
   scene->addRect(0, y * 25, 40, 20);
}
//...

这不是我要找的,红色色调。如果场景包含数十万个或数百万个项目,则禁用索引并不是一个好主意。我想要的是追踪这种奇怪行为的根源。此外,我已经知道了加速代码的解决方法,Fivos Vilanakis还发布了另一种方法。 - Nick Dandoulakis
@Nick Dandoulakis 你是否已经提交了一个错误报告? - Arlen
但这是一个 bug 吗?也许是设计如此。无论如何,制作一份报告都是个好主意。 - Nick Dandoulakis
在插入项目后,您始终可以重新启用BSP索引。不确定是否仍会利用该错误。 - Ariya Hidayat

0

这可能是由于使用 float 时精度丢失导致的。32位浮点数具有23位尾数(或称为有效数字)、1位符号和8位指数,就像科学计数法一样。您有23个“有效数字”(实际上由于隐含的前导1而变成了24个),指数为2^exp,其中指数可以从-126到127范围内取值(其他值用于提供诸如NaNInf之类的内容)。因此,您可以表示非常大的数字,例如2^24*2^127,但是最接近该浮点数的下一个浮点数是(2^24-1)*2^127,相差170万亿亿亿亿。如果您尝试将较小的金额(例如1000)添加到此类数字中,则不会更改它。它没有办法表示。

这在计算机图形学中变得非常重要,因为你需要保留一些有效数字来制作小数部分。当你的场景范围达到1250000.0时,你可以加上0.1得到1250000.1。如果你将2500000.0和0.1相加,你会得到2500000.0。任何缩放或旋转都会使问题更加严重。如果你真的飞到那些坐标并查看你的场景,这可能会导致明显的视觉问题。
为什么以0为中心有帮助呢?因为浮点表示中有一个单独的符号位。在浮点中,在(-x,+x)之间有比从(0,2x)之间“更多的数字”。如果我没错的话,如果你只是将整个场景缩小1/2,它也会起作用。这将把最高有效位向下移动,使其在另一端具有精度。
为什么这会导致性能不佳?如果没有阅读Qt源代码,我只能推测,但考虑一种按位置存储对象的数据结构。如果由于精度损失而两个对象接触(或重叠),你可能需要做一些与未重叠时不同的事情。

嗨,本。我刚刚尝试了你的假设,将比例从2,500,000减少到1,000,000甚至500,000,但没有任何改进。我认为问题(或错误?)与精度无关,主要与Qt QGraphicsView如何使用QGraphicsScene的BSP索引来剪辑场景对象有关,当QGraphicsView视口被刷新/更新时。 - Fivos Vilanakis
嗨@Ben和@Fivo。我检查了BSP的实现,浮点精度似乎不是问题。 Qt的BSP只是将场景/区域分割成N*N个桶,而不考虑项目位置!也许这确实是“QGraphicsView”中的一个错误。 - Nick Dandoulakis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接