cuda.core: Fix graphics tests - #2701
Conversation
Stop treating graphics test-body failures as GL-unavailable skips, and bind a CUDA context in those tests so missing thread state cannot hide real errors.
f348d94 to
d45b987
Compare
|
/ok to test baf3e23 |
This comment has been minimized.
This comment has been minimized.
|
|
||
| with patch.object(Buffer, "close", new=tracking_close): | ||
| resource.close(stream=close_stream) | ||
| @pytest.mark.xfail( |
There was a problem hiding this comment.
I couldn't get this test to work. Should I remove it?
============================================================= short test summary info =============================================================
FAILED cuda_core/tests/test_graphics.py::test_close_while_mapped_passes_stream_override - TypeError: cannot set 'close' attribute of immutable type 'cuda.core._memory._buffer.Buffer'
========================================================== 1 failed, 29 passed in 0.63s ===========================================================
There was a problem hiding this comment.
Yes, I agree with removing it. The stream behavior of Buffer.close() is already covered elsewhere.
|
/ok to test 8fbb9cf |
|
pre-commit.ci run |
c095715 to
5dc9814
Compare
|
/ok to test 5dc9814 |
Andy-Jost
left a comment
There was a problem hiding this comment.
Looks good. The structure is improved and the main problem is addressed. Found a few minor issues to optionally clean up.
There was a problem hiding this comment.
Were the changes to this file intentional?
There was a problem hiding this comment.
Yes, I updated the lock file after adding the missing pyglet test dependency.
| def _setup_gl_buffer(pyglet, nbytes): | ||
| """Open a GL context and allocate a buffer. Returns (win, buf_id).""" | ||
| win = _open_gl_window(pyglet) | ||
| from pyglet.gl import gl as _gl | ||
|
|
||
| buf_id = _gl.GLuint(0) | ||
| _gl.glGenBuffers(1, ctypes.byref(buf_id)) | ||
| _gl.glBindBuffer(_gl.GL_ARRAY_BUFFER, buf_id.value) | ||
| _gl.glBufferData(_gl.GL_ARRAY_BUFFER, nbytes, None, _gl.GL_DYNAMIC_DRAW) | ||
| return win, buf_id |
There was a problem hiding this comment.
Optional: if setup fails after creating win or buf_id, those resources may leak. Low severity.
| def _setup_gl_texture(pyglet, width, height): | ||
| """Open a GL context and allocate a 2-D RGBA8 texture. Returns (win, tex_id, target).""" | ||
| win = _open_gl_window(pyglet) | ||
| from pyglet.gl import gl as _gl | ||
|
|
||
| tex_id = _gl.GLuint(0) | ||
| _gl.glGenTextures(1, ctypes.byref(tex_id)) | ||
| target = _gl.GL_TEXTURE_2D | ||
| _gl.glBindTexture(target, tex_id.value) | ||
| _gl.glTexParameteri(target, _gl.GL_TEXTURE_MIN_FILTER, _gl.GL_NEAREST) | ||
| _gl.glTexParameteri(target, _gl.GL_TEXTURE_MAG_FILTER, _gl.GL_NEAREST) | ||
| _gl.glTexImage2D(target, 0, _gl.GL_RGBA8, width, height, 0, _gl.GL_RGBA, _gl.GL_UNSIGNED_BYTE, None) | ||
| return win, tex_id, target |
There was a problem hiding this comment.
Optional: similarly, a failure after creating win or tex_id may leave those resources uncleaned. Low severity.
|
|
||
| with patch.object(Buffer, "close", new=tracking_close): | ||
| resource.close(stream=close_stream) | ||
| @pytest.mark.xfail( |
There was a problem hiding this comment.
Yes, I agree with removing it. The stream behavior of Buffer.close() is already covered elsewhere.
| def _setup_gl_texture(pyglet): | ||
| """Open a GL context and allocate a 2-D RGBA8 texture. Returns (win, tex_id, target).""" | ||
| if not pyglet.options.get("headless"): | ||
| # Hidden window path (WGL on Windows, GLX/WLS on Linux) | ||
| from pyglet import gl | ||
|
|
||
| config = gl.Config(double_buffer=False) | ||
| win = pyglet.window.Window(visible=False, config=config) | ||
| win.switch_to() | ||
| else: | ||
| # Headless EGL path; pyglet will arrange a pbuffer-like headless context | ||
| from pyglet.gl import headless # noqa: F401 | ||
|
|
||
| win = None | ||
|
|
||
| # Make a tiny texture so we have a real GL object to register | ||
| from pyglet.gl import gl as _gl | ||
|
|
||
| tex_id = _gl.GLuint(0) | ||
| _gl.glGenTextures(1, ctypes.byref(tex_id)) | ||
| target = _gl.GL_TEXTURE_2D | ||
| _gl.glBindTexture(target, tex_id.value) | ||
| _gl.glTexParameteri(target, _gl.GL_TEXTURE_MIN_FILTER, _gl.GL_NEAREST) | ||
| _gl.glTexParameteri(target, _gl.GL_TEXTURE_MAG_FILTER, _gl.GL_NEAREST) | ||
| width, height = 16, 16 | ||
| _gl.glTexImage2D(target, 0, _gl.GL_RGBA8, width, height, 0, _gl.GL_RGBA, _gl.GL_UNSIGNED_BYTE, None) | ||
| return win, tex_id, target |
There was a problem hiding this comment.
Optional: same potential leak issue here
|
Thanks. I have a few more things for a follow-on PR and will address your comments there. |
|
Description
The GL context helpers in
cuda_core/tests/test_graphics.pyandcuda_bindings/tests/test_graphics_apis.pywrappedyieldinexcept Exceptionand calledpytest.skip(), so CUDA errors, assertion failures, andTypeErrors raised in the test body were recorded as "could not create GL context" skips rather than failures. Thecuda_coregraphics tests also never requestedinit_cuda, so whetherfrom_gl_buffer/from_gl_imagesucceeded depended on leftover thread state from the previous test (pytest-randomlyseed).This PR:
_configure_pyglet_headless,_open_gl_window,_setup_gl_buffer,_setup_gl_texture) and a thin context manager that wraps only the setup call intry/exceptso test-body exceptions propagate as failures.init_cudato everycuda_coregraphics test that touches CUDA, and usesinit_cuda.create_stream()instead of a local_create_stream()helper.cuda_coretest classes to plain test functions (they carried no shared state or fixtures).StridedMemoryView.from_buffercall sites that passeddtype=np.float32(a scalar type class) instead ofnp.dtype(np.float32).test_close_while_mapped_passes_stream_overrideasxfail(strict=True)becauseBufferis an immutable Cython type and neitherpatch.objectnor__class__assignment can interceptBuffer.close.Checklist