|
26 | 26 | QtCore = importlib.import_module(".QtCore", libname) |
27 | 27 | QtGui = importlib.import_module(".QtGui", libname) |
28 | 28 | QtWidgets = importlib.import_module(".QtWidgets", libname) |
| 29 | + # Uncomment the line below to try QtOpenGLWidgets.QOpenGLWidget instead of QWidget |
| 30 | + # QtOpenGLWidgets = importlib.import_module(".QtOpenGLWidgets", libname) |
29 | 31 | try: |
30 | 32 | # pyqt6 |
31 | 33 | WA_PaintOnScreen = QtCore.Qt.WidgetAttribute.WA_PaintOnScreen |
@@ -374,39 +376,50 @@ def _rc_force_draw(self): |
374 | 376 | self.repaint() |
375 | 377 |
|
376 | 378 | def _rc_present_bitmap(self, *, data, format, **kwargs): |
377 | | - width, height = data.shape[1], data.shape[0] # width, height |
378 | | - rect1 = QtCore.QRect(0, 0, width, height) |
379 | | - rect2 = self.rect() |
| 379 | + # Notes on performance: |
| 380 | + # |
| 381 | + # In the early stage of https://github.com/pygfx/rendercanvas/pull/138, |
| 382 | + # with a single copy-buffer, running the cube example on my M1, with |
| 383 | + # bitmap-present, I get about 75 FPS. |
| 384 | + # |
| 385 | + # AK: I tried to make this a QLabel and update a QPixmap by doing |
| 386 | + # self._pixmap.convertFromImage(qImage), but this is much slower. |
| 387 | + # |
| 388 | + # AK: I tried to maintain a self._qimage, so that it maybe gets bound |
| 389 | + # internally to a texture, but that even makes it slightly slower. |
| 390 | + # |
| 391 | + # AK: I tried inheriting from QOpenGLWidget, because I saw a blog post |
| 392 | + # (https://doc.qt.io/archives/qt-5.15/qtopengl-2dpainting-example.html) |
| 393 | + # that says it will make the painter hardware accelerated. |
| 394 | + # Interestingly, the content is drawn different to screen, as if the |
| 395 | + # rect args to drawImage are interpreted differently (or wrong), which |
| 396 | + # suggests that the painter *does* take a different path. Also, it can |
| 397 | + # be observed that the CPU usage is less that with QWidget. However, the |
| 398 | + # performance does not significantly increase (in my tests) |
| 399 | + # |
| 400 | + # If I understand things correctly, Qt uses composition on the CPU, so |
| 401 | + # there is an inherent limit to the performance. Rendering with GL likely |
| 402 | + # includes downloading the rendered image for composition. |
| 403 | + # |
| 404 | + # Also see https://github.com/pygfx/rendercanvas/pull/139 |
380 | 405 |
|
381 | | - painter = QtGui.QPainter(self) |
382 | | - # backingstore = self.backingStore() |
383 | | - # backingstore.beginPaint(rect2) |
384 | | - # painter = QtGui.QPainter(backingstore.paintDevice()) |
385 | | - |
386 | | - # We want to simply blit the image (copy pixels one-to-one on framebuffer). |
387 | | - # Maybe Qt does this when the sizes match exactly (like they do here). |
388 | | - # Converting to a QPixmap and painting that only makes it slower. |
389 | | - |
390 | | - # Just in case, set render hints that may hurt performance. |
391 | | - painter.setRenderHints( |
392 | | - painter.RenderHint.Antialiasing | painter.RenderHint.SmoothPixmapTransform, |
393 | | - False, |
394 | | - ) |
| 406 | + width, height = data.shape[1], data.shape[0] # width, height |
395 | 407 |
|
| 408 | + # Wrap the data in a QImage (no copy) |
396 | 409 | qtformat = BITMAP_FORMAT_MAP[format] |
397 | 410 | bytes_per_line = data.strides[0] |
398 | 411 | image = QtGui.QImage(data, width, height, bytes_per_line, qtformat) |
399 | 412 |
|
400 | | - painter.drawImage(rect2, image, rect1) |
401 | | - |
402 | | - # Uncomment for testing purposes |
403 | | - # painter.setPen(QtGui.QColor("#0000ff")) |
404 | | - # painter.setFont(QtGui.QFont("Arial", 30)) |
405 | | - # painter.drawText(100, 100, "This is an image") |
| 413 | + # Prep drawImage rects |
| 414 | + rect1 = QtCore.QRect(0, 0, width, height) |
| 415 | + rect2 = self.rect() |
406 | 416 |
|
| 417 | + # Paint the image. Nearest neighbor interpolation, like the other backends. |
| 418 | + painter = QtGui.QPainter(self) |
| 419 | + painter.setRenderHints(painter.RenderHint.Antialiasing, False) |
| 420 | + painter.setRenderHints(painter.RenderHint.SmoothPixmapTransform, False) |
| 421 | + painter.drawImage(rect2, image, rect1) |
407 | 422 | painter.end() |
408 | | - # backingstore.endPaint() |
409 | | - # backingstore.flush(rect2) |
410 | 423 |
|
411 | 424 | def _rc_set_logical_size(self, width, height): |
412 | 425 | width, height = int(width), int(height) |
|
0 commit comments