Skip to content

Commit 12a7239

Browse files
author
operel
committed
vertex array object fix for gl 3.3
Signed-off-by: operel <[email protected]> Extra robustness to missing gl / cugl features Signed-off-by: operel <[email protected]>
1 parent 1f4203a commit 12a7239

File tree

3 files changed

+39
-16
lines changed

3 files changed

+39
-16
lines changed

wisp/framework/state.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class InteractiveRendererState:
114114
'none' - No inherent antialising mode will be activated.
115115
"""
116116

117-
gl_version: str = "GL 3.3"
117+
gl_version: str = "GL 3.3 core"
118118
""" Wisp applications rely on glumpy + OpenGL to render specific features and blit content to the window.
119119
This setting configures glumpy to load with a specific OpenGL backend.
120120
OpenGL 3.3 is widely supported and is therefore assumed to be the default.

wisp/renderer/app/wisp_app.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from __future__ import annotations
1212
from abc import ABC
13+
import logging
1314
import numpy as np
1415
import torch
1516
from glumpy import app, gloo, gl, ext
@@ -111,7 +112,8 @@ def __init__(self, wisp_state, window_name="wisp app"):
111112
# There we generate a simple billboard GL program (normally with a shared CUDA resource)
112113
# Canvas content will be blitted onto it
113114
self.canvas_program: Optional[gloo.Program] = None # GL program used to paint a single billboard
114-
self.cugl_rgb_handle = None # CUDA buffer, as a shared resource with OpenGL
115+
self.vao: Optional[gloo.VertexArray] = None # Vertex array object to hold GL buffers
116+
self.cugl_rgb_handle = None # CUDA buffer, as a shared resource with OpenGL
115117
self.cugl_depth_handle = None
116118

117119
try:
@@ -133,7 +135,7 @@ def __init__(self, wisp_state, window_name="wisp app"):
133135
self.change_user_mode(self.default_user_mode())
134136

135137
self.redraw() # Refresh RendererCore
136-
138+
137139
def add_pipeline(self, name, pipeline, transform=None):
138140
"""Register a neural fields pipeline into the scene graph.
139141
@@ -143,7 +145,7 @@ def add_pipeline(self, name, pipeline, transform=None):
143145
transform (wisp.core.ObjectTransform): The transform for the pipeline.
144146
"""
145147
add_pipeline_to_scene_graph(self.wisp_state, name, pipeline, transform=transform)
146-
148+
147149
def add_widget(self, widget):
148150
""" Adds a widget to the list of widgets.
149151
@@ -242,10 +244,10 @@ def run(self):
242244
"""
243245
app.run() # App clock should always run as frequently as possible (background tasks should not be limited)
244246

245-
def _create_window(self, width, height, window_name, gl_version):
247+
def _create_window(self, width, height, window_name, gl_version) -> app.Window:
246248
# Currently assume glfw backend due to integration with imgui
247249
app.use(f"glfw_imgui ({gl_version})")
248-
win_config = app.configuration.Configuration()
250+
win_config = app.configuration.get_default()
249251
if self.wisp_state.renderer.antialiasing == 'msaa_4x':
250252
win_config.samples = 4
251253

@@ -267,7 +269,7 @@ def _create_window(self, width, height, window_name, gl_version):
267269
return window
268270

269271
@staticmethod
270-
def _create_gl_depth_billboard_program(texture: np.ndarray, depth_texture: np.ndarray):
272+
def _create_gl_depth_billboard_program(texture: np.ndarray, depth_texture: np.ndarray) -> gloo.Program:
271273
vertex = """
272274
uniform float scale;
273275
attribute vec2 position;
@@ -301,8 +303,22 @@ def _create_gl_depth_billboard_program(texture: np.ndarray, depth_texture: np.nd
301303
canvas['depth_tex'] = depth_texture
302304
return canvas
303305

306+
def _create_vao(self, gl_config: app.Configuration) -> gloo.VertexArray:
307+
""" Creates a "default" VertexBufferObject to be used by the GL Programs. """
308+
# OpenGL 3.3+ requires that a VertexArrayObject is always bound.
309+
# Since glumpy's glfw_imgui backend doesn't guarantee one, and imgui expects a GL context with OpenGL >= 3.3,
310+
# we create a default one here for all programs and buffers which gloo will bind its buffers to.
311+
# This isn't how VAOs are meant to be used: it is more correct to keep a VAO per group of buffers (in Wisp's
312+
# case, at least once per gizmo). However, the following glumpy issue needs to be sorted out first, see:
313+
# https://github.com/glumpy/glumpy/issues/310
314+
vao = None
315+
if gl_config.major_version >= 3:
316+
vao = np.zeros(0, np.float32).view(gloo.VertexArray) # Keep GL happy by binding with some VAO handle
317+
vao.activate() # Actual vao created here
318+
return vao
319+
304320
@staticmethod
305-
def _create_screen_texture(res_h, res_w, channel_depth, dtype=np.uint8):
321+
def _create_screen_texture(res_h, res_w, channel_depth, dtype=np.uint8) -> gloo.Texture2D:
306322
""" Create and return a Texture2D with gloo and a cuda handle. """
307323
if issubclass(dtype, np.integer):
308324
tex = np.zeros((res_h, res_w, channel_depth), dtype).view(gloo.Texture2D)
@@ -317,8 +333,14 @@ def _create_screen_texture(res_h, res_w, channel_depth, dtype=np.uint8):
317333

318334
def _register_cugl_shared_texture(self, tex):
319335
if self.blitdevice2device:
320-
# Create shared GL / CUDA resource
321-
handle = cuda_register_gl_image(image=int(tex.handle), target=tex.target)
336+
try:
337+
# Create shared GL / CUDA resource
338+
handle = cuda_register_gl_image(image=int(tex.handle), target=tex.target)
339+
except RuntimeError as e:
340+
logging.warning('cugl device2device interface is not available in this env, '
341+
'wisp will fallback & memcopy cuda output to gl canvas through cpu.')
342+
self.blitdevice2device = False
343+
handle = None
322344
else:
323345
# No shared resource required, as we copy from cuda buffer -> cpu -> GL texture
324346
handle = None
@@ -385,7 +407,7 @@ def render_canvas(self, render_core, time_delta, force_render):
385407

386408
return img, depth_img
387409

388-
def _blit_to_gl_renderbuffer(self, img, depth_img, canvas_program, cugl_rgb_handle, cugl_depth_handle, height):
410+
def _blit_to_gl_renderbuffer(self, img, depth_img, canvas_program, vao, cugl_rgb_handle, cugl_depth_handle, height):
389411
if self.blitdevice2device:
390412
# Device to device copy: Copy CUDA buffer to GL Texture mem
391413
shared_tex = canvas_program['tex']
@@ -402,6 +424,8 @@ def _blit_to_gl_renderbuffer(self, img, depth_img, canvas_program, cugl_rgb_hand
402424
canvas_program['tex'] = img.cpu().numpy()
403425
canvas_program['depth_tex'] = depth_img.cpu().numpy()
404426

427+
if vao is not None:
428+
vao.activate()
405429
canvas_program.draw(gl.GL_TRIANGLE_STRIP)
406430

407431
def update_renderer_state(self, wisp_state, dt):
@@ -487,7 +511,7 @@ def render(self):
487511

488512
# glumpy code injected within the pyimgui render loop to blit the renderer core output to the actual canvas
489513
# The torch buffers are copied by with cuda, connected as shared resources as 2d GL textures
490-
self._blit_to_gl_renderbuffer(img, depth_img, self.canvas_program, self.cugl_rgb_handle,
514+
self._blit_to_gl_renderbuffer(img, depth_img, self.canvas_program, self.vao, self.cugl_rgb_handle,
491515
self.cugl_depth_handle, self.height)
492516

493517
# Finally, render OpenGL gizmos on the canvas.
@@ -544,6 +568,7 @@ def on_resize(self, width, height):
544568
self.cugl_depth_handle = self._register_cugl_shared_texture(depth_tex)
545569
if self.canvas_program is None:
546570
self.canvas_program = self._create_gl_depth_billboard_program(texture=tex, depth_texture=depth_tex)
571+
self.vao = self._create_vao(self.window.config)
547572
else:
548573
if self.canvas_program['tex'] is not None:
549574
self.canvas_program['tex'].delete()
@@ -708,7 +733,6 @@ def dump_framebuffer(self, path='./framebuffer'):
708733
framebuffer = np.flip(framebuffer, 0)
709734
ext.png.from_array(framebuffer, 'L').save(path + '_depth.png')
710735

711-
712736
def register_io_mappings(self):
713737
WispMouseButton.register_symbol(WispMouseButton.LEFT_BUTTON, app.window.mouse.LEFT)
714738
WispMouseButton.register_symbol(WispMouseButton.MIDDLE_BUTTON, app.window.mouse.MIDDLE)
@@ -720,4 +744,3 @@ def register_io_mappings(self):
720744
WispKey.register_symbol(WispKey.DOWN, app.window.key.DOWN)
721745

722746
# TODO: Take care of remaining mappings, and verify the event handlers of glumpy were not overriden
723-

wisp/renderer/gizmos/ogl/world_grid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ def __init__(self, squares_per_axis: int = 20, grid_size: float = 1.0,
5454
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
5555
try:
5656
gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_ANISOTROPY, 16)
57-
except AttributeError as e:
58-
logging.warning('GL_TEXTURE_MAX_ANISOTROPY not available; appearance may suffer')
57+
except Exception as e:
58+
logging.warning('GL_TEXTURE_MAX_ANISOTROPY not available; world grid cosmetic appearance may degrade.')
5959
gl.glGenerateMipmap(gl.GL_TEXTURE_2D)
6060
tex.deactivate()
6161

0 commit comments

Comments
 (0)