-
Notifications
You must be signed in to change notification settings - Fork 35
2.5. Troubleshooting
The below options are generally useful, regardless of the LWJGL bindings used.
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 aNullPointerException
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
inconfig/build-bindings.xml
to true.
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.
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.
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.
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.
Some bindings offer dedicated tools that can be very helpful for troubleshooting.
EGL implementations may support the KHR_debug
extension. See OpenGL below for more information.
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-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:
- Effectively disables all logging
- Prints out errors only
- Prints out warnings and errors
- Prints out additional information, as well as warnings and errors
- 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.
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.
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
.
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.
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.