[gl2ps] [patch] Enable use without an OpenGL context

David Lonie david.lonie at kitware.com
Thu Jan 14 22:00:57 CET 2016


Hi Christophe,

I realized I wasn't very happy with the patch I'd made earlier and
made a few more changes that I think make the feature easier to
use/maintain.

Instead of passing 0 as the buffersize to disable OpenGL queries
(which was rather hacky), I've added a new option,
GL2PS_NO_OPENGL_CONTEXT that more clearly indicates the intended
effect.

I also added a runtime check that ensures the supplied options will
work without surprises. If NO_OPENGL_CONTEXT is set, the following
conditions are enforced:

- DRAW_BACKGROUND is off (can't query OpenGL for the clear color.
Users can manually draw a background rectangle as part of their
geometry).
- USE_CURRENT_VIEWPORT is off (can't query OpenGL for the viewport).
- NO_BLENDING is on (can't query OpenGL for the blending parameters).
- colormode is GL_RGBA (can't query OpenGL for the color map).

I've attached a new patch that implements this and adds the
restrictions to the documentation. It is based off of current SVN
trunk. Let me know if there are any concerns with this patch.

Thanks,
Dave

On Wed, Jan 13, 2016 at 9:31 AM, David Lonie <david.lonie at kitware.com> wrote:
> On Wed, Jan 13, 2016 at 2:46 AM, Christophe Geuzaine
> <cgeuzaine at ulg.ac.be> wrote:
>>
>> Hi David,
>>
>> This is a very good idea - I've merged the patch in SVN.
>
> Great!
>
>> Do you think it would be doable to incorporate the parsing of the transform feedback buffers directly in GL2PS?
>
> It would be possible. One of the issues I see is that the new buffers
> aren't annotated like the old feedback buffer -- there's no
> GL_POINT_TOKEN, etc. The captured attributes (and their ordering) are
> dependent on how each feedback transform buffer is configured.
>
> What I've done is keep a list of "roles" around with the feedback
> buffer to identify the attributes that are captured, e.g. "Vertex Clip
> Coordinates" or "RGBA color". The order of the roles specifies the
> order of the attributes in the buffer to make generic parsing
> possible. Something similar could be implemented in GL2PS.
>
> For instance, I encapsulate transform feedback buffers in the
> vtkTransformFeedback class:
> https://gitlab.kitware.com/dlonie/vtk/blob/opengl2-gl2ps/Rendering/OpenGL2/vtkTransformFeedback.h
> https://gitlab.kitware.com/dlonie/vtk/blob/opengl2-gl2ps/Rendering/OpenGL2/vtkTransformFeedback.cxx
>
> And parse them here in vtkOpenGLGL2PSHelperImpl::ProcessTransformFeedback:
> https://gitlab.kitware.com/dlonie/vtk/blob/opengl2-gl2ps/Rendering/GL2PSOpenGL2/vtkOpenGLGL2PSHelperImpl.cxx#L73
>
> So generic parsing is possible by providing role information or having
> a convention in place.
>
> But then, there are some complicated edge cases to worry about. For
> example, VTK tends to do coloring in the fragment shader, which cannot
> be captured into the transform feedback buffer, and the vert/geo
> shaders know nothing about colors. In situations like these, it's
> easier to insert the primitives manually since the feedback vertices
> can be combined with a separate color array.
>
> So I think it could be done, but it would add a lot of API bloat to
> cover all of the possibilities.
>
> Another idea to simplify this would be to add higher level
> "gl2psAdd[Point|Line|Triangle]" methods that are more straightforward
> to use than gl2psAddPolyPrimitive, but I found the latter easy enough
> to use.
>
> Dave
-------------- next part --------------
Index: gl2ps.c
===================================================================
--- gl2ps.c	(revision 616)
+++ gl2ps.c	(working copy)
@@ -912,14 +912,14 @@
 
   gl2ps->forcerasterpos = GL_FALSE;
 
-  /* If no buffer, just add directly to primitives */
-  if (gl2ps->buffersize > 0) {
+  /* If no OpenGL context, just add directly to primitives */
+  if (gl2ps->options & GL2PS_NO_OPENGL_CONTEXT) {
+    gl2psListAdd(gl2ps->primitives, &prim);
+  }
+  else {
     gl2psListAdd(gl2ps->auxprimitives, &prim);
     glPassThrough(GL2PS_TEXT_TOKEN);
   }
-  else {
-    gl2psListAdd(gl2ps->primitives, &prim);
-  }
 
   return GL2PS_SUCCESS;
 }
@@ -5619,7 +5619,7 @@
   GL2PSxyz eye = {0.0F, 0.0F, 100.0F * GL2PS_ZSCALE};
   GLint used = 0;
 
-  if (gl2ps->buffersize > 0) {
+  if ((gl2ps->options & GL2PS_NO_OPENGL_CONTEXT) == GL2PS_NONE) {
     used = glRenderMode(GL_RENDER);
   }
 
@@ -5689,6 +5689,34 @@
   return GL2PS_SUCCESS;
 }
 
+static GLboolean gl2psCheckOptions(GLint options, GLint colormode)
+{
+  if (options & GL2PS_NO_OPENGL_CONTEXT) {
+    if (options & GL2PS_DRAW_BACKGROUND) {
+      gl2psMsg(GL2PS_ERROR, "Options GL2PS_NO_OPENGL_CONTEXT and "
+                            "GL2PS_DRAW_BACKGROUND are incompatible.");
+      return GL_FALSE;
+    }
+    if (options & GL2PS_USE_CURRENT_VIEWPORT) {
+      gl2psMsg(GL2PS_ERROR, "Options GL2PS_NO_OPENGL_CONTEXT and "
+                            "GL2PS_USE_CURRENT_VIEWPORT are incompatible.");
+      return GL_FALSE;
+    }
+    if ((options & GL2PS_NO_BLENDING) == GL2PS_NONE) {
+      gl2psMsg(GL2PS_ERROR, "Option GL2PS_NO_OPENGL_CONTEXT requires "
+                            "option GL2PS_NO_BLENDING.");
+      return GL_FALSE;
+    }
+    if (colormode != GL_RGBA) {
+      gl2psMsg(GL2PS_ERROR, "Option GL2PS_NO_OPENGL_CONTEXT requires colormode "
+                            "to be GL_RGBA.");
+      return GL_FALSE;
+    }
+  }
+
+  return GL_TRUE;
+}
+
 /*********************************************************************
  *
  * Public routines
@@ -5712,6 +5740,13 @@
 
   gl2ps = (GL2PScontext*)gl2psMalloc(sizeof(GL2PScontext));
 
+  /* Validate options */
+  if (gl2psCheckOptions(options, colormode) == GL_FALSE) {
+    gl2psFree(gl2ps);
+    gl2ps = NULL;
+    return GL2PS_ERROR;
+  }
+
   if(format >= 0 && format < (GLint)(sizeof(gl2psbackends) / sizeof(gl2psbackends[0]))){
     gl2ps->format = format;
   }
@@ -5775,7 +5810,7 @@
   gl2ps->threshold[1] = ng ? 1.0F / (GLfloat)ng : 0.034F;
   gl2ps->threshold[2] = nb ? 1.0F / (GLfloat)nb : 0.100F;
   gl2ps->colormode = colormode;
-  gl2ps->buffersize = buffersize;
+  gl2ps->buffersize = buffersize > 0 ? buffersize : 2048 * 2048;
   for(i = 0; i < 3; i++){
     gl2ps->lastvertex.xyz[i] = -1.0F;
   }
@@ -5795,7 +5830,7 @@
 
   /* get default blending mode from current OpenGL state (enabled by
      default for SVG) */
-  if (gl2ps->buffersize > 0) {
+  if ((gl2ps->options & GL2PS_NO_BLENDING) == GL2PS_NONE) {
     gl2ps->blending = (gl2ps->format == GL2PS_SVG) ? GL_TRUE
                                                    : glIsEnabled(GL_BLEND);
     glGetIntegerv(GL_BLEND_SRC, &gl2ps->blendfunc[0]);
@@ -5808,7 +5843,9 @@
   if(gl2ps->colormode == GL_RGBA){
     gl2ps->colorsize = 0;
     gl2ps->colormap = NULL;
-    glGetFloatv(GL_COLOR_CLEAR_VALUE, gl2ps->bgcolor);
+    if ((gl2ps->options & GL2PS_NO_OPENGL_CONTEXT) == GL2PS_NONE) {
+      glGetFloatv(GL_COLOR_CLEAR_VALUE, gl2ps->bgcolor);
+    }
   }
   else if(gl2ps->colormode == GL_COLOR_INDEX){
     if(!colorsize || !colormap){
@@ -5862,12 +5899,16 @@
 
   gl2ps->primitives = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
   gl2ps->auxprimitives = gl2psListCreate(100, 100, sizeof(GL2PSprimitive*));
-  gl2ps->feedback = (GLfloat*)gl2psMalloc(gl2ps->buffersize * sizeof(GLfloat));
 
-  if (gl2ps->buffersize > 0) {
+  if ((gl2ps->options & GL2PS_NO_OPENGL_CONTEXT) == GL2PS_NONE) {
+    gl2ps->feedback = (GLfloat*)gl2psMalloc(gl2ps->buffersize * sizeof(GLfloat));
     glFeedbackBuffer(gl2ps->buffersize, GL_3D_COLOR, gl2ps->feedback);
     glRenderMode(GL_FEEDBACK);
   }
+  else {
+    gl2ps->feedback = NULL;
+    gl2ps->buffersize = 0;
+  }
 
   return GL2PS_SUCCESS;
 }
@@ -6049,8 +6090,8 @@
     break;
   }
 
-  /* If no buffer, just add directly to primitives */
-  if (gl2ps->buffersize > 0) {
+  /* If no OpenGL context, just add directly to primitives */
+  if ((gl2ps->options & GL2PS_NO_OPENGL_CONTEXT) == GL2PS_NONE) {
     gl2psListAdd(gl2ps->auxprimitives, &prim);
     glPassThrough(GL2PS_DRAW_PIXELS_TOKEN);
   }
@@ -6186,6 +6227,10 @@
 {
   if(!gl2ps) return GL2PS_UNINITIALIZED;
 
+  if(gl2psCheckOptions(options, gl2ps->colormode) == GL_FALSE) {
+    return GL2PS_ERROR;
+  }
+
   gl2ps->options = options;
 
   return GL2PS_SUCCESS;
Index: gl2ps.h
===================================================================
--- gl2ps.h	(revision 616)
+++ gl2ps.h	(working copy)
@@ -134,6 +134,7 @@
 #define GL2PS_COMPRESS             (1<<10)
 #define GL2PS_NO_BLENDING          (1<<11)
 #define GL2PS_TIGHT_BOUNDING_BOX   (1<<12)
+#define GL2PS_NO_OPENGL_CONTEXT    (1<<13)
 
 /* Arguments for gl2psEnable/gl2psDisable */
 
Index: gl2ps.tex
===================================================================
--- gl2ps.tex	(revision 616)
+++ gl2ps.tex	(working copy)
@@ -293,6 +293,23 @@
   program. For PDF files the compression is done ``locally'' for each
   group of primitives, in accordance with the official PDF
   specification.
+\item[\dd{GL2PS_NO_OPENGL_CONTEXT}] GL2PS will not query an OpenGL
+  context for information, nor will it create/parse a feedback
+  buffer. This is required for newer OpenGL versions that have
+  deprecated the feedback render mode. Using GL2PS in this mode
+  requires all geometry to be added manually using
+  \dd{gl2psAddPolyPrimitive} (see \url{https://open.gl/feedback} for a
+  replacement to the deprecated OpenGL feedback
+  buffers). \dd{gl2psText}, \dd{gl2psTextOpt}, \dd{gl2psDrawPixels},
+  and \dd{gl2psSpecial} are still usable, but require the use of
+  \dd{gl2psForceRasterPos} to set the raster position.
+
+  This option is incompatible with \dd{GL2PS_DRAW_BACKGROUND} and
+  \dd{GL2PS_USE_CURRENT_VIEWPORT}, and requires \dd{GL2PS_NO_BLENDING}
+  to be set. The \dd{gl2psBeginPage} \dd{colormode} must be set to
+  \dd{GL_RGBA} if this option is enabled. If these restrictions are
+  not met, a runtime error will be reported and \dd{gl2psBeginPage} /
+  \dd{gl2psSetOptions} will return \dd{GL2PS_ERROR}.
 \end{description}
 
 \item[\dd{colormode}] Specifies the color mode: \dd{GL_RGBA} or
@@ -675,6 +692,7 @@
 \noindent\dd{gl2psSetOptions} and \dd{gl2psGetOptions} return:
 \begin{description}
 \item[\dd{GL2PS_UNINITIALIZED}] if the library is not initialized;
+\item[\dd{GL2PS_ERROR}] if an error occurs;
 \item[\dd{GL2PS_SUCCESS}] otherwise.
 \end{description}