• State safe TBGL programming

    TBGL rendering system is internally based on the OpenGL technology, therefore it shares the state machine oriented design. This means that once you set some of the states, such as drawing color, it stays enabled until you change it to something else. While this can be advantageous in some situations, in others it can cause problem, especially when trying to write modular code.

    This article describes the possible side effects of state handling and proposes new code style which should help you write modular graphic code of better quality.

    To demonstrate the issue, let's presume this model case with 2 drawing functions, DrawPositionMarker and DrawGrid:
    FUNCTION DrawPositionMarker(x As Single, y As Single)
    
      TBGL_PushMatrix
        ' -- Enable red color
        TBGL_Color(255, 0, 0)
        TBGL_Translate(x, y)
    
        ' -- Draw the cross
        TBGL_BeginPoly(%GL_LINES)    
          TBGL_Vertex(-1, 0 )
          TBGL_Vertex( 1, 0 )
    
          TBGL_Vertex( 0,-1 )
          TBGL_Vertex( 0, 1 )
        TBGL_EndPoly
      TBGL_PopMatrix 
    
    END FUNCTION
    
    FUNCTION DrawGrid(width As Single, height As Single, gridStep As single)
    
      Single i
    
      TBGL_BeginPoly(%GL_LINES)    
    
        FOR i = -width/2 to width/2 step gridStep
          TBGL_Vertex( i, -height/2 )
          TBGL_Vertex( i,  height/2 )
        NEXT 
    
        FOR i = -height/2 to height/2 step gridStep
          TBGL_Vertex( -width/2, i )
          TBGL_Vertex(  width/2, i )
        NEXT 
    
      TBGL_EndPoly
    
    END FUNCTION
    
    These two routines might look as valid pieces of code, but there is one problem - the DrawPositionMarker explicitly sets drawing color to red, while the DrawGrid does not specify any color at all. That means, if we call the functions in this order:
    DrawPositionMarker(1, 1)
    DrawGrid(10, 5, 1)
    
    ... the grid will be painted red, because the first function enabled red color.

    State leaking can cause unexpected results and graphical glitches as well, take for example this reversed case:
    DrawGrid(10, 5, 1)
    DrawPositionMarker(1, 1)
    
    During the first iteration of this sequence, the color is set to default white. That means, the grid will be rendered white, and marker red. Why not. The problem is that once the marker is drawn, the default color is set to red, and in next frame even the grid will be red. This color "leaking" can be really troublesome, and the behavior is not limited just to the color, but to all the other states as well (causing us hell).

    TBGL state handling
    To prevent this group of problems, TBGL present in ThinBASIC 1.9.1.0 (and newer) introduces new safe state functionality.

    The whole concept is based on Push/Pop pairs of commands. The Push command sets some state, and remembers the previous value. Once the matching Pop command is executed, everything is returned back to previous state.

    Little illustration:
    TBGL_PushColor(255, 0, 0)    ' -- Pushing red color as active, remembering previous color
    
      tbgl_Sphere 1              ' -- Therefore this sphere will be rendered in red
    
      TBGL_PushColor(0, 255, 0)  ' -- Pushing blue color as active, remembering previous red
    
        TBGL_Box 1, 1, 1         ' -- This box will be rendered in blue
    
      TBGL_PopColor              ' -- Pop command will restore the color back to previous = red
    
      tbgl_Torus 0.5, 2          ' -- Therefore this torus will be rendered in red again
    
    TBGL_PopColor                ' -- Pop command will restore the color back to previous, whatever it was
    
    This kind of handling is not limited to color, but to all the other presets. To sum it up, the state safe code can be used for all these cases in TBGL:

    • Creating code block isolating transformations (present in OpenGL as well):
      • TBGL_PushMatrix / TBGL_PopMatrix

    • Creating code block with forced use states (TBGL specific):
      • TBGL_PushState / TBGL_PopState

    • Creating code block with disabled specified states (TBGL specific):
      • TBGL_PushStateProtect / TBGL_PopStateProtect

    • Creating code block with specific numeric state (TBGL specific, new in ThinBASIC 1.9.0.0):
      • tbgl_PushAlphaFunc / tbgl_PopAlphaFunc
      • tbgl_PushBlendFunc / tbgl_PopBlendFunc
      • tbgl_PushColor / tbgl_PopColor
      • tbgl_PushDepthFunc / tbgl_PopDepthFunc
      • tbgl_PushLineWidth / tbgl_PopLineWidth
      • tbgl_PushLineStipple / tbgl_PopLineStipple
      • tbgl_PushPointSize / tbgl_PopPointSize
      • tbgl_PushPolygonLook / tbgl_PopPolygonLook
      • tbgl_PushTexture / tbgl_PopTexture


    Conclusion
    While using direct state changes is tempting and straightforward, everybody writing code units or routines meant to be reused without state conflicts should consider taking advantage of arsenal of TBGL Push/Pop state safe commands. The syntax is the same as for direct calls, except the additional Push and enclosing Pop, and it can really save some time otherwise spent investigating where the state change occurred.
    Comments 1 Comment
    1. Oscar Ugolini's Avatar
      Oscar Ugolini -
      nice Petr!
      i'm waiting it...

      bye,
      Oscar