[RFE] LWJGL 3.0

Started by spasi, November 08, 2012, 13:23:54

Previous topic - Next topic

kappa

The LWJGL3.0a natives in the release total at about 5mb (about 5 times the size of the 1mb lwjgl.jar) which is not bad.

However I recalled that Mazon used to use UPX on official releases. So had a quick go at UPX'ing the LWJGL3 natives and it pretty much shrinks them to about half the size. The four windows natives went from 1.81mb down to 951kb (plus shrinks further when compressed into a zip file to 683kb). The four linux natives went from 2.22mb down to 1.05mb (and 644kb when inside a zip file).

UPX works on LWJGL's 32/64 windows and linux natives (OS X 64 natives support isn't there yet) and can be run on the all natives from a single OS.

Although not much saving in the grand scheme of things, I'd say its a good thing to maintain for official releases as natives are often kept outside zip/jar files and it keeps the perception of the LWJGL library being small and compact (making the jar and natives total at just over 1mb for each platform arch).

spasi

Thanks kappa, support for UPX has been added. It is optionally enabled with:

Quoteant compile-native -Dorg.lwjgl.upx=<path to folder that contains the UPX executable>

The build servers have been updated and it will be done automatically from now on. I've also updated 3.0.0a with the compressed binaries. The final .zip didn't change by much, but we'll provide separate downloads for binaries, source and documentation in the near future.

Xpe

Spasi is GLFWCursorPosCallback being invoked by it self every 1 sec when there is no mouse movements?

I was updating libgdx backend and I detected this.  Is it a bug or its normal?

spasi

It shouldn't happen. On what platform(s) did you test? If you can build LWJGL locally, try the Events demo:

Quoteant demo -Dclass=org.lwjgl.demo.glfw.Events

and see if it behaves similarly.

kappa

LWJGL3a is in pretty good shape, with stb we've got pretty much all we need to write fully featured GUI applications now.

The one thing lacking is the ability to display a message box or alert dialog (like LWJGL2's Sys.alert()).

A reason you would need this is for systems where there is no OpenGL and therefore you can't create a GLFW window or display anything, in these cases it'd be useful to be able to show a simple alert message about no opengl being available or that the drivers need to be updated.

A really cool library seems to be tiny file dialogs which is designed to complement GLFW. Its really small (single C file, just a few kb's), crossplatform, 6 function calls (in fact we only need one of the calls and could remove the others, although they would be cool to keep too).

QuoteA single C file (add it to your C or C++ project) with 6 modal function calls
- message box & question box
- input box & password box
- save file dialog
- open file dialog & multiple files
- select folder dialog
- color picker.

Would make a useful addition to LWJGL3?

spasi

It would be useful, yes, I'll have a look. Do you know if it supports unicode?

kappa

Quote from: spasi on May 27, 2015, 22:07:41
It would be useful, yes, I'll have a look. Do you know if it supports unicode?
hmm, doesn't look like unicode is supported. Anyway having looked further into it, I think I might be able to implement a similar thing for Linux and Mac in pure java without JNI. Will have a go at it tonight.

kappa

Had an initial go at the above and got a basic dialog box working for Mac and Windows using just standard OS features and without any JNI. Will look at Linux implementation tonight as its a little more tricky due to there being no single dialog utility or method.

Implementation looks like this so far:

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

public class MessageBox {
	
	private static void winAlert(String title, String message) {
		// create random file name for script
		String filename = "dialog" + (int)(Math.random() * 100000);
		File file = null;
		
		try {
			// store script in temporary location
			file = File.createTempFile(filename, ".vbs");
			
			PrintWriter writer = new PrintWriter(file, "UTF-8");
			writer.println("msgbox \"" + message 
									   + "\"" + ",vbOKOnly + vbSystemModal, " 
									   + "\"" + title + "\"");
			writer.close();
			
			// run script
			try {
				Runtime runtime = Runtime.getRuntime();
				Process process = runtime.exec("wscript " + file.getAbsolutePath());
				process.waitFor();
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally {
			file.delete(); // clean up script
		}
	}
	
	private static void macAlert(String title, String message) {
		Runtime runtime = Runtime.getRuntime();
		String[] args1 = { "osascript", 
						   "-e", 
						   "tell app \"System Events\" to display dialog "
						   + "\"" + message + "\""  
						   + "with title \"" + title + "\""
						   + "buttons {\"OK\"}"
						   + "default button \"OK\""
						};
		
		try {
			Process process = runtime.exec(args1);
			process.waitFor();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	private static void linuxAlert(String title, String message) {
		// TODO
	}

	public static void alert(String title, String message) {
		String osName = System.getProperty("os.name");
		
		if (osName.startsWith("Win")) {
			winAlert(title, message);
		} else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
			macAlert(title, message);
		}
		else if (osName.startsWith("Linux") || osName.startsWith("Unix")) {
			linuxAlert(title, message);
		}
	}
	
	public static void main(String[] args) {
		alert("Dialog", "HELLO WORLD!");
		System.out.println("DONE");
	}

}

Cornix

Perhaps you should throw some kind of RuntimeException in case the dialog could not be displayed. A user might want to do some own error handling.

kappa

Finished the linux implementation too, implemented dialogs using Zenity (Gnome/GTK), Kdialog (KDE), notify-send and finally a console output. That should pretty much cover the majority of linux distro's.

Therefore the code now allows showing a message dialog across platforms, supports unicode, is a single java class (5kb compiled) without any JNI use :)

A simple dialog should be enough but if there is a need, shouldn't be too much of a stretch to also add support for native open/load/save file dialogs, question dialogs, input/password dialogs, etc in a similar fashion.

Final code:

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

public class MessageBox {
	
	private static void winAlert(String title, String message) {
		// create random file name for script
		String filename = "dialog" + (int)(Math.random() * 100000);
		File file = null;
		
		try {
			// store script in temporary location
			file = File.createTempFile(filename, ".vbs");
			
			PrintWriter writer = new PrintWriter(file, "UTF-8");
			writer.println("msgbox \"" + message 
									   + "\"" + ",vbOKOnly + vbSystemModal, " 
									   + "\"" + title + "\"");
			writer.close();
			
			// run script
			try {
				Runtime runtime = Runtime.getRuntime();
				Process process = runtime.exec("wscript " + file.getAbsolutePath());
				process.waitFor();
				return;
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally {
			file.delete(); // clean up script
		}
		
		consoleOutput(message, title);
	}
	
	private static void macAlert(String title, String message) {
		Runtime runtime = Runtime.getRuntime();
		String[] args = { "osascript", 
						  "-e", 
						  "tell app \"System Events\" to display dialog "
						  + "\"" + message + "\""  
						  + "with title \"" + title + "\""
						  + "buttons {\"OK\"}"
						  + "default button \"OK\""
						};
		
		try {
			Process process = runtime.exec(args);
			process.waitFor();
			return;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		consoleOutput(message, title);
	}
	
	private static void linuxAlert(String title, String message) {
		try {
			if (runKdialog(message, title)) return;
			if (runZenity(message, title)) return;
			if (runNotifySend(message, title)) return;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		// if all fails output to console
		consoleOutput(message, title);
	}
	
	private static boolean runZenity(String message, String title) throws InterruptedException {
		String[] args = {"zenity", "--info", "--text", message, "--title", title};
					
		try {
			Process process = Runtime.getRuntime().exec(args);
			process.waitFor();
		} catch (IOException e) {
			return false;
		}
		
		return true;
	}
	
	private static boolean runKdialog(String message, String title) throws InterruptedException {
		String[] args = {"kdialog", "--msgbox", message, "--title", title };
		
		try {
			Process process = Runtime.getRuntime().exec(args);
			process.waitFor();
		} catch (IOException e) {
			return false;
		}
		
		return true;
	}
	
	private static boolean runNotifySend(String message, String title) throws InterruptedException {
		String[] args = {"notify-send", title, message};
	
		try {
			Process process = Runtime.getRuntime().exec(args);
			process.waitFor();
		} catch (IOException e) {
			return false;
		}
		
		return true;
	}
	
	private static void consoleOutput(String message, String title) {
		System.out.println("Title: " + title);
		System.out.println("Message: " + message);
	}

	public static void alert(String title, String message) {
		String osName = System.getProperty("os.name");
		
		if (osName.startsWith("Win")) {
			winAlert(title, message);
		} else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
			macAlert(title, message);
		}
		else if (osName.startsWith("Linux") || osName.startsWith("Unix")) {
			linuxAlert(title, message);
		}
	}
	
	public static void main(String[] args) {
		alert("MsgBox", "HELLO WORLD!");
		System.out.println("DONE");
	}

}

Hooji

I am *very* excited about the upcoming Mantle support!

I'd love to get started with it asap. Any idea when an early release might be available?

Quote from: spasi on April 26, 2015, 18:12:23
Quote from: kappa on April 26, 2015, 17:59:00Are the Projects Valhalla or Panama going to be of any help to LWJGL's structs implementation? If so, might be worth waiting for them as an official solution.

Valhalla, no. Anything they do will be heap-based for sure. Super-interesting and useful if you're doing server programming, but does nothing for native interop. Panama is more promising, but we're talking Java 10 (at best). LWJGL 4 will be out when that's mainstream enough.

Quote from: kappa on April 26, 2015, 17:59:00Just curious, is the Mantle library even relevant anymore or worth implementing? given that its specific to AMD hardware and most are now throwing their weight behind Vulkan (including AMD) and will likely do what Mantle does but better.

Yes, for a few reasons:

- It won't affect the official LWJGL build. The Mantle bindings will be an optional module and you'll have to manually build LWJGL to use it. It's currently Windows-only anyway.

- Mantle is not dead. AMD will use it as a faster way to get exciting new features to customers. Like NV's CUDA. If you only care about Mantle 1.0 features, then obviously Vulkan will be better (and that's AMD's suggestion too).

- I think many people are excited about Vulkan and Mantle is a very nice way to get a glimpse of what's coming. The two APIs are very similar and you can start preparing for Vulkan right now.

spasi

LWJGL 3 now has full EGL and OpenGL ES bindings. All core versions and extensions are supported. Like in LWJGL 2, the plan for now is to not include them in the official builds. There are two reasons for this:

- GLFW currently can only work with EGL/GLES if it is statically compiled for it and you cannot compile GLFW with both EGL/GLES and desktop OpenGL support. LWJGL also statically links with GLFW, which complicates things further. This is going to change in GLFW 3.2 and you'll be able to select the API at runtime. GLFW itself will dynamically load the necessary libraries, just like LWJGL does. Not sure when this is going to be implemented, you can follow this issue if interested.

- It would make the binaries quite big. I have a plan that, when implemented, will reduce the binary sizes considerably. I cannot estimate the reduction in advance, but if it's big enough, I may consider having a single build with both EGL/GLES and desktop OpenGL bindings.

With that said, I have locally tested EGL/GLES with a custom build and it works great. If someone cannot wait for GLFW 3.2 and must use GLES now, let me know and I'll provide instructions on how to build GLFW/LWJGL.

spasi

API breaking change: The latest build (3.0.0b #11) has implemented the hybrid solution mentioned at the end of this post. Specifically, the LWJGL generator now supports mixing AutoType and MultiType modifiers in the same function. To explain this, I'll first give an example, using GL20.glVertexAttribPointer. The generator template looks like this (Kotlin code, with irrelevant stuff removed):

void(
	"VertexAttribPointer",

	GLuint.IN("index"),
	GLint.IN("size"),
	AutoType("pointer", GL_FLOAT) _ GLenum.IN("type"),
	GLboolean.IN("normalized"),
	GLsizei.IN("stride"),
	ARRAY_BUFFER _ MultiType(DATA_SHORT, DATA_INT) _ const _ void_p.IN("pointer")
)


Note the AutoType on the "type" argument and the MultiType on the "pointer" argument. This generates the following Java code:

// This is the standard method, matching the native signature
void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer)
// This is generated because of the ARRAY_BUFFER modifier on "pointer"
void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointerOffset)
// This is generated by the AutoType on "type". GL_FLOAT is passed automatically to the "type" argument of the native function.
void glVertexAttribPointer(int index, int size, boolean normalized, int stride, FloatBuffer pointer)
// The following two are generated by the MultiType on "pointer".
void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ShortBuffer pointer)
void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, IntBuffer pointer)


Before this change, the Short/Int versions were also AutoTyped, but an extra virtual "unsigned" argument was generated. This is now gone and you must specify the type explicitly. So for example you can use the ShortBuffer overload with a GL_HALF_FLOAT type, or the IntBuffer one with GL_INT_2_10_10_10_REV. Previously, you were limited to GL_SHORT/GL_UNSIGNED_SHORT and GL_INT/GL_UNSIGNED_INT respectively.

The GL_FLOAT AutoType remains, because there's no other data type you can use with floating point data. The same applies to other functions with very specific types, for example GL11.glDrawElements, only AutoType is used there.

Let me know if you have any feedback on the above changes. With the addition of EGL/GLES bindings, we're now very close to the 3.0.0 beta release and an API freeze.

princec

Hmm I foresee potential (unusual perhaps) situations of having a FloatBuffer I want to pass in which contains data which is not actually floats at all underneath but just a view on some mixed data with appropriate strides... Mostly it's the slight discrepancy in the APIs which is a little painful on the eye though. Just my 2p.

Cas :)

spasi

Indeed, the discrepancy is ugly. Two options:

a) Use the unsafe version, i.e.

FloatBuffer data = ...
nglVertexAttribPointer(index, size, type, normalized, stride, memAddress(data));


b) We stop mixing AutoType/MultiType in the same function. In this case, the FloatBuffer version will get an explicit type argument.

If we got with b, do you think we should also drop AutoType support completely? I mean even for functions that are exclusively AutoTyped, like glDrawElements.