com.io7m.jcanephora 0.61.0 Documentation
Package Information
Orientation
Overview
The jcanephora package implements a set of abstractions over OpenGL with the intention of providing an API that enforces program correctness statically. The OpenGL API is extremely error prone, but thankfully many of the problems can be alleviated with the simple application of static types. Where the Java type system is too weak to give static guarantees, dynamic checks are used to prevent silent failure.
Correctness
Most functions in OpenGL return integers (type int). The glCreateShader() function, for example, returns an int regardless of whether the function is creating a fragment shader or a vertex shader. Obviously, returning a different type depending on whether a fragment shader or a vertex shader was requested would require a dependent type system. As Java clearly does not have dependent types, the jcanephora package provides a shaderCompileVertex() function that returns a value of type JCGLVertexShaderType and a shaderCompileFragment() function that returns a value of type JCGLFragmentShaderType, eliminating at compile-time the possibility of confusing the two. The package takes a similar approach to static safety throughout.
All implementations of the jcanephora API are run through a large battery of tests to ensure consistent semantics.
Simplicity
The jcanephora package attempts to provide abstractions that are as minimal as possible. It should be possible for any experienced OpenGL programmer to use the package without having to learn new concepts or fight a complicated class hierarchy. The idea is to provide static type safety in as simple a manner as possible.
Compatibility
The jcanephora exposes interfaces that represent the OpenGL 3.3 core API.
Performance
OpenGL programs often have high performance requirements. The jcanephora API abstractions attempt to be as cheap as possible. Operations that allocate memory or perform potentially expensive operations are clearly marked. Emphasis is placed on writing code paths that generate little in the way of garbage to be collected, reducing GC pressure and improving realtime performance. Many potentially expensive queries (such as a request for the available number of texture units) are requested eagerly and cached, in order to reduce the number of OpenGL calls required at "render time". The package tracks OpenGL state locally to avoid redundant OpenGL calls.
Requirements
The Maven POM files are the canonical source for the project's dependencies:
Beyond the dependencies given in the POM files, the jcanephora package requires a a GPU supporting OpenGL 3.3.
Installation
Source compilation
The project can be compiled and installed with Maven:
$ mvn -C clean install
Maven
Regular releases are made to the Central Repository, so it's possible to use the com.io7m.jcanephora package in your projects by simply adding some dependencies to your Maven project.
The package is split into multiple sub-packages. The jcanephora-core package provides the types, interfaces, and implementations comprising the core of jcanephora. This package is required to use jcanephora.
<dependency>
  <groupId>com.io7m.jcanephora</groupId>
  <artifactId>io7m-jcanephora-core</artifactId>
  <version>0.61.0</version>
</dependency>
To use the JOGL backend for jcanephora, include the jcanephora-jogl package:
<dependency>
  <groupId>com.io7m.jcanephora</groupId>
  <artifactId>io7m-jcanephora-jogl</artifactId>
  <version>0.61.0</version>
</dependency>
All io7m.com packages use Semantic Versioning [0] , which implies that it is always safe to use version ranges with an exclusive upper bound equal to the next major version - the API of the package will not change in a backwards-incompatible manner before the next major version.
Platform Specific Issues
There are currently no known platform-specific issues.
License
All files distributed with the com.io7m.jcanephora package are placed under the following license:
Copyright © 2015 <code@io7m.com> http://io7m.com

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      
Design/Rationale
Problems with OpenGL
Overview
The jcanephora package takes the general view that OpenGL as an API is a ridiculously error-prone and poorly defined mess [1]. The package attempts to address most if not all of the problems listed in this section.
Types
The OpenGL API itself is weakly typed even for a C language API, simply exposing absolutely everything as a float, int, or untyped pointer. Entire classes of problems that should be prevented at compile time are moved to run-time, increasing development time, developer frustration, and testing burden.
Confusion
Rather than add or remove function entry points, the OpenGL designers decided to endlessly extend existing functions with yet more combinations of barely-typed parameters. Actually using the OpenGL API correctly is a challenge even for those that are familiar with the full API specification. Out of the hundreds of thousands of combinations of parameters that can be passed to any given API function, only a handful are actually valid.
Silent failure
OpenGL programs frequently fail, and when they do, they usually do so silently. Programmers are forced to call glGetError after every API call if they wish to avoid this (and actually detect the error at the correct function call).
Goals
Overview
Given the problems with OpenGL, the jcanephora package has the following goals (in no particular order):
Portability
Programs using the jcanephora APIs must run on as many different OpenGL implementations as possible. Code written using the API should be version-branch-free, and use only the features common to all available OpenGL implementations.
Type safety
Representing every object as an integer is unacceptable. Any conceptually distinct objects must be made type-incompatible, eliminating entire classes of bugs typical to OpenGL programs at compile time.
Only the correct ways to use the API are exposed. Misuse of the API is simply made impossible.
Linearity
Branching, version-specific code must be eliminated as far as is possible. Code of this type is fragile, difficult to test, and usually incorrect.
Conspicuity of errors
Silent failure of programs is unacceptable. All errors that cannot be prevented statically must be loudly and immediately signalled at runtime.
API Design
Interfaces as capabilities
The package is written with one particular concept, throughout: Types represent capabilities. If the developer has a value of a given type, then the developer can perform all of the operations associated with that type.
This is in direct contrast to the OpenGL API, where the developer always has full access to all API functions, and individual functions (or combinations of parameters to functions) may not work based on what the implementation supports. The emphasis is on isolating the correct operations and statically preventing incorrect operations from occurring.
Types
The jcanephora package wraps all conceptually distinct objects in new types and provides sane and unsurprising functions to construct them. This prevents large classes of bugs from occurring, at compile time. It also allows the interfaces to be self-documenting. Consider the following C function prototype:
void _ (GLsizei, GLuint *)
What does the function do? It could generate a set of textures (glGenTextures), or it could generate a set of new buffer names (glGenBuffers). Without carefully reading the documentation, the required parameters to the function are certainly not obvious from the type alone. Assuming the developer has read the documentation and worked out that the correct way to call the function is with the following sequence:
GLuint texture;

glGenTextures(1, &texture);
checkGLErrors();
The developer now has a value of type GLuint. Given that many functions in the OpenGL API also take values of GLuint, and those values have nothing to do with textures, that's a huge number of potential bugs that cannot be prevented by the compiler.
The developer now wants to allocate a new RGBA texture. The types of the glTexImage2D and glTexParameteri functions are, quite frankly, insane. Almost no developer manages to use these functions successfully without trial and error and, naturally, all errors are runtime errors. Eventually, through some painful experimentation, the developer might stumble onto the following sequence of calls:
GLuint texture;
void *data = makeTextureData();

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
The types of the parameters to all of the functions mentioned allow the developer to pass in practically anything. Only a very small subset of the possible parameters are actually valid. An even smaller subset of the possible parameters actually work across different versions of OpenGL!
Now consider the following jcanephora function:
public JCGLTexture2DType _(
    JCGLTextureUnitType _,
    long _,
    long _,
    JCGLTextureFormat _,
    JCGLTextureWrapS _,
    JCGLTextureWrapT _,
    JCGLTextureFilterMinification _,
    JCGLTextureFilterMagnification _)
    throws JCGLException;
Even without the names of the function and the parameters, the type above make it quite clear that the function returns a JCGLTexture2DType, which is somehow derived from the arguments to the function. It's a reasonable assumption to make that the function allocates a new texture.
The wrapping modes on the S and T axis will presumably be derived from the parameters of type JCGLTextureWrapS and JCGLTextureWrapT respectively. The minification and magnification filters will presumably be derived from the parameters of type JCGLTextureFilterMinification and JCGLTextureFilterMagnification, respectively. The programmer is statically prevented from mixing up the two axes, or the two filter domains. The JCGLTextureFormat parameter is obviously intended to specify the format of the texture. Any parameter familiar with OpenGL texturing will be aware that any operations involving textures requires access to a texture unit, and the JCGLTextureUnitType parameter above presumably specifies the unit that will be used.
The two parameters of type long presumably correspond to the width and height of the resulting texture, but the types are not descriptive enough to say for sure. Unfortunately, Java lacks a way to make existing arithmetic types incompatible without sacrificing efficiency [2]. In this case, the two parameters do indeed correspond to the width and height, in that order, and the jcanephora package uses this order consistently throughout.
The extremely descriptive type given for the function, and the presence of declared (but unchecked) exceptions are direct contributions towards the goals of type safety and conspicuity of errors. It is very difficult for the programmer to use this function incorrectly: the type system guarantees correct usage.
jcanephora Core Interfaces
Overview
The jcanephora package is separated into a number of "core" interfaces, through which all higher-level abstractions/interfaces work.
JCGLContextType
Description
The JCGLImplementationType interface represents an OpenGL implementation. Specific implementations, such as the JOGL implementation, provide subtypes of this interface that allow for the creation of Contexts. It is not a goal of the package to wrap all of the complicated context creation functionality exposed by packages such as JOGL - users are expected to initialize and configure OpenGL contexts and then pass those contexts to jcanephora for use.
Usage Example
An example that creates a pair of contexts using the JOGL implementation:
GLContext gc_main;
GLContext gc_background;

JCGLImplementationJOGLType i = JCGLImplementationJOGL.getInstance();
JCGLContextType c_main = i.newContextFrom(gc_main, "main");
JCGLContextType c_back = i.newContextFrom(gc_background, "background-resources");

      
JCGLContextType
Description
The JCGLContextType interface represents a single OpenGL context.
Currency
To accept commands, an OpenGL context must be made current. For a given thread t, at most one context can be current on t at any given time.
Sharing
An OpenGL context may be shared. Briefly, this means that if a context c is shared with a context d, then certain types of objects allocated on c are visible to d, and vice versa. This is often used in rendering engines to enable textures and other large resources to be loaded on a separate background thread without interrupting rendering. The jcanephora API knows types of objects may be shared across contexts, and raises errors at run-time if the user attempts to use a non-shared object on the wrong context.
JCGLArrayBuffersType
Description
The JCGLArrayBuffersType interface provides functions for allocating, updating, and using array buffers. It provides a typed interface to the glGenBuffers and glBufferData functions.
Array buffers are the basic storage type for vertex data used during rendering. The jcanephora API uses the same model as the OpenGL 3.3 API: Buffers must be bound before use, and at most one buffer of each type can be bound at any one time. The API currently exposes the following buffer types:
Usage examples
To allocate a 128 byte array buffer, indicating to OpenGL that the contents of the buffer will be specified once and then used repeatedly for rendering:
JCGLArrayBuffersType g;

JCGLArrayBufferType a = g.arrayBufferAllocate(128, JCGLUsageHint.USAGE_STATIC_DRAW);
The buffer will remain bound after allocation. To update the contents of the buffer, an instance of JCGLBufferUpdateType must be created:
JCGLArrayBuffersType g;
JCGLArrayBufferType a;

final JCGLBufferUpdateType<JCGLArrayBufferType> u =
  JCGLBufferUpdates.newUpdateReplacingAll(this.array_buffer);
final ByteBuffer b = u.getData();

for (int index = 0; index < 128; ++index) {
  b.put(index, (byte) index);
}

g.arrayBufferUpdate(u);
g.arrayBufferUnbind();
State
OpenGL specifies that at most one array buffer can be bound on a context at any given time. Many API functions operate on the currently bound buffer and the jcanephora API avoids changing the current binding behind the programmer's back.
JCGLArrayObjectsType
Description
The JCGLArrayObjectsType interface provides functions for allocating, updating, and using array objects.
Usage Examples
An array object is a container for an index buffer binding and a set of numbered vertex array attributes. Essentially, the programmer can state that numbered vertex array attributes will be supplied with data from assigned array buffers, with each attribute knowing how to extract values from the otherwise untyped buffer. An array attribute will read count values of type t from an array buffer, starting at offset o in bytes, and stepping forward stride bytes for each vertex. This more or less encodes the notion that an array buffer is an array of record [3] values, with each array attribute reading from a specific record field for each element.
As a simple example, consider an array containing three vertices, with each vertex containing a single three-element floating point vector:
JCGLArrayBuffersType g_ab;
JCGLArrayBufferType ab;

final int vertex_size = 3 * 4;
ab = g_ab.arrayBufferAllocate(vertex_size * 3L, JCGLUsageHint.USAGE_STATIC_DRAW);

final JCGLBufferUpdateType<JCGLArrayBufferType> u = JCGLBufferUpdates.newUpdateReplacingAll(ab);
final FloatBuffer d = u.getData().asFloatBuffer();

d.put(0, -0.5f);
d.put(1, 0.5f);
d.put(2, -0.5f);

d.put(3, -0.5f);
d.put(4, -0.5f);
d.put(5, -0.5f);

d.put(6, 0.5f);
d.put(7, -0.5f);
d.put(8, -0.5f);

g_ab.arrayBufferUpdate(u);
g_ab.arrayBufferUnbind();
Then, assuming that vertex positions should be supplied to a shading language program via vertex attribute 0:
JCGLArrayObjectsType aob;

final JCGLArrayObjectBuilderType aob = g_ao.arrayObjectNewBuilder();
aob.setAttributeFloatingPoint(
  0,
  ab,
  3,
  JCGLScalarType.TYPE_FLOAT,
  vertex_size,
  0L,
  false);

JCGLArrayObjectType ao = g_ao.arrayObjectAllocate(aob);
State
OpenGL specifies that exactly one array object will be bound on a context at any given time. OpenGL provides a default array object that is bound when no user-created array object exists. Many API functions operate on the currently bound array object and the jcanephora API avoids changing the current binding behind the programmer's back.
JCGLBlendingType
Description
The JCGLBlendingType interface provides functions for configuring framebuffer color blending.
Usage examples
To enable blending, using the fairly typical color = (destination * source_alpha) + (source * (1.0 - source_alpha)) blending equation:
JCGLBlendingType g;

g.blendingEnable(
  BlendFunction.BLEND_SOURCE_ALPHA,
  BlendFunction.BLEND_ONE_MINUS_SOURCE_ALPHA);
State
The current blending state is global to a given context; when enabled, it is enabled for all subsequent rendering operations on that context until explicitly modified or disabled.
JCGLClearType
Description
The JCGLClearType interface provides functions for clearing the contents of framebuffers.
Usage example
The following code creates a clearing specification that will clear the current framebuffer to a dark grey color, and the current depth buffer to 1.0:
JCGLClearType g;

final JCGLClearSpecification.Builder cb = JCGLClearSpecification.builder();
cb.setColorBufferClear(new VectorI4F(0.1f, 0.1f, 0.1f, 1.0f));
cb.setDepthBufferClear(1.0f);
JCGLClearSpecification c = cb.build();

g.clear(c);
The JCGLClearSpecification value can be re-used indefinitely. The API also provides optional strict checking which can, for example, raise an exception if the programmer attempts to clear the depth or stencil buffer without the current framebuffer actually having one [4].
JCGLColorBufferMaskingType
Description
The JCGLColorBufferMaskingType interface provides functions for masking writes to specific color channels of the framebuffer.
Usage example
The following code prevents subsequent rendering operations from rendering to the red channel of any framebuffer:
JCGLColorBufferMaskingType g;

g.colorBufferMask(false, true, true, true);
State
The current masking state is global to a given context; when enabled, it is enabled for all subsequent rendering operations on that context until explicitly modified or disabled.
JCGLDepthBuffersType
Description
The JCGLDepthBuffersType interface provides functions for controlling depth testing, writing, and clamping of depth values.
Usage examples
To clear the current depth buffer to a value typical of the far clipping plane:
JCGLDepthBuffersType g;

g.depthBufferClear(1.0f);
To discard all writes to the depth buffer (note that this will also prevent clearing of the depth buffer):
JCGLDepthBuffersType g;

g.depthBufferWriteDisable();
To enable depth testing and set the depth function such that only depth values less than the current value in the depth buffer will be written:
JCGLDepthBuffersType g;

g.depthBufferTestEnable(DepthFunction.DEPTH_LESS_THAN);
Most of the functions raise exceptions if attempting to perform operations on a framebuffer that does not have a depth buffer.
State
The current depth-testing and depth-writing state is global to a given context; when enabled, it is enabled for all subsequent rendering operations on that context until explicitly modified or disabled.
JCGLDrawType
Description
The JCGLDrawType interface provides functions for initiating rendering operations.
Usage examples
Assuming that an array object is currently bound and has an attached index buffer, drawing an object is trivial:
JCGLDrawType g;

g.drawElements(Primitives.PRIMITIVE_TRIANGLES);
Similar functions exist for rendering without index buffers.
JCGLFramebuffersType
Description
The JCGLFramebuffersType interface provides functions for creating and controlling framebuffers.
Usage examples
A framebuffer in OpenGL is an opaque structure with one or more attachments. Currently, attachments can only be textures. Textures are useful when the result of rendering to a framebuffer is going to be read back at some point, or perhaps used to texture other objects. For example, some 3D engines implement so-called "procedural textures" by rendering a generated pattern to a texture and then applying that texture to objects in the scene. Attachments are attached at specific points to any given framebuffer: Framebuffers can have a depth attachment, a stencil attachment, a depth+stencil attachment, and an implementation-defined maximum number of color attachments.
When rendering to a framebuffer, a shading program will write values to one or moredraw buffers, and each draw buffer is mapped to a color attachments. Multiple draw buffers cannot be mapped to the same color attachment.
In jcanephora, framebuffers are created via mutable builders which are then passed to the API functions, resulting in immutable framebuffers [5].
JCGLFramebuffersType g;
JCGLFramebufferBuilderType b = g.framebufferNewBuilder();
To attach allocated textures/buffers to a framebuffer, the API requires the programmer to first obtain a list of the available attachment points:
JCGLFramebuffersType g;

List<JCGLFramebufferColorAttachmentPointType> points = g.framebufferGetColorAttachmentPoints();
Similarly, the API requires the programmer to obtain a list of the available draw buffers:
JCGLFramebuffersType g;

List<JCGLFramebufferDrawBufferType> buffers = g.framebufferGetDrawBuffers();
The programmer must pass values of type JCGLFramebufferColorAttachmentPointType to various attachment functions and is therefore statically prevented from trying to use more color attachments than the OpenGL implementation allows.
After allocating a color and depth/stencil texture, the textures must be attached at the relevant attachment points and draw buffers mapped to the color attachments.
JCGLFramebuffersType g;
JCGLTexturesType gt;
JCGLTextureUnitType u;

List<FramebufferColorAttachmentPointType> points = g.framebufferGetColorAttachmentPoints();
List<JCGLFramebufferDrawBufferType> buffers = g.framebufferGetDrawBuffers();

JCGLTexture2DType d = gt.texture2DAllocate(u, 128, 128, JCGLTextureFormat.TEXTURE_FORMAT_DEPTH_16_2BPP, ...);
JCGLTexture2DType c = gt.texture2DAllocate(u, 128, 128, JCGLTextureFormat.TEXTURE_FORMAT_RGBA_8_4BPP, ...);
gt.textureUnitUnbind(u);

JCGLFramebufferBuilderType b = g.framebufferNewBuilder();
b.attachColorTexture2DAt(points.get(0), buffers.get(0), c);
b.attachDepthTexture2D(d);
All unspecified draw buffers are mapped to GL_NONE (meaning that any data written to the other draw buffers will go nowhere). The builders also raise exceptions if the programmer attempts to, for example, specify a non-color-renderable format for a color attachment.
To actually allocate a framebuffer, the mutable builder is passed to an allocation function:
JCGLFramebuffersType g;
JCGLFramebufferBuilderType b;

FramebufferType f = g.framebufferAllocate(b);
The function validates the framebuffer configuration upon allocation, raising an exception if the framebuffer is not usable on the current OpenGL implementation.
To render to the framebuffer, the framebuffer must be bound:
JCGLFramebuffersType g;
JCGLFramebufferType f;

g.framebufferDrawBind(f);
{
  // Perform rendering.
}
g.framebufferDrawUnbind(f);
Feedback
The OpenGL specification explicitly states that feedback is illegal and results in undefined behaviour. Feedback is defined as occurring when a texture t is both attached to the current draw framebuffer and is also bound to a texture unit and being sampled. This would result in a feedback loop where data written to the framebuffer would be written to a texture currently being sampled.
The jcanephora attempts to detect and prevent feedback by raising exceptions:
  1. When a texture is bound, the current framebuffer (if any) is checked to see if the texture is attached to the framebuffer.
  2. When a draw framebuffer is bound, a check is made to see if any of the attached textures are currently bound.
State
OpenGL specifies that at most one framebuffer can be bound on a context at any given time. Many API functions operate on the currently bound framebuffer and the jcanephora API avoids changing the current binding behind the programmer's back.
JCGLIndexBuffersType
Description
The JCGLIndexBuffersType interface provides functions for allocating, updating, and using index buffers. It provides a typed interface to the glGenBuffers and glBufferData functions.
Index buffers are used to store indices for indexed rendering as provided by the OpenGL glDrawElements family of functions.
Usage examples
Usage of index buffers is similar to array buffers with the main difference being that index buffers know the type of their indices.
JCGLIndexBuffersType g;

JCGLIndexBufferType a = g.indexBufferAllocate(3, JCGLUnsignedType.TYPE_UNSIGNED_INT, JCGLUsageHint.USAGE_STATIC_DRAW);

final JCGLBufferUpdateType<JCGLIndexBufferType> u =
  JCGLBufferUpdates.newUpdateReplacingAll(this.index_buffer);
final IntBuffer i = u.getData().asIntBuffer();

i.put(0, 0);
i.put(1, 1);
i.put(2, 2);

g.indexBufferUpdate(u);
g.indexBufferUnbind();
State
OpenGL specifies that at most one index buffer can be bound to the current array object at any given time. OpenGL provides a default array object that is used when attempting to bind an index buffer, if no user-created array object is bound.
JCGLPolygonModesType
Description
The JCGLPolygonModesType interface provides functions for controlling the rendering style of individual polygons.
Usage example
The following code sets a wireframe rendering style:
JCGLPolygonModesType g;

g.polygonSetMode(JCGLPolygonMode.POLYGON_LINES);
State
The current polygon rendering style state is global to a given context; when enabled, it is enabled for all subsequent rendering operations on that context until explicitly modified or disabled.
JCGLScissorType
Description
The JCGLScissorType interface provides functions for controlling the OpenGL scissor test.
JCGLShadersType
Description
The JCGLShadersType interface provides functions for compiling and using shading language programs.
Usage examples
To compile vertex, geometry, and fragment shaders, simply pass in lists of strings, with each string terminated with CRLF or LF characters:
JCGLShadersType g;

List<String> v_source = new ArrayList<String>();
v_source.add("#version 330\n");
v_source.add("in vec4 position;\n");
v_source.add("void\n");
v_source.add("main (void)\n");
v_source.add("{\n");
v_source.add("  gl_Position = position;\n");
v_source.add("}\n");
JCGLVertexShaderType vs = g.shaderCompileVertex("example-v", v_source);

List<String> g_source = new ArrayList<String>();
g_source.add("#version 330 core\n");
g_source.add("layout(points) in;\n");
g_source.add("layout(points, max_vertices = 1) out;\n");
g_source.add("void\n");
g_source.add("main ()\n");
g_source.add("{\n");
g_source.add("  gl_Position = gl_in[0].gl_Position;\n");
g_source.add("  EmitVertex();\n");
g_source.add("  EndPrimitive();\n");
g_source.add("}\n");
JCGLGeometryShaderType gs = g.shaderCompileGeometry("example-g", g_source);

List<String> f_source = new ArrayList<String>();
f_source.add("#version 330\n");
f_source.add("uniform vec4 color;\n");
f_source.add("out vec4 color0;\n");
f_source.add("void\n");
f_source.add("main (void)\n");
f_source.add("{\n");
f_source.add("  color0 = color;\n");
f_source.add("}\n");
JCGLFragmentShaderType fs = g.shaderCompileFragment("example-f", f_source);
To combine (attach and link) the shaders into a working program:
JCGLShadersType g;

JCGLProgramShaderType p = g.shaderLinkProgram("program", vs, Optional.of(gs), fs);
The program functions interrogate the compiled program when it is created and produce a set of mappings from names to active uniforms (parameters). A read-only view of these can be accessed via the program reference, and assigned values via the provided functions:
JCGLShadersType g;

Map<String, JCGLProgramUniformType> uniforms = p.getUniforms();

JCGLProgramUniformType p = uniforms.get("color");
assert p != null;
g.programActivate(p);
p.programUniformPutVector4f(p, new VectorI4F(1.0f, 0.0f, 0.0f, 1.0f));
State
OpenGL specifies that at most one shading program can be active on a context at any given time. Many API functions operate on the currently active program and the jcanephora API avoids changing the current binding behind the programmer's back.
JCGLStencilBuffersType
Description
The JCGLStencilBuffersType interface provides functions for controlling the stencil buffer.
Usage examples
To clear the current stencil buffer:
JCGLStencilBuffersType g;

g.stencilBufferClear(0);
See the referenced JavaDoc for the full range of functions. Most of the functions will raise exceptions if the current framebuffer configuration does not include a stencil buffer; this is considered to be behaviour preferable to silently producing incorrect results.
JCGLTexturesType
Description
The JCGLTexturesType interface provides functions for allocating, updating, and using textures.
Usage examples
The JCGLTexturesType interfaces expose functions that will allocate textures of formats supported by the current implementation. The interfaces only expose textures that are required to be supported by OpenGL 3.3 core.
In order to use a texture in OpenGL, the texture must be bound to a texture unit. There are a limited number of texture units available [6], and so the API requires the user to obtain access to a list of the current units:
JCGLTexturesType g;
List<JCGLTextureUnitType> u = g.textureGetUnits();
A texture unit may have at most one texture bound to it at any one time. This is in slight contrast to the way that the OpenGL API normally works: The OpenGL API specifies that a given texture unit may have multiple textures bound to a given texture unit as long as all of the textures are of a different type (two-dimensional, cube map, etc). However, it then states that it is not legal to pass that texture unit to multiple different uniform parameters in a shading program. There is, therefore, very little practical utility to binding multiple textures to a single texture unit, and exposing such behaviour in the API complicates tracking of state and makes programs slightly more difficult to reason about. The jcanephora API therefore imposes a simple restriction: One texture per texture unit.
To allocate a 256 * 256 RGBA texture, 8 bits per channel (four bytes per pixel), with nearest-neighbour filtering and repeat wrapping around both axes:
JCGLTexturesType g;
JCGLTextureUnitType u;

JCGLTexture2DType t =
  g.texture2DAllocate(
    u,
    256,
    256,
    JCGLTextureFormat.TEXTURE_FORMAT_RGBA_8_4BPP,
    JCGLTextureWrapS.TEXTURE_WRAP_REPEAT,
    JCGLTextureWrapT.TEXTURE_WRAP_REPEAT,
    JCGLTextureFilterMinification.TEXTURE_FILTER_NEAREST,
    JCGLTextureFilterMagnification.TEXTURE_FILTER_NEAREST);
Once allocated, the texture will remain bound to the texture unit u until explicitly unbound.
The jcanephora exposes both two-dimensional and cube map textures. See the relevant superinterfaces.
Cube map coordinate systems
Note that in the OpenGL specification, cube textures arbitrarily use a left-handed coordinate system as opposed to OpenGL's right-handed system. This is a frequent source of errors in programs. See the following fragment shader:
in vec3 f_normal;   // Eye-space surface normal
in vec3 f_position; // Eye-space surface position

uniform mat4        m_view_inverse; // Inverse view matrix, eye-space -> world-space.
uniform samplerCube t_reflection;

out vec4 frag_color;

void
main (void)
{
  vec3 n = normalize (f_normal);
  vec3 v = normalize (f_position);
  vec3 r = reflect (v, n);
  vec3 u = m_view_inverse * r;

  frag_color = texture (t_reflection, u);
}
The program calculates a reflection vector r from the eye-space incident vector f_position and eye-space surface normal f_normal. It transforms r back into world-space, giving u, and then uses u to index into the cube texture. This would be textbook-correct, if OpenGL didn't arbitrarily use a left-handed coordinate system for cube textures! The coordinates u are obviously specified in a right-handed coordinate system. The practical results of this are that all of the cube texture faces will be displayed upside down, and the familiar concept of the negative Z axis meaning "forwards" will be reversed.
The jcanephora package contains support for using right-handed cube maps in OpenGL, consistent with the right-handed coordinate system used in most OpenGL programs. In order to use right-handed cube maps, the following steps are required:
  1. Cube map textures are supplied to the OpenGL implementation using the "RH" variants of the texture loading and update functions. Faces are specified in a manner consistent with OpenGL's coordinate system. Informally, the "forward" face of the cube map is the negative Z face, the "right" face of the cube map is the positive X face, and so on.
  2. In shaders, the coordinates passed to the texture function (or textureCube function, on old OpenGL implementations) should be multiplied with a matrix that effectively negates the Y and Z components.
The earlier example shader becomes:
in vec3 f_normal;   // Eye-space surface normal
in vec3 f_position; // Eye-space surface position

uniform mat4        m_view_inverse; // Inverse view matrix, eye-space -> world-space.
uniform samplerCube t_reflection;

out vec4 frag_color;

void
main (void)
{
  const mat3 m_t = mat3 (
    1.0,  0.0,  0.0,
    0.0, -1.0,  0.0,
    0.0,  0.0, -1.0
  );

  vec3 n = normalize (f_normal);
  vec3 v = normalize (f_position);
  vec3 r = reflect (v, n);
  vec3 u = m_view_inverse * r;
  vec3 w = m_t * u;

  frag_color = texture (t_reflection, w);
}
This approach means that both the loading and generation of cube textures is consistent with that of 2D textures and the rest of OpenGL, and the complexity of addressing the textures is limited to one function call in shaders. Renderers that produce cube maps do not need to use unusual projection matrices to "flip" the coordinate system when rendering, and everything works according to the principles of least astonishment!
The correct solution to the problem would have been for the cube map coordinate system to have been in a consistent right-handed coordinate space to begin with.
JCGLTimersType
Description
The JCGLTimersType interface provides functions for allocating and evaluating timer queries.
Usage examples
The JCGLTimersType interfaces expose functions that allocate and evaluate timer queries. A timer query is, at the most basic level, a way of having the GPU record the current time in a manner that can be retrieved by the CPU later on.
To allocate a new timer query:
JCGLTimersType g;
JCGLTimerType t = g.timerQueryAllocate();
A timer query must be asynchronously started and stopped, and can then later be queried. For example, to measure the time it takes to execute a series of OpenGL commands:
JCGLDrawType gd;
JCGLTimersType g;
JCGLTimerType t;

g.timerQueryBegin(t);
gd.drawElements(JCGLPrimitives.PRIMITIVE_TRIANGLES);
gd.drawElements(JCGLPrimitives.PRIMITIVE_TRIANGLES);
gd.drawElements(JCGLPrimitives.PRIMITIVE_TRIANGLES);
g.timerQueryFinish(t);
The timerQueryBegin/timerQueryFinish commands execute asynchronously, recording the current time on the GPU as the GPU reaches each command in the queue. The application can then ask the GPU if the timer commands have finished executing, and return the recorded time values if they have:
JCGLDrawType gd;
JCGLTimersType g;
JCGLTimerType t;

if (g.timerQueryResultIsReady(t)) {
  long elapsed = g.timerQueryResultGet(t);
  // The time to execute the three draw commands is time_end - time_start nanoseconds
}
At most one timer query can be executing at any one time; calls to timerQueryBegin/timerQueryFinish cannot be nested.
Calling timerQueryResultGet implies synchronization between the CPU and GPU and should therefore be called after all other rendering operations for the frame have completed. As timer queries are most often used to implement OpenGL profiling, this is not usually an onerous restriction. Generally, applications will allocate many timers for their rendering pipelines, update timers during rendering of a frame, and then query all timers at the end of the frame to measure the time taken by each part of the pipeline.
JCGLViewportsType
Description
The JCGLViewportsType interface provides functions for controlling the current viewport.
Usage example
The following code configures a 640x480 viewport:
JCGLViewportsType g;

AreaInclusiveUnsignedLType area =
  AreaInclusiveUnsignedL.of(
    new UnsignedRangeInclusiveL(0, 639),
    new UnsignedRangeInclusiveL(0, 479)
  );

g.viewportSet(area);
State
The current viewport state is global to a given context; when modified, it is modified for all subsequent rendering operations on that context until modified again.
API Reference
Javadoc
API documentation for the package is provided via the included Javadoc.

[1]
Whilst, annoyingly, being the only viable cross-platform and vendor neutral graphics API available.
[2]
New types will be new classes, resulting in all integers being replaced with pointers to integers.
[3]
Or struct, in C terminology.
[4]
This is usually the result of the programmer forgetting to attach a depth/stencil attachment on framebuffer creation, and can lead to difficult-to-trace bugs.
[5]
That is, the framebuffer attachments cannot be reconfigured once the framebuffer is created. This matches the typical use patterns with regards to framebuffers and is also a design choice taken from practical experience: On most OpenGL implementations, reconfiguring a framebuffer is a rather expensive operation.
[6]
OpenGL 3.3 guarantees that at least 16 will be available in any implementation.