Skip to content

VertexArrayObject fix for gl 3.3 #118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion wisp/framework/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class InteractiveRendererState:
'none' - No inherent antialising mode will be activated.
"""

gl_version: str = "GL 3.3"
gl_version: str = "GL 3.3 core"
""" Wisp applications rely on glumpy + OpenGL to render specific features and blit content to the window.
This setting configures glumpy to load with a specific OpenGL backend.
OpenGL 3.3 is widely supported and is therefore assumed to be the default.
Expand Down
49 changes: 36 additions & 13 deletions wisp/renderer/app/wisp_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

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

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

self.redraw() # Refresh RendererCore

def add_pipeline(self, name, pipeline, transform=None):
"""Register a neural fields pipeline into the scene graph.

Expand All @@ -143,7 +145,7 @@ def add_pipeline(self, name, pipeline, transform=None):
transform (wisp.core.ObjectTransform): The transform for the pipeline.
"""
add_pipeline_to_scene_graph(self.wisp_state, name, pipeline, transform=transform)

def add_widget(self, widget):
""" Adds a widget to the list of widgets.

Expand Down Expand Up @@ -242,10 +244,10 @@ def run(self):
"""
app.run() # App clock should always run as frequently as possible (background tasks should not be limited)

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

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

@staticmethod
def _create_gl_depth_billboard_program(texture: np.ndarray, depth_texture: np.ndarray):
def _create_gl_depth_billboard_program(texture: np.ndarray, depth_texture: np.ndarray) -> gloo.Program:
vertex = """
uniform float scale;
attribute vec2 position;
Expand Down Expand Up @@ -301,8 +303,22 @@ def _create_gl_depth_billboard_program(texture: np.ndarray, depth_texture: np.nd
canvas['depth_tex'] = depth_texture
return canvas

def _create_vao(self, gl_config: app.Configuration) -> gloo.VertexArray:
""" Creates a "default" VertexBufferObject to be used by the GL Programs. """
# OpenGL 3.3+ requires that a VertexArrayObject is always bound.
# Since glumpy's glfw_imgui backend doesn't guarantee one, and imgui expects a GL context with OpenGL >= 3.3,
# we create a default one here for all programs and buffers which gloo will bind its buffers to.
# 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
# case, at least once per gizmo). However, the following glumpy issue needs to be sorted out first, see:
# https://github.com/glumpy/glumpy/issues/310
vao = None
if gl_config.major_version >= 3:
vao = np.zeros(0, np.float32).view(gloo.VertexArray) # Keep GL happy by binding with some VAO handle
vao.activate() # Actual vao created here
return vao

@staticmethod
def _create_screen_texture(res_h, res_w, channel_depth, dtype=np.uint8):
def _create_screen_texture(res_h, res_w, channel_depth, dtype=np.uint8) -> gloo.Texture2D:
""" Create and return a Texture2D with gloo and a cuda handle. """
if issubclass(dtype, np.integer):
tex = np.zeros((res_h, res_w, channel_depth), dtype).view(gloo.Texture2D)
Expand All @@ -317,8 +333,14 @@ def _create_screen_texture(res_h, res_w, channel_depth, dtype=np.uint8):

def _register_cugl_shared_texture(self, tex):
if self.blitdevice2device:
# Create shared GL / CUDA resource
handle = cuda_register_gl_image(image=int(tex.handle), target=tex.target)
try:
# Create shared GL / CUDA resource
handle = cuda_register_gl_image(image=int(tex.handle), target=tex.target)
except RuntimeError as e:
logging.warning('cugl device2device interface is not available in this env, '
'wisp will fallback & memcopy cuda output to gl canvas through cpu.')
self.blitdevice2device = False
handle = None
else:
# No shared resource required, as we copy from cuda buffer -> cpu -> GL texture
handle = None
Expand Down Expand Up @@ -385,7 +407,7 @@ def render_canvas(self, render_core, time_delta, force_render):

return img, depth_img

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

if vao is not None:
vao.activate()
canvas_program.draw(gl.GL_TRIANGLE_STRIP)

def update_renderer_state(self, wisp_state, dt):
Expand Down Expand Up @@ -487,7 +511,7 @@ def render(self):

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

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


def register_io_mappings(self):
WispMouseButton.register_symbol(WispMouseButton.LEFT_BUTTON, app.window.mouse.LEFT)
WispMouseButton.register_symbol(WispMouseButton.MIDDLE_BUTTON, app.window.mouse.MIDDLE)
Expand All @@ -720,4 +744,3 @@ def register_io_mappings(self):
WispKey.register_symbol(WispKey.DOWN, app.window.key.DOWN)

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

4 changes: 2 additions & 2 deletions wisp/renderer/gizmos/ogl/world_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ def __init__(self, squares_per_axis: int = 20, grid_size: float = 1.0,
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
try:
gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_ANISOTROPY, 16)
except AttributeError as e:
logging.warning('GL_TEXTURE_MAX_ANISOTROPY not available; appearance may suffer')
except Exception as e:
logging.warning('GL_TEXTURE_MAX_ANISOTROPY not available; world grid cosmetic appearance may degrade.')
gl.glGenerateMipmap(gl.GL_TEXTURE_2D)
tex.deactivate()

Expand Down