Skip to content

fix: GL_POINT_SPRITE was not enabled for NoProfile#3236

Merged
j9ac9k merged 2 commits intopyqtgraph:masterfrom
pijyoi:fix-point-sprite
Feb 5, 2025
Merged

fix: GL_POINT_SPRITE was not enabled for NoProfile#3236
j9ac9k merged 2 commits intopyqtgraph:masterfrom
pijyoi:fix-point-sprite

Conversation

@pijyoi
Copy link
Copy Markdown
Contributor

@pijyoi pijyoi commented Feb 4, 2025

The previous implementation had notably neglected to enable GL_POINT_SPRITE for OpenGL <= 2.1.
If it had previously worked, it would probably be due to the permissiveness of the various OpenGL implementations.

Remarks:

  1. For Core Profile, it is not needed and trying to enable it will result in an error.
  2. macOS is the platform that will default to OpenGL 2.1 (NoProfile). However, point sprites was working despite not having enabled GL_POINT_SPRITE.

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Feb 5, 2025

In addition to fixing the bug, I also took the opportunity to move the computation of pxMode=False into the vertex shader.
Previously, moving the camera around without any data change would necessitate a recomputation of the scaled point size on the CPU. Now, that code executes in the vertex shader.

A script to demonstrate the improvement. On my laptop, master runs at 15 fps while this PR runs at 120 fps. Alternatively, task manager shows that the CPU utilization has decreased while the GPU utilization has increased. Your mileage may vary, depending on your system.

import importlib

import numpy as np

import pyqtgraph as pg
import pyqtgraph.opengl as gl
from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.examples.utils import FrameCounter

pngfile = importlib.resources.files("pyqtgraph.icons.peegee") / "peegee_512px@2x.png"
qimg = QtGui.QImage.fromData(pngfile.read_bytes()).mirrored(True, True)
qimg.convertTo(QtGui.QImage.Format.Format_RGBA8888)
data = pg.functions.ndarray_from_qimage(qimg).astype(np.float32) / 255
colors = data.reshape((-1, 4))

pg.mkQApp()
win = gl.GLViewWidget()
win.show()
win.setCameraPosition(distance=2, elevation=30)

rows, cols = data.shape[:2]
pos3 = np.zeros((rows,cols,3))
pos3[...,:2] = np.mgrid[:rows, :cols].transpose(1,2,0) / rows
pos3 = pos3.reshape(-1, 3)
item = gl.GLScatterPlotItem(pos=pos3, color=colors, size=0.001, pxMode=False, glOptions='translucent')

win.addItem(item)

def update():
    win.orbit(-1, 0)
    framecnt.update()

timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)

framecnt = FrameCounter()
framecnt.sigFpsUpdate.connect(lambda fps: win.setWindowTitle(f'{fps:.1f} fps'))

pg.exec()

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Feb 5, 2025

I had identical speedups on macOS (using M2 Max chip). Nice work!

@j9ac9k j9ac9k merged commit f8ab8e0 into pyqtgraph:master Feb 5, 2025
57 checks passed
@pijyoi pijyoi deleted the fix-point-sprite branch February 5, 2025 22:13
@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Feb 8, 2025

If you look closely, the left and right dots are smaller than the one in the center, even though they are all at the same depth.
The formulation used by GLScatterPlotItem is that size is scaled to the distance from the eye, so dots at the edges appear smaller (at the same depth).

image

An alternative implementation would be to have dots at the same depth to have the same size. This is a simpler calculation and doesn't require the camera position and view transform to be sent to the shader.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants