Skip to content

2.5. Troubleshooting

Giuseppe Barbieri edited this page Oct 12, 2020 · 4 revisions

Generic

The below options are generally useful, regardless of the LWJGL bindings used.

Simple checks

LWJGL performs several checks in all bindings and internal code. These checks are focused mostly on catching common bugs that would crash the JVM process. Examples:

  • If an optional function is called (e.g. an OpenGL extension function), LWJGL checks and throws a NullPointerException if it is not available.
  • If a pointer function parameter or struct member must never be NULL, LWJGL checks and throws a NullPointerException if it is.
  • If a buffer function parameter or struct member is required to have a particular minimum size, LWJGL checks and throws an IllegalArgumentException if it is.

These checks are generally low-overhead and should not have a measurable effect on performance, so they are enabled by default and disabling them is not recommended, especially during development. For optimal performance in production builds, they can be disabled with -Dorg.lwjgl.util.NoChecks=true or Configuration.DISABLE_CHECKS.set(true).

Disabled LWJGL checks have no runtime overhead. But like Java assertions, they do increase the bytecode size of the surrounding methods. In rare cases, this may affect the JVM's inlining decisions and ultimately have an impact on performance. If such an issue is identified, LWJGL can be built with all checks removed by setting binding.DISABLE_CHECKS in config/build-bindings.xml to true.

Debug mode

LWJGL performs additional, more expensive checks, when the debug mode is enabled with -Dorg.lwjgl.util.Debug=true or Configuration.DEBUG.set(true). This should be the first option to enable when facing trouble with LWJGL. The debug mode also produces additional output (to stderr by default, can be overridden with Configuration.DEBUG_STREAM) and is required by some of the other troubleshooting tools.

When reporting an LWJGL issue, it is highly recommended to enable the debug mode and include its output with the problem description.

Shared Library Loader debug mode

One of the most common problems new LWJGL users face is setting up their project such that the shared libraries are properly loaded. Enabling this mode with -Dorg.lwjgl.util.DebugLoader=true or Configuration.DEBUG_LOADER.set(true) produces additional output to the DEBUG_STREAM that is very helpful with identifying library loading issues.

The debug mode must also be enabled.

Memory allocator debug mode

Native libraries means off-heap memory, which means an increased risk for memory leaks compared to standard Java code. Enabling this mode with -Dorg.lwjgl.util.DebugAllocator=true or Configuration.DEBUG_MEMORY_ALLOCATOR.set(true) makes LWJGL track all off-heap allocations done via org.lwjgl.system.MemoryUtil. When the JVM process ends, any allocations that were not explicitly freed will be reported on the DEBUG_STREAM.

This mode may have a very negative impact on performance.

Memory stack debug mode

Using the org.lwjgl.system.MemoryStack is simple, but may be the source of tricky to identify issues. Enabling this mode with -Dorg.lwjgl.util.DebugStack=true or Configuration.DEBUG_MEMORY_DEBUG_STACK.set(true) makes LWJGL report any stack pop without a matching push in the same method.

This mode may have a very negative impact on performance.

Bindings

Some bindings offer dedicated tools that can be very helpful for troubleshooting.

EGL

EGL implementations may support the KHR_debug extension. See OpenGL below for more information.

GLFW

GLFW supports registering a callback function for error notifications, even before GLFW itself is initialized, with the glfwSetErrorCallback function. Example code:

// custom
glfwSetErrorCallback((error, description) -> {
	System.err.println("GLFW error [" + Integer.toHexString(error) + "]: " + GLFWErrorCallback.getDescription(description));
});
// or shortcut that prints to DEBUG_STREAM
GLFWErrorCallback.createPrint().set();
// or shortcut that throws an exception on error
GLFWErrorCallback.createThrow().set();

// easy clean-up
glfwSetErrorCallback(null).free();

OpenAL

OpenAL-Soft, the OpenAL implementation bundled with LWJGL, can be configured with environment variables.

The ALSOFT_LOGLEVEL variable specifies the amount of logging OpenAL Soft will write out:

  1. Effectively disables all logging
  2. Prints out errors only
  3. Prints out warnings and errors
  4. Prints out additional information, as well as warnings and errors
  5. Same as 3, but also device and context reference count changes. This will print out a lot of info, and is generally not useful unless you're trying to track a reference leak within the library.

OpenCL

When an OpenCL context is created, a callback function may be registered that will be used by the OpenCL implementation to report information on errors during context creation as well as errors that occur at runtime in this context. Example code:

CLContextCallback contextCB = CLContextCallback.create((errinfo, private_info, cb, user_data) -> {
    System.err.println("cl_context_callback info: " + memUTF8(errinfo));
});

long context = clCreateContext(ctxProps, device, contextCB), NULL, errcode_ret);

The callback function may be called asynchronously by the OpenCL implementation. It is the application's responsibility to ensure that the callback function is thread-safe.

OpenGL

Modern versions of OpenGL support a very powerful debug mode for reporting errors and warnings. This mode became standard in OpenGL 4.3, but there are extensions that provide the same functionality in earlier versions (KHR_debug, ARB_debug_output, AMD_debug_output). Example code:

// before context creation
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);

// after context creation
glfwMakeContextCurrent(window);
GL.createCapabilities();
Callback debugProc = GLUtil.setupDebugMessageCallback(); // may return null if the debug mode is not available

// cleanup
if ( debugProc != null )
    debugProc.free();

Some OpenGL drivers produce verbose information messages and warnings by default. The amount and type of output produced can be controlled with functions such as glDebugMessageControl.

OpenGL ES

Similar to OpenGL, OpenGL ES supports the debug mode starting from OpenGL ES 3.2. The KHR_debug extension may also be available in earlier versions.

Vulkan

Even though Vulkan implementations are optimized for peak performance and perform very little checks by default, the extension and layer system can be used to enable a vast number of runtime validations, from basic checks to complex usage tracking across function calls.

As with everything in Vulkan, the code to enable such validation is not trivial. Be sure to check out the LunarG Vulkan SDK and the HelloVulkan LWJGL sample for examples.