Graphic System Overlays

Andrew Markovich
August 03, 2017

Tags: rendering performance Example

Introduction

Complex graphic scenes with a large number of elements are sometimes drawn slowly. In this case, dynamic graphic elements (such as cursors, grip/snap points, selection rectangles, user interface elements, and so on) are drawn slowly too because to correctly combine graphic scenes and dynamic graphic elements, the renderer must redraw all underlying scene elements. This problem can be solved using overlay buffers where the main rendered scene and dynamic graphic elements are drawn to separate buffers. Only the buffer that contains changed geometry will be redrawn and finally combined with the unchanged buffers during screen output.

image1

Overlay buffers can be invoked by the graphic system if the vectorization module supports them. Currently overlay buffers are supported only by the WinGLES2.txv Teigha Vectorization Module.

Usage example

Since one of the most frequently used dynamic graphic elements is command trackers, this usage example will show a simple implementation of a command tracker. A command tracker is a simple custom drawable which can be used inside Teigha commands to provide additional visual information. First we’ll show the entire tracker listing, and after that we’ll describe the most important parts of the code.

Command tracker code listing

class ExRectFrame : public OdEdPointDefTracker, public OdGiDrawableImpl<>
{
  OdGsModelPtr m_pModel; // Overlay model.
  mutable OdGePoint3d m_pts[4];

  virtual OdUInt32 subSetAttributes(OdGiDrawableTraits* ) const ODRX_OVERRIDE
  {
    return kDrawableIsAnEntity;
  }
  virtual bool subWorldDraw(OdGiWorldDraw* pWd) const ODRX_OVERRIDE
  {
    return false;
  }
  virtual void subViewportDraw(OdGiViewportDraw* pVd) const ODRX_OVERRIDE
  {
    const OdGiViewport& vp = pVd->viewport();
    OdGeMatrix3d x = vp.getWorldToEyeTransform();
    OdGePoint3d p0 = x * m_pts[0];
    OdGePoint3d p2 = x * m_pts[2];
    
    m_pts[1].x = p0.x; m_pts[3].x = p2.x;
    m_pts[1].y = p2.y; m_pts[3].y = p0.y;
    m_pts[1].z = m_pts[3].z = p2.z;
    
    x = vp.getEyeToWorldTransform();
    m_pts[1].transformBy(x);
    m_pts[3].transformBy(x);

    OdGiDrawFlagsHelper _dfh(pVd->subEntityTraits(), OdGiSubEntityTraits::kDrawNoPlotstyle);
    pVd->subEntityTraits().setFillType(kOdGiFillAlways);
    pVd->subEntityTraits().setTransparency(OdCmTransparency(0.5));
    if (m_pts[3].x < m_pts[1].x)
      pVd->subEntityTraits().setColor(3);
    else
      pVd->subEntityTraits().setColor(5);
    pVd->geometry().polygon(4, m_pts);
  }
  OdGePoint3d basePoint() const
  {
    return m_pts[0];
  }
public:
  static OdEdPointTrackerPtr create(const OdGePoint3d& base)
  {
    OdEdPointTrackerPtr pRes = OdRxObjectImpl::createObject();
    static_cast(pRes.get())->m_pts[0] = base;
    return pRes;
  }
  void setValue(const OdGePoint3d& p)
  {
    m_pts[2] = p;
    if (!m_pModel.isNull())
    { // Update drawable cache.
      OdGiDrawable* pParent = NULL;
      m_pModel->onModified(this, pParent);
    }
  }
  int addDrawables(OdGsView* pView)
  {
    m_pModel = pView->device()->createModel();
    if (!m_pModel.isNull()) // Setup model overlay.
      m_pModel->setRenderType(OdGsModel::kDirect);
    pView->add(this, m_pModel);
    return 1;
  }
  void removeDrawables(OdGsView* pView)
  {
    pView->erase(this);
  }
};
*>,>

Enable overlay buffer usage for custom drawables

First, create a separate graphics system model object:

m_pModel = pView->device()->createModel();

This separate model object is required to enable the separate overlay buffer:

if (!m_pModel.isNull()) // Setup model overlay.
m_pModel->setRenderType(OdGsModel::kDirect);

The main scene typically is rendered to the “kMain” overlay buffer. Each overlay type contains its own specifics and can be used for different needs:

  • kUserBg1 - Render under the main scene without depth buffer usage. Useful to render backgrounds.
  • kUserBg2 - Render under the main scene with own depth buffer. Useful to render sprites and 3D objects under the main scene.
  • kUserBg3 - Render under the main scene with main depth buffer usage. Useful to render grids.
  • kMain - Main graphic scene overlay.
  • kSprite - Render on top of the main scene with own depth buffer. Useful to render sprites and 3D objects on top of the main scene.
  • kDirect - Render on top of the main scene directly.
  • kHighlight - Render on top of the main scene directly without depth buffer.
  • kHighlightSelection - Render on top of the main scene directly without depth buffer, but with usage of highlighting style.
  • kDirectTopmost - Render on top of the main scene without depth buffer.
  • kContrast - Render on top of main scene without depth buffer, but with usage of contrast style. Useful for UI elements.
  • kUserFg1 - Render on top of the main scene with main depth buffer usage.
  • kUserFg2 - Render on top of the main scene with own depth buffer usage.
  • kUserFg3 - Render on top of all other overlays without usage of depth buffer.

Overlay buffers are rendered in the order in which they are specified in the table, so you can choose the rendering order of custom elements using different overlays with similar properties. If the overlay doesn’t invoke a depth buffer, the element rendering order inside the overlay is urgent since the Z coordinate of geometry will not be taken into account. Overlay buffers that invoke a main depth buffer will be combined like those drawn onto the same overlay (scene depth information will be taken into account during the merge). Overlays with their own depth buffer are useful to render 3D objects which will be visually drawn on top of or under the main graphics scene. The following table shows specific overlay properties in a more convenient way:

Overlay Type

Rendering Order

Depth Buffer

Direct Rendering

Highlighting Style

Contrast Style

kUserBg1

1

-

-

-

-

kUserBg2

2

Own

-

-

-

kUserBg3

3

Main

-

-

-

kMain

4

Main

-

-

-

kSprite

5

Own

-

-

-

kDirect

6

-

+

-

-

kHighlight

7

-

+

-

-

kHighlightSelection

8

-

+

+

-

kDirectTopmost

9

-

-

-

-

kContrast

10

-

-

-

+

kUserFg1

11

Main

-

-

-

kUserFg2

12

Own

-

-

-

kUserFg3

13

-

-

-

-

Overlay buffers that are marked with “Direct Rendering” ability can be drawn by the renderer directly onto the screen if this mode is supported (skipping intermediate buffer caching).

After graphics system model creation and configuration, renderable objects (drawable) can be added into the graphics system view together with the configured model:

pView->add(this, m_pModel);

Don’t forget that the graphics cache will be created for this drawable if it is added into the view together with the graphics system model pointer. If the drawable state is modified and must be redrawn, manually inform the graphics system that the graphics cache must be updated for this drawable:

if (!m_pModel.isNull())
{ // Update drawable cache.
  OdGiDrawable* pParent = NULL;
  m_pModel->onModified(this, pParent);
}

Checking custom tracker rendering

To check custom tracker rendering, use any graphics example that supports mouse input and user command context:

bool OdExEditorObject::OnMouseLeftButtonClick(int x, int y)
{
  OdGePoint3d pt = toEyeToWorld(x, y);
  OdDbUserIO* pIO = m_pCmdCtx->dbUserIO();
  try
  {
    OdEdPointTrackerPtr pTracker = ExRectFrame::create(pt);
    OdGePoint3d pts[2] = {
      pt,
      pIO->getPoint(OD_T("Specify opposite corner:"), OdEd::kGptNoLimCheck|OdEd::kGptNoUCS, NULL, OdString::kEmpty, pTracker)
    };
  }
  catch( const OdError& )
  {
    return false;
  }
  catch( ... )
  {
    throw;
  }
  return true;
}

The following example simply invokes a database command context to get an input rectangle. The tracker draws a transparent green rectangle if the second input point is placed on the left of the first input point and draws a transparent blue rectangle otherwise.

To check the rendering speed, use a massive drawing with a large number of elements. The following pictures were generated with a file containing one million color lines. First try to invoke the tracker without the graphics system model or with the graphics system model configured for a main scene overlay buffer:

image2

The speed of the rectangle redraw is visually slow, so working with large files is difficult. Enable usage of a separate overlay:

image3

The rectangle redraw time becomes fast if overlay buffers are enabled and supported by the vectorization module. This is recommended for editor applications — moving most of the dynamically changeable graphic elements to overlay buffers — to increase application usability while editing massive drawings.

Overlay behavior with non-compatible vectorization modules

Even if a vectorization module doesn’t support overlay buffers, usage of separate graphics system models with different rendering types can influence rendering results. First, the graphics system sorts drawables in overlay order. This means that overlay models can be used to modify rendering order. For example, grip points and other elements that must be rendered on top of the main scene can be placed in the model with a render type larger than the kMain render type. Secondary vectorization modules can disable usage of depth buffer for overlays that require it. This means that drawables placed to the model with, for example, kDirect render type, will be drawn on top of main scene graphics even if the drawables are placed spatially under the main scene graphics.

Conclusion

Prior to configuring the rendering elements order, an application could create separate OdGsView objects with similar parameters, like those specified for underlying OdGsView, and draw them on top or under a duplicated OdGsView. This mechanism works well, but requires more source code than what is required for overlays. But the main advantage of overlay buffers is fast rendering of dynamically changeable geometry on top or under main graphics scenes.

This feature is useful not only for command trackers, cursors, user interface elements and grip/snap points, but also for fast highlighting (or hovering) too, and for animated scene elements. The WinGLES2.txv vectorization module invokes GPU OpenGL frame buffers to completely support the overlay buffers feature, so any video card (even if it is very old) supports it. Overall, the overlay buffers feature will be helpful for modern editing applications with creative user interfaces.

All posts