Using glPrimitiveRestartIndex to declare multiple geometries in the same VBO

TL;DR : OpenGL does not consider the restart value and instead see it as a vertex. What am I doing wrong?

I have a VBO storing a collection of polygons (will move to triangle fans later, for now does not help). I am willing to use glPrimitiveRestartIndex to declare when a polygon finishes and when a new one start.

I draw with

GL2 gl2 = gl.getGL2();
gl2.glEnable (GL2.GL_PRIMITIVE_RESTART);
gl2.glPrimitiveRestartIndex (PRIMITIVE_RESTART_VALUE); // 0xffffffff, or -1
...
gl2.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL);
gl2.glDrawElements (glGeometryType, elementSize, GL.GL_UNSIGNED_INT, firstCoordOffset);

I generate VBO with a restart value every 4 point

protected void append(int[] hexahedronPoints, int p1, int p2, int p3, int p4) {
  elementArray[cursor++] = hexahedronPoints[p1];
  elementArray[cursor++] = hexahedronPoints[p2];
  elementArray[cursor++] = hexahedronPoints[p3];
  elementArray[cursor++] = hexahedronPoints[p4];

  if(DrawableVBO2.PRIMITIVE_RESTART)
    elementArray[cursor++] = DrawableVBO2.PRIMITIVE_RESTART_VALUE;
}

OpenGL does not consider the restart value and instead see it as a vertex. The below picture show a VBO fed with an element array made of 4 indices and 1 primitive restart flag. The four points are labeled and declared in order : 3, 2, 6, 7. Then comes the restart flag that is considered as a fifth point in the polygon.

When drawing, I verify that

  • element array has the appropriate content. |3|2|6|7|-1| (where -1 is my restart flag, and the buffer has no more element).
  • indices refer to the vertex I setup (1st point : x=1.0 y=2.0 z=-2.0, 2nd point : x=1.0 y=2.0 z=-1.5, 3rd point : x=0.0 y=2.0 z=-1.5, 4th point : x=0.0 y=2.0 z=-2.0).

Complete VBO is here, with drawing here. Defining the restart flag is done here.

NB : Any point of the vertex array is shared between up to 8 polygons. I can not use glMultiDrawElements as a replacer (that would require to repeat most of the vertices of the vertex array by a factor of 8)

NB2 : My code is in Java with JOGL.

Any number of things could have happened between setting the restart index and drawing. Or maybe you uploaded the data wrong.

True. I have edited what I have verified at the drawing step (element and vertice buffers).

I haven’t found why primitive restart fails, but I found an alternative solution : glMultiDrawArrays let me specify an array of start index and length information. This is not the best solution as the geometry size (4=quad) is always the same for all polygon.

What GPU, GPU drivers, Java version/arch, and OS are you trying this on and having issues?

In your code, I see this:

  protected void applyPrimitiveRestartIfEnabled(GL gl) {
    if (PRIMITIVE_RESTART) {
      if (gl.isGL2()) {
        GL2 gl2 = gl.getGL2();
        gl2.glEnable(GL2.GL_PRIMITIVE_RESTART);
        gl2.glEnable(GL2.GL_PRIMITIVE_RESTART_FIXED_INDEX);  <--------------
        gl2.glPrimitiveRestartIndex(PRIMITIVE_RESTART_VALUE);
      }
    }
  }

glEnable( GL_PRIMITIVE_RESTART_FIXED_INDEX ) enables a driver-internal default for the restart index to be (2^n-1), completely ignoring the index you set via glPrimitiveRestartIndex() above.

In your case with 32-bit UINTs (GL_UNSIGNED_INT), that means 2^32-1 == ~0 == (unsigned int)(-1) is used.

  public static int PRIMITIVE_RESTART_VALUE = -1;// 0xffffffff;

This might end up being the same (would in C++), but this is Java and I don’t know anything about your Java version/arch/behavior, underlying GL context version, and GL driver support.

So I would start by getting rid of that glDisable( GL_PRIMITIVE_RESTART_FIXED_INDEX ) and try a specific, small, fixed, unsigned index provided to glPrimitiveRestartIndex() and see if you fare better. If so, you can adapt from there. Maybe GL_PRIMITIVE_RESTART_FIXED_INDEX would work for you, but there’s some error in your code that’s preventing it from being used properly.

Also, what is glGeometryType? It sounds like you’re using GL_POLYGON which is old, deprecated, and probably not as well tested. If you’re going to give primitive restart a fair try, consider using GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN. However, FWIW you may find that you get better perf by just submitting indexed GL_TRIANGLES batches and then there’s no need for primitive restart at all. In the general mesh case with optimized triangle order, you can often get better post vertex shader transform cache reuse (i.e. fewer vertex shader executions) with the latter path anyway, which is part of the win.

Thanks!

I am working with Java 8 and 9, using JOGL on Mac OS 10.12.6, with Intel Core I7 CPU and NVIDIA GeForce GT 650M GPU (my OS also list a Intel HD Graphics 4000 - not sure why).

I used the GL_PRIMITIVE_RESTART_FIXED_INDEX here just to try all but indeed commented this. I also played with the restart values, tried 0xffffffff and also -1 to make it more readable for a pseudo index.

I indeed use GL_POLYGON for now as it works correctly most of the time in other cases. Despite GL_TRIANGLE_FAN did not change anything, I’ll indeed stick to this to avoid using GPU primitives that might still exist but not properly tested… I remain a bit afraid of fully working with triangles because I often need to toggle wireframe ON and prefer a QUAD shape - but I can workaround this in my framework primitives.

I am currently evaluating all other way of defining indices with VBO. I’ll come back to this a bit later and will inform you if (and how) I can get this working.

Seeing this w.r.t. OpenGL triggered some warning neurons, such as:

I guess your MacOS X is prior to that. However, searching the forums for MacOS and primitive restart calls into question whether this ever worked properly on Mac’s OpenGL implementation (…that is, before Apple decided to kick OpenGL developers off of Mac, or onto API mappers like Zink and MoltenVK). For a few of those links, see below.

Starting from first principles: does your GL support primitive restart?

That’s not enough information. In your running application, query GL_VERSION and GL_EXTENSIONS and share the result here.

I expect that: because you’re using GL_POLYGON without errors, you will see that JOGL has given you a GL 2.1 (i.e. “compatibility”) context. And you will see that GL_EXTENSIONS does not include “NV_primitive_restart”, because no Mac OS driver ever implemented that extension. Therefore: your GL does not support primitive restart. It is working correctly, but you’re trying to use a feature it doesn’t claim to implement.

If you can get JOGL to create a Core Profile GL context, then primitive restart will be supported. But you’ll have to rewrite all of your GL code which uses features removed in Core Profile (e.g. GL_POLYGON.)

For reference, Core Profile is the only way to use primitive restart on Mac OS. It isn’t exposed in the compatibility profile. ARB_ES3_compatibility isn’t implemented, so GL_PRIMITIVE_RESTART_FIXED_INDEX isn’t an option.

Thanks, these links are very helpful and discourage to use primitive restart in a cross-platform java API (Jzy3D, the OS API I maintain allows building simple 3D charts that will work everywhere).

Thanks this is very instructive. I did not imagine primitive restart would require an extension. Do you know any page or documentation explaining what extensions can be made available when using a core profile? Do you recommend a page clarifying what feature of OpenGL are supported everywhere vs those that require an extension for working?

The NV_primitive_restart is indeed not listed in the GL extensions of my mac, as shown on the export below :

Capabilities  : GLCaps[rgba 8/8/8/8, opaque, accum-rgba 0/0/0/0, dp/st/ms 16/0/0, dbl, mono  , hw, GLProfile[GL2/GL2.hw], offscr[fbo]]
GL_VENDOR     : NVIDIA Corporation
GL_RENDERER   : NVIDIA GeForce GT 650M OpenGL Engine
GL_VERSION    : 2.1 NVIDIA-10.17.5 355.10.05.45f01
GL_EXTENSIONS : 
	GL_ARB_color_buffer_float
	GL_ARB_depth_buffer_float
	GL_ARB_depth_clamp
	GL_ARB_depth_texture
	GL_ARB_draw_buffers
	GL_ARB_draw_elements_base_vertex
	GL_ARB_draw_instanced
	GL_ARB_fragment_program
	GL_ARB_fragment_program_shadow
	GL_ARB_fragment_shader
	GL_ARB_framebuffer_object
	GL_ARB_framebuffer_sRGB
	GL_ARB_half_float_pixel
	GL_ARB_half_float_vertex
	GL_ARB_imaging
	GL_ARB_instanced_arrays
	GL_ARB_multisample
	GL_ARB_multitexture
	GL_ARB_occlusion_query
	GL_ARB_pixel_buffer_object
	GL_ARB_point_parameters
	GL_ARB_point_sprite
	GL_ARB_provoking_vertex
	GL_ARB_seamless_cube_map
	GL_ARB_shader_objects
	GL_ARB_shader_texture_lod
	GL_ARB_shading_language_100
	GL_ARB_shadow
	GL_ARB_sync
	GL_ARB_texture_border_clamp
	GL_ARB_texture_compression
	GL_ARB_texture_compression_rgtc
	GL_ARB_texture_cube_map
	GL_ARB_texture_env_add
	GL_ARB_texture_env_combine
	GL_ARB_texture_env_crossbar
	GL_ARB_texture_env_dot3
	GL_ARB_texture_float
	GL_ARB_texture_mirrored_repeat
	GL_ARB_texture_non_power_of_two
	GL_ARB_texture_rectangle
	GL_ARB_texture_rg
	GL_ARB_transpose_matrix
	GL_ARB_vertex_array_bgra
	GL_ARB_vertex_blend
	GL_ARB_vertex_buffer_object
	GL_ARB_vertex_program
	GL_ARB_vertex_shader
	GL_ARB_window_pos
	GL_EXT_abgr
	GL_EXT_bgra
	GL_EXT_bindable_uniform
	GL_EXT_blend_color
	GL_EXT_blend_equation_separate
	GL_EXT_blend_func_separate
	GL_EXT_blend_minmax
	GL_EXT_blend_subtract
	GL_EXT_clip_volume_hint
	GL_EXT_debug_label
	GL_EXT_debug_marker
	GL_EXT_depth_bounds_test
	GL_EXT_draw_buffers2
	GL_EXT_draw_range_elements
	GL_EXT_fog_coord
	GL_EXT_framebuffer_blit
	GL_EXT_framebuffer_multisample
	GL_EXT_framebuffer_multisample_blit_scaled
	GL_EXT_framebuffer_object
	GL_EXT_framebuffer_sRGB
	GL_EXT_geometry_shader4
	GL_EXT_gpu_program_parameters
	GL_EXT_gpu_shader4
	GL_EXT_multi_draw_arrays
	GL_EXT_packed_depth_stencil
	GL_EXT_packed_float
	GL_EXT_provoking_vertex
	GL_EXT_rescale_normal
	GL_EXT_secondary_color
	GL_EXT_separate_specular_color
	GL_EXT_shadow_funcs
	GL_EXT_stencil_two_side
	GL_EXT_stencil_wrap
	GL_EXT_texture_array
	GL_EXT_texture_compression_dxt1
	GL_EXT_texture_compression_s3tc
	GL_EXT_texture_env_add
	GL_EXT_texture_filter_anisotropic
	GL_EXT_texture_integer
	GL_EXT_texture_lod_bias
	GL_EXT_texture_mirror_clamp
	GL_EXT_texture_rectangle
	GL_EXT_texture_shared_exponent
	GL_EXT_texture_sRGB
	GL_EXT_texture_sRGB_decode
	GL_EXT_timer_query
	GL_EXT_transform_feedback
	GL_EXT_vertex_array_bgra
	GL_APPLE_aux_depth_stencil
	GL_APPLE_client_storage
	GL_APPLE_element_array
	GL_APPLE_fence
	GL_APPLE_float_pixels
	GL_APPLE_flush_buffer_range
	GL_APPLE_flush_render
	GL_APPLE_object_purgeable
	GL_APPLE_packed_pixels
	GL_APPLE_pixel_buffer
	GL_APPLE_rgb_422
	GL_APPLE_row_bytes
	GL_APPLE_specular_vector
	GL_APPLE_texture_range
	GL_APPLE_transform_hint
	GL_APPLE_vertex_array_object
	GL_APPLE_vertex_array_range
	GL_APPLE_vertex_point_size
	GL_APPLE_vertex_program_evaluators
	GL_APPLE_ycbcr_422
	GL_ATI_separate_stencil
	GL_ATI_texture_env_combine3
	GL_ATI_texture_float
	GL_ATI_texture_mirror_once
	GL_IBM_rasterpos_clip
	GL_NV_blend_square
	GL_NV_conditional_render
	GL_NV_depth_clamp
	GL_NV_fog_distance
	GL_NV_fragment_program_option
	GL_NV_fragment_program2
	GL_NV_light_max_exponent
	GL_NV_multisample_filter_hint
	GL_NV_point_sprite
	GL_NV_texgen_reflection
	GL_NV_texture_barrier
	GL_NV_vertex_program2_option
	GL_NV_vertex_program3
	GL_SGIS_generate_mipmap
	GL_SGIS_texture_edge_clamp
	GL_SGIS_texture_lod

Primitive restart doesn’t require an extension if you’re using OpenGL 3.1 or later. GL_PRIMITIVE_RESTART_FIXED_INDEX` doesn’t require an extension if you’re using OpenGL 4.3 or later.

But as arekkusu says: if GL_POLYGON works, you’re using 2.1, because that mode doesn’t exist in the core profile and MacOS doesn’t support the compatibility profile, only 2.1 or 3+ core.

If you’re developing for a Mac, you really should use the core profile unless it’s a legacy code base which can’t feasibly be updated.

Hi,
I am indeed with GL_VERSION : 2.1 NVIDIA-10.17.5 355.10.05.45f01.

I forced the GL2 core profile with JOGL to ensure Jzy3D API stability across computers (in JOGL : GLProfile.get(GLProfile.GL2), while a more adaptive behaviour would be to invoke GLProfile.getMaxProgrammable(true), but I prefer sticking to the lowest requirements as charting does not require modern OpenGL).

For Mac OS: Apple has removed most of the OpenGL documentation from their current developer website. But you can browse the old documentation via archive.org. There, you will find the OpenGL Capabilities Table, which lists all extensions in all profiles for all drivers across a range of OS versions, with links to all the specifications.

For Windows and Linux: the set of drivers is much more diverse. A database is maintained at gpuinfo.org.

It is not much of an exaggeration to say that all features after 1992 are extensions. Indeed, this is the intended design.

That means avoiding primitive restart.