Bindless Graphics

Hi!

Is there a way to use the pretty cool new nvidia feature “Bindless Graphics” (http://developer.nvidia.com/object/bindless_graphics.html) with the current jogl-version? ???

If I try to look up the needed extensions and functions they seem to be available, but I can’t find them in the current GL3-Implementation, i.e. the function “glMakeBufferResidentNV” or “glGetUniformui64vNV”.

Thanks in advance!

no, those extensions aren’t available yet. Someone has to add them to jogl’s /make/stub_includes/opengl/GL3/gl3ext.h header first.

Lets assuming that I manage to add them to the header, what are the next steps?
I suppose that jogl must be generated new?

exactly. GlueGen parses this file (and others) and generates the binding. After you modified the header the next rebuild should already add the methods and fields to the GL3 profile.
If you could provide a patch I could try to bring it into the jogl2 repo if you like.

That would be great!

I´ll try it and let you know about the progress…

I managed to add the necessary extensions and functions to the header and also the compilation of jogl works.
But when i try to use one of the new binded functions, i.e. “glIsNamedBufferResidentNV” i get the following exception:

Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: com.sun.opengl.impl.gl3.GL3Impl.dispatch_glIsNamedBufferResidentNV0(IJ)Z
	at com.sun.opengl.impl.gl3.GL3Impl.dispatch_glIsNamedBufferResidentNV0(Native Method)
	at com.sun.opengl.impl.gl3.GL3Impl.glIsNamedBufferResidentNV(GL3Impl.java:3988)
	at de.firstGL3Tests.Test_001.init(Test_001.java:102)
	at com.sun.opengl.impl.GLDrawableHelper.init(GLDrawableHelper.java:88)
	at javax.media.opengl.awt.GLCanvas$InitAction.run(GLCanvas.java:603)
	at com.sun.opengl.impl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:149)
	at javax.media.opengl.awt.GLCanvas.maybeDoSingleThreadedWorkaround(GLCanvas.java:553)
	at javax.media.opengl.awt.GLCanvas.display(GLCanvas.java:290)
	at javax.media.opengl.awt.GLCanvas.paint(GLCanvas.java:368)
	at sun.awt.RepaintArea.paintComponent(RepaintArea.java:248)
	at sun.awt.RepaintArea.paint(RepaintArea.java:224)
	at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:306)
	at java.awt.Component.dispatchEventImpl(Component.java:4706)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Did i miss something to add to the header?
Each idea is welcome…

where can i find more details on gluegen? can it bind C++ libs

On a totally different note:

The VBO implementation in JOGL is check-check-double-checked in Java code, to ensure there is no invalid memory access. It’s even implemented in a HashMap<Integer,Integer>. This is done to ensure that JOGL applets do not need a security dialog.

So… the bindless extension allows you access to direct GPU points, to get rid of the conversion step in the driver, from handle to pointer. If this extension would be officially added to JOGL, its advantages would similarly be destroyed by the ‘safe’ JOGL wrapping code, which does even a slower job than the driver would have.

You’d better to use SWIG for binding Java and C++

You realize JOGL is generated by BlueGen, and switching to SWIG to add an extension is kinda… weird?

Agreed. I just skipped about jogl … sorry.

[quote]The VBO implementation in JOGL is check-check-double-checked in Java code, to ensure there is no invalid memory access. It’s even implemented in a HashMap<Integer,Integer>. This is done to ensure that JOGL applets do not need a security dialog.
[/quote]
Ok, I´ve looked at the Implementation and your right, some gl-method calls generate this overhead, i.e. glBindBuffer and glBufferData…

But let’s look at the bindless graphics example code from the nvidia presentation…

INIT (ONLY ONE TIME)

for (i = 0; i < N; ++i) {
    BindBuffer(ARRAY_BUFFER, vboNames[i]);
    BufferData(ARRAY_BUFFER, size, ptr, STATIC_DRAW);
    GetBufferParameterui64vNV(ARRAY_BUFFER, BUFFER_GPU_ADDRESS_NV, &vboAddrs[i]);
    MakeBufferResidentNV(ARRAY_BUFFER, READ_ONLY);
}

FORMAT/ENABLED CHANGE (RARE)

EnableClientState(COLOR_ARRAY);
EnableClientState(VERTEX_ARRAY);
ColorFormatNV(4, UNSIGNED_BYTE, 20);
VertexFormatNV(4, FLOAT, 20);
EnableClientState(VERTEX_ATTRIB_ARRAY_UNIFIED_NV);

BUFFER CHANGE (FREQUENT)

for (i = 0; i < N; ++i) {
    // point at buffer i
    BufferAddressRangeNV(COLOR_ARRAY_ADDRESS_NV, 0, vboAddrs[i], size);
    BufferAddressRangeNV(VERTEX_ARRAY_ADDRESS_NV, 0, vboAddrs[i]+4, size-4);
    DrawArrays(POINTS, 0, size/20);
}

The above code uses the functions “with overhead”. But they are only called on initialization of the buffer…
All other functions doesn´t have this overhead, have they? Therefore I understand your objection not completely:

[quote]So… the bindless extension allows you access to direct GPU points, to get rid of the conversion step in the driver, from handle to pointer. If this extension would be officially added to JOGL, its advantages would similarly be destroyed by the ‘safe’ JOGL wrapping code, which does even a slower job than the driver would have
[/quote]
Or do I think in the wrong direction?

BTW: When I try to build jogl from source i get some errors, while building one of the nativewindows libraries… I have found a workaround (I removed the “nativewindow-compilation-code” from the build.xml and copied the nativewindow libraries to the build folder) but nevertheless I would like to build the source without this workaround… Maybe someone knows whats going on/wrong here?

My fear is that ‘vboAddrs[i]’ will be validated:

BufferAddressRangeNV(COLOR_ARRAY_ADDRESS_NV, 0, vboAddrs[i], size);

Oh well… maybe we’ll have to fork JOGL to get rid of all those annoyances.

I can’t help you with the errors, sorry.

you don’t have to fork anything (but of course you can). Tracking gl “objects” is not only for security (webstart) i think its main purpose was to enable interoperability between the gl-swing pipeline and jogl.

There are many things which could be improved e.g using a bsd compatible int hashmap instead of the autoboxing hell. Since I believe we agree that object tracking on a plain desktop app not deployed via webstart is not needed I don’t think someone would try to prevent you to integrate it into the main jogl repo as build option (however this build won’t be signed).

well im on desktop, can we have this option? :persecutioncomplex:

sure, we need only someone who implements it :wink:

good point :persecutioncomplex:

That’s overkill. Keep the current Map<Integer,Integer> for user bindings and have a 4-element int[] for known bindings. I have a patch but I can’t submit it because one of the millions of people with the same name as me is on one of Sun’s lists of people who might be terrorists.

?

something like the box method?
http://kenai.com/projects/jogl/sources/jogl-git/content/src/jogl/classes/com/sun/opengl/impl/GLBufferStateTracker.java?rev=c623c34c4e4b667a2c1bd7351e7b416f815f52ff
the gl objects cause autoboxing problems not the targets… (if you have thousands of vbos, object ids will become large too…)

/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * 
 * You acknowledge that this software is not designed or intended for use
 * in the design, construction, operation or maintenance of any nuclear
 * facility.
 * 
 * Sun gratefully acknowledges that this software was originally authored
 * and developed by Kenneth Bradley Russell and Christopher John Kline.
 */
package com.sun.opengl.impl;

import java.util.*;
import javax.media.opengl.*;

/**
 * Tracks as closely as possible which OpenGL buffer object is bound
 * to which binding target in the current OpenGL context.
 * GLBufferStateTracker objects are allocated on a per-GLImpl basis,
 * which is basically identical to a per-OpenGL-context basis
 * (assuming correct usage of the GLImpl objects, which is checked by
 * the DebugGL). This class is used to verify that e.g. the vertex
 * buffer object extension is in use when the glVertexPointer variant
 * taking a long as argument is called. <P>
 * 
 * Note that because the enumerated value used for the binding of a
 * buffer object (e.g. GL_ARRAY_BUFFER) is different than that used to
 * query the binding using glGetIntegerv (e.g.
 * GL_ARRAY_BUFFER_BINDING), then in the face of new binding targets
 * being added to the GL (e.g. GL_TRANSFORM_FEEDBACK_BUFFER_NV) it is
 * impossible to set up a query of the buffer object currently bound
 * to a particular state. It turns out that for some uses, such as
 * finding the size of the currently bound buffer, this doesn't
 * matter, though of course without knowing the buffer object we can't
 * re-associate the queried size with the buffer object ID. <P>
 *
 * Because the namespace of buffer objects is the unsigned integers
 * with 0 reserved by the GL, and because we have to be able to return
 * both 0 and other integers as valid answers from
 * getBoundBufferObject(), we need a second query, which is to ask
 * whether we know the state of the binding for a given target. For
 * "unknown" targets such as GL_TRANSFORM_FEEDBACK_BUFFER_NV we return
 * false from this, but we also clear the valid bit and later refresh
 * the binding state if glPushClientAttrib / glPopClientAttrib are
 * called, since we don't want the complexity of tracking stacks of
 * these attributes.
 *
 */

public class GLBufferStateTracker {
  private static final boolean DEBUG = Debug.debug("GLBufferStateTracker");

  // Indices into queryTargets and bindingMap0 for known binding targets.
  private static final int BINDING_ARRAY_BUFFER = 0;
  private static final int BINDING_ELEMENT_ARRAY_BUFFER = 1;
  private static final int BINDING_PIXEL_PACK_BUFFER = 2;
  private static final int BINDING_PIXEL_UNPACK_BUFFER = 3;

  private static final Integer zero = new Integer(0);

  // Arguments for glGetInteger when querying bound buffers for known binding targets.
  private static final int[] queryTargets;
  static {
    queryTargets = new int[4];
    queryTargets[BINDING_ARRAY_BUFFER] =         GL.GL_ARRAY_BUFFER_BINDING;
    queryTargets[BINDING_ELEMENT_ARRAY_BUFFER] = GL.GL_ELEMENT_ARRAY_BUFFER_BINDING;
    queryTargets[BINDING_PIXEL_PACK_BUFFER] =    GL.GL_PIXEL_PACK_BUFFER_BINDING;
    queryTargets[BINDING_PIXEL_UNPACK_BUFFER] =  GL.GL_PIXEL_UNPACK_BUFFER_BINDING;
  }

  // Mask indicating which elements of bindingMap0 are known.
  private int bindingMask = 0;

  // Maps known binding targets to buffer objects. A zero value means either that the
  // binding is unknown or that it is known that no buffer is bound to the target:
  // the corresponding bit of bindingMask distinguishes the cases.
  private final int[] bindingMap0 = new int[4];

  // Maps unknown binding targets to buffer objects. A null value indicates
  // that the binding is unknown. A zero value indicates that it is
  // known that no buffer is bound to the target.
  private Map/*<Integer,Integer>*/ bindingMap = new HashMap/*<Integer,Integer>*/();

  private int[] bufTmp = new int[1];

  public GLBufferStateTracker() {
    // Start with known unbound targets for known keys
    setBoundBufferObject(BINDING_ARRAY_BUFFER,         0);
    setBoundBufferObject(BINDING_ELEMENT_ARRAY_BUFFER, 0);
    setBoundBufferObject(BINDING_PIXEL_PACK_BUFFER,    0);
    setBoundBufferObject(BINDING_PIXEL_UNPACK_BUFFER,  0);
  }

  public void setBoundBufferObject(int target, int buffer) {
    int off = -1;
    switch (target) {
      case GL.GL_ARRAY_BUFFER:          off = BINDING_ARRAY_BUFFER;               break;
      case GL.GL_ELEMENT_ARRAY_BUFFER:  off = BINDING_ELEMENT_ARRAY_BUFFER;       break;
      case GL.GL_PIXEL_PACK_BUFFER:     off = BINDING_PIXEL_PACK_BUFFER;          break;
      case GL.GL_PIXEL_UNPACK_BUFFER:   off = BINDING_PIXEL_UNPACK_BUFFER;        break;
      default:                          bindingMap.put(box(target), box(buffer)); return;
    }
    bindingMap0[off] = buffer;
    bindingMask |= (1<<off);
  }

  /** Note: returns an unspecified value if the binding for the
      specified target (e.g. GL_ARRAY_BUFFER) is currently unknown.
      You must use isBoundBufferObjectKnown() to see whether the
      return value is valid. */
  public int getBoundBufferObject(int target, GL caller) {
    Integer value = null;
    int off = -1;
    switch (target) {
      case GL.GL_ARRAY_BUFFER:          off = BINDING_ARRAY_BUFFER;                   break;
      case GL.GL_ELEMENT_ARRAY_BUFFER:  off = BINDING_ELEMENT_ARRAY_BUFFER;           break;
      case GL.GL_PIXEL_PACK_BUFFER:     off = BINDING_PIXEL_PACK_BUFFER;              break;
      case GL.GL_PIXEL_UNPACK_BUFFER:   off = BINDING_PIXEL_UNPACK_BUFFER;            break;
      default:                          value = (Integer)bindingMap.get(box(target)); break;
    }

    if (off < 0) {
      // If value is non-null we know the value. Otherwise we don't know how to fetch it
      // and must return 0.
      if (value != null) return value.intValue();
      return 0;
    }

    // Check whether we know the value.
    if ((bindingMask & (1<<off)) != 0) return bindingMap0[off];

    // User probably either called glPushClientAttrib / glPopClientAttrib.
    int queryTarget = queryTargets[off];
    caller.glGetIntegerv(queryTarget, bufTmp, 0);
    if (DEBUG) {
      System.err.println("GLBufferStateTracker.getBoundBufferObject(): queried bound buffer " +
                         bufTmp[0] +
                         " for query target 0x" + Integer.toHexString(queryTarget));
    }
    setBoundBufferObject(target, bufTmp[0]);
    return bufTmp[0];
  }

  /** Indicates whether the binding state for the specified target is
      currently known. Should be called after getBoundBufferObject()
      because that method may change the answer for a given target. */
  public boolean isBoundBufferObjectKnown(int target) {
    int off = -1;
    switch (target) {
      case GL.GL_ARRAY_BUFFER:          off = BINDING_ARRAY_BUFFER;         break;
      case GL.GL_ELEMENT_ARRAY_BUFFER:  off = BINDING_ELEMENT_ARRAY_BUFFER; break;
      case GL.GL_PIXEL_PACK_BUFFER:     off = BINDING_PIXEL_PACK_BUFFER;    break;
      case GL.GL_PIXEL_UNPACK_BUFFER:   off = BINDING_PIXEL_UNPACK_BUFFER;  break;
      default:                          return bindingMap.get(box(target)) != null;
    }
    return (bindingMask & (1<<off)) != 0;
  }

  /** Clears out the known/unknown state of the various buffer object
      binding states. These will be refreshed later on an as-needed
      basis. This is called by the implementations of
      glPushClientAttrib / glPopClientAttrib. Might want to call this
      from GLContext.makeCurrent() in the future to possibly increase
      the robustness of these caches in the face of external native
      code manipulating OpenGL state. */
  public void clearBufferObjectState() {
    bindingMask = 0;
    bindingMap0[BINDING_ARRAY_BUFFER] = 0;
    bindingMap0[BINDING_ELEMENT_ARRAY_BUFFER] = 0;
    bindingMap0[BINDING_PIXEL_PACK_BUFFER] = 0;
    bindingMap0[BINDING_PIXEL_UNPACK_BUFFER] = 0;
    bindingMap.clear();
  }

  // FIXME: could remove this and use Integer.valueOf() in JDK 5
  private static Integer box(int key) {
    if (key == 0) return zero;
    return new Integer(key);
  }
}

Goodbye autoboxing.