Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compile all demos to native executable #37

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
/.project
/*.iml
/dependency-reduced-pom.xml
.gradle
.vscode
2 changes: 1 addition & 1 deletion .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
183 changes: 170 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,179 @@
# lwjgl3-demos
Demo suite for LWJGL 3
# lwjgl3-demos + GraalVM native image

## Building
Demos for LWJGL 3, compiled to native executable by GraalVM native-image utility.

./mvnw package

To override main class
All demos in [src](src/org/lwjgl/demo) are included in the build. Most demos are portable
across different OSes, but some are GPU-specific, like the [Vulkan demos](src/org/lwjgl/demo/vulkan)
which need a discrete GPU with supporting Vulkan driver, or the [CUDA demos](src/org/lwjgl/demo/cuda)
which require a NVIDIA graphics card to run.

./mvnw package -Dclass=opengl.UniformArrayDemo
Gradle and Maven build scripts are provided for building the project,
which requires JDK 11+ or GraalVM 21+ (for native image).

## Running
## GraalVM pre-requisites

java -jar target/lwjgl3-demos.jar
### Setup for GraalVM native-image utility and C compiler

on Mac OS you need to specify the `-XstartOnFirstThread` JVM argument, so the above becomes:
The [GraalVM native-image](https://www.graalvm.org/reference-manual/native-image) page
shows how to set up GraalVM and its native-image utility for common platforms.
[Gluon](https://gluonhq.com/) also provides some setup
[details](https://docs.gluonhq.com/#_platforms) for GraalVM native-image creation.

java -XstartOnFirstThread -jar target/lwjgl3-demos.jar
This project's Gradle build script uses the
[gluonfx-gradle-plugin](https://github.com/gluonhq/gluonfx-gradle-plugin)
from Gluon to build the native executable from Gradle with GraalVM.

To override main class
Gluon also provides the [gluonfx-maven-plugin](https://github.com/gluonhq/gluonfx-maven-plugin)
which is used in this project's Maven build script and works similarly to the above
gluonfx-gradle-plugin.

### Native-image configuration files

The GraalVM native-image utility will use the configuration files in
`res/META-INF/native-image` folder to assist in the native-image generation.

The configuration files were generated when running the demos in standard JVM with a
[GraalVM agent](https://www.graalvm.org/reference-manual/native-image/BuildConfiguration/#assisted-configuration-of-native-image-builds),
which tracks all usages of dynamic features of an execution of the demos
and writes the info to the configuration files.
Usage of the agent is contained in the [Gradle build script](build.gradle#L135),
or in the [Maven build script](pom.xml#L185), which can be turned on (i.e. uncommented)
and the demos are re-run to update the configuration files, if need be.

The above agent is not perfect; it sometimes misses some classes referenced via reflection
which is used extensively in LWJGL. In the case of the LWJGL demos, many `org.lwjgl.bgfx.*`
and `org.lwjgl.assimp.*` classes need be added manually to the configuration files,
to avoid ClassNotFoundException being thrown when running the [bgfx](src/org/lwjgl/demo/bgfx)
or [assimp](src/org/lwjgl/demo/opengl/assimp) demos in the generated native image.

## Gradle build tasks

### Run in standard JVM

To build and run the demos in standard JVM with Gradle, execute the `run` task:

gradlew run

By default, the [Bump](src/org/lwjgl/demo/bgfx/Bump.java) demo is executed
by the above `run` task without parameter. To run a different demo, e.g.
[WavefrontObjDemo](src/org/lwjgl/demo/opengl/assimp/WavefrontObjDemo.java), execute the `run` task
with that specific demo class as parameter:

gradlew run --args=opengl.assimp.WavefrontObjDemo

System properties can be passed on to the running demo with the -D parameter,
e.g. to print out some debug info in the console:

gradlew run --args=opengl.assimp.WavefrontObjDemo -Dorg.lwjgl.util.Debug=true

The above tasks can use any standard JDK 11+.

### Produce native executable

To generate native executable, GraalVM 21+ need be set up as mentioned in
*GraalVM pre-requisites* section above.

Once GraalVM is set up and available in the path, run the `nativeBuild` task:

gradlew nativeBuild

The `nativeBuild` task would take a while to compile all demo source code and
link them with the LWJGL libraries into an executable file.
The resulting `lwjgl3-demos` file is (in Linux):

build/gluonfx/x86_64-linux/lwjgl3-demos

(or if building on a Windows machine:

build\gluonfx\x86_64-windows\lwjgl3-demos.exe

)

which can then be run directly with a demo class as parameter
(e.g. [DepthEdgeShaderDemo20](src/org/lwjgl/demo/opengl/fbo/DepthEdgeShaderDemo20.java)):

./build/gluonfx/x86_64-linux/lwjgl3-demos opengl.fbo.DepthEdgeShaderDemo20

System properties can be passed on to the running demo with the -D parameter,
e.g. to print out some debug info in the console:

./build/gluonfx/x86_64-linux/lwjgl3-demos opengl.fbo.DepthEdgeShaderDemo20 -Dorg.lwjgl.util.Debug=true

(or if building on a Windows machine:

build\gluonfx\x86_64-windows\lwjgl3-demos.exe opengl.fbo.DepthEdgeShaderDemo20
build\gluonfx\x86_64-windows\lwjgl3-demos.exe opengl.fbo.DepthEdgeShaderDemo20 -Dorg.lwjgl.util.Debug=true

)

## Maven build tasks

### Run in standard JVM

To build and run the demos in standard JVM with Maven, execute the
`compile` then `exec:exec` tasks:

mvnw compile
mvnw exec:exec

By default, the [Bump](src/org/lwjgl/demo/bgfx/Bump.java) demo is executed
by the above `exec:exec` task without parameter. To run a different demo, e.g.
[WavefrontObjDemo](src/org/lwjgl/demo/opengl/assimp/WavefrontObjDemo.java), execute the `exec:exec` task
with that specific demo class as value of the property `class`:

mvnw exec:exec -Dclass=opengl.assimp.WavefrontObjDemo

System properties can be passed on to the running demo with the -Dsys.props parameter,
e.g. to print out some debug info in the console:

mvnw exec:exec -Dclass=opengl.assimp.WavefrontObjDemo -Dsys.props="-Dorg.lwjgl.util.Debug=true"

The above tasks can use any standard JDK 11+.

### Produce native executable

To generate native executable, GraalVM 21+ need be set up as mentioned in
*GraalVM pre-requisites* section above.

Once GraalVM is set up and available in the path, run the `gluonfx:build` task:

mvnw gluonfx:build

The `gluonfx:build` task would take a while to compile all demo source code and
link them with the LWJGL libraries into an executable file.
The resulting `lwjgl3-demos` file is (in Linux):

target/gluonfx/x86_64-linux/lwjgl3-demos

(or if building on a Windows machine:

target\gluonfx\x86_64-windows\lwjgl3-demos.exe

)

which can then be run directly with a demo class as parameter
(e.g. [Demo33Ubo](src/org/lwjgl/demo/opengl/raytracing/Demo33Ubo.java)):

./target/gluonfx/x86_64-linux/lwjgl3-demos opengl.raytracing.Demo33Ubo

System properties can be passed on to the running demo with the -D parameter,
e.g. to print out some debug info in the console:

./target/gluonfx/x86_64-linux/lwjgl3-demos opengl.raytracing.Demo33Ubo -Dorg.lwjgl.util.Debug=true

(or if building on a Windows machine:

target\gluonfx\x86_64-windows\lwjgl3-demos.exe opengl.raytracing.Demo33Ubo
target\gluonfx\x86_64-windows\lwjgl3-demos.exe opengl.raytracing.Demo33Ubo -Dorg.lwjgl.util.Debug=true

)

## Compressed native executable

The resulting `lwjgl3-demos` executable file, whether produced by Gradle or Maven build script,
can be further reduced in size via compression using the [UPX](https://upx.github.io) utility,
as described [here](https://medium.com/graalvm/compressed-graalvm-native-images-4d233766a214).

For example, the resulting `lwjgl3-demos.exe` native application file produced in Windows
is normally 88MB in size, but is compressed to 29MB with the UPX command: `upx --best lwjgl3-demos.exe`

java -cp target/lwjgl3-demos.jar org.lwjgl.demo.opengl.UniformArrayDemo
143 changes: 143 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.10' //requires JDK11+
id 'com.gluonhq.gluonfx-gradle-plugin' version '1.0.4' //requires GraalVM 21.1+
}

wrapper {
gradleVersion = '7.2'
distributionType = Wrapper.DistributionType.ALL
}

group = 'org.lwjgl'
description = 'LWJGL3 demos with native image by GraalVM'
//version = '0.0.1-SNAPSHOT'

ext {
lwjglVersion = '3.2.3'
jomlVersion = '1.10.1'
swtMavenVersion = '3.105.3' //contains Eclipse SWT 4.6.2.9 software
graalvmVersion = '21.2.0'

//LWJGL modules used: minimal OpenGL, plus bgfx, cuda, jemalloc, shaderc, vma, vulkan
lwjglModules = [
'lwjgl', 'lwjgl-assimp', 'lwjgl-glfw', 'lwjgl-openal', 'lwjgl-opengl', 'lwjgl-stb', //for OpenGL demos
'lwjgl-bgfx', //for BGFX demos
'lwjgl-cuda', //for CUDA demos (require NVIDIA hardware)
'lwjgl-jemalloc', //utilities
'lwjgl-shaderc', 'lwjgl-vma', 'lwjgl-vulkan', //for Vulkan demos (requires discrete GPU with Vulkan driver)
]

mainClassName = 'org.lwjgl.demo.DemoLauncher'
currentPlatform = getCurrentPlatform()
}

repositories {
mavenCentral()
mavenLocal()
}

//detect the OS (assuming 64-bit, on Intel/AMD hardware)
private static String getCurrentPlatform() {
def currentOS = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
if (currentOS.isWindows()) {
return 'windows'
} else if (currentOS.isLinux()) {
return 'linux'
} else if (currentOS.isMacOsX()) {
return 'macos'
}
return 'unknown'
}

dependencies {
implementation "org.joml:joml:$jomlVersion"
//for compiling GraalVM substitution classes
compileOnly "org.graalvm.nativeimage:svm:$graalvmVersion"

//OS-specific SWT library (64-bit Intel/AMD hardware only)
switch (currentPlatform) {
case 'linux':
implementation("org.eclipse.platform:org.eclipse.swt.gtk.linux.x86_64:$swtMavenVersion") {
exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt'
}
break
case 'macos':
implementation("org.eclipse.platform:org.eclipse.swt.cocoa.macosx.x86_64:$swtMavenVersion") {
exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt'
}
break
case 'windows':
implementation("org.eclipse.platform:org.eclipse.swt.win32.win32.x86_64:$swtMavenVersion") {
exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt'
}
break
default:
throw new GradleException('Unknown OS: ' + currentPlatform)
}

//get recommended dependency versions from the LWJGL BOM
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion")

//add LWJGL modules and their natives of current OS to the compile and runtime classpaths
lwjglModules.each {
implementation "org.lwjgl:$it"
if (it != 'lwjgl-cuda' && it != 'lwjgl-egl') { //cuda, egl have no native modules
//use natives for 64-bit Intel/AMD platforms only
if (it == 'lwjgl-vulkan') {
if (currentPlatform == 'macos') {
//Vulkan natives exist for Mac OSX only
runtimeOnly "org.lwjgl:$it::natives-$currentPlatform"
}
} else {
runtimeOnly "org.lwjgl:$it::natives-$currentPlatform"
}
}
}
}

sourceSets {
main {
java {
srcDirs = ['src']
}
resources {
srcDirs = ['res', "graal-cfg/$currentPlatform"]
}
}
}

application {
mainClass = project.mainClassName
applicationName = project.name //name of the resulting native executable
}

compileJava {
options.release = 11 //use JDK11+ for compiling & running
options.encoding = 'UTF-8'
}

run {
//default demo to be run is bgfx.Bump
//(the package "org.lwjgl.demo." is prepended automatically)
args 'bgfx.Bump'

//get system properties specified from the command line (for debugging, etc.)
//and pass them on to the running application's JVM
systemProperties = System.getProperties()

//use the following jvmArgs for as many different run scenarios as possible,
//and for all the code-execution paths as much as possible,
//to generate (or merge with) the GraalVM native-image configuration files
//in the graal-cfg/$currentPlatform/META-INF/native-image directory.
//This directory is read by GraalVM during the native-image build.

//jvmArgs = ["-agentlib:native-image-agent=config-merge-dir=graal-cfg/$currentPlatform/META-INF/native-image"]
}

gluonfx {
compilerArgs = [
'--initialize-at-run-time=org.lwjgl',
'--report-unsupported-elements-at-runtime',
]
}
Loading