Preface: This isn't a solid RFE, but I've been musing about a potential modification to the API and I wanted to know what others thought. Also, since this type of programming is one I know almost nothing about, some of my ideas may be unworkable.
Plus, warning: wall of text.
Okay, here goes.
Intro:I've been noticing a lot of improvements to the Display class in LWJGL lately (2.8.4 seems to be a sizable jump forward). In that spirit, I've been thinking about what it would take to make the Display class use an OO API. I think that this would make the current API more compact and flexible in a few ways, and could (in theory) allow for things like multiple concurrent windows, getting rid of the AWTGLCanvas class in favor of a more intuitive system, simplifying context sharing, etc.
Also, part of the reason I feel that this could be a good idea is because I've always been uncomfortable with how the vast majority of LWJGL is static. I understand why it is, and I approve of the reasons, but I think that there are some ways that it could be more sensibly compartmentalized (I've included those ideas below). This RFE would not modify how any of the OpenGL calls work, but would provide a more controllable framework to call them from.
Part 1: Display ObjectsThe first major difference would, of course, be the use of a Display object! Consider the following:
// Potential ways of instantiaion:
Display display1 = new Display();
Display display2 = new Display([String title]);
Display display3 = new Display([DisplayMode mode]);
Display display4 = new Display([DisplayMode mode], [boolean fullscreen]);
// You could then set further settings for the created display, such as:
display1.setTitle("A Game");
display1.setFullscreen(true);
//After settings have been chosen, a display can be created and run like normal.
display1.create();
while(!display1.isCloseRequested())
{
// render calls per usual
display1.update();
}
display1.destroy();
This is not anything particularly different from what is in place currently. However, there are a couple of ramifications of it:
- You can have multiple Displays created at the same time. (Potentially valuable.)
- A reference to the active display needs to be available to modify the current window.
On its own, this modification does not deliver any improvement. However, it opens the doors for more possibilities:
Part 2: Managing contextBy necessity, if we allow for multiple Display objects, we need to give them a simple way to control the OpenGL thread context. After all, you can't have a call to glTranslatef() be directed at two displays at once.
What I'm envisioning would (on the outside API, at least) be simple: allow a Display instance to declare itself as the active display in a Thread. While it is active, all GL-calls from that thread would be directed to it. An example of giving a Display its own thread:
public class Test1RenderThread extends Thread
{
private Display display;
public Test1RenderThread(Display display)
{
this.display = display;
}
public void run()
{
display.makeCurrent();
while(!display.isCloseRequested())
{
// renderin
display.update();
}
}
}
You could logically extend this by allowing for multiple Displays being updated and rendered by the same thread:
public class Test2RenderThread extends Thread
{
private Display display1;
private Display display2;
public Test2RenderThread(Display display1, Display display2)
{
this.display1 = display1
this.display2 = display2;
}
public void run()
{
boolean d1Active = true;
boolean d2Active = true;
while(d1Active || d1Active)
{
if(d1Active)
{
if(!display1.isCloseRequested())
{
// rendering to display1
display1.update();
}
else
d1Active = false;
}
if(d2Active)
{
if(!display2.isCloseRequested())
{
// rendering to display2
display2.update();
}
else
d2Active = false;
}
}
}
}
And the opposite direction works, too. We could use this API to easily allow context sharing, instead of dealing with SharedDrawable. This would be a nice simplification of the current API:
public class Test3RenderThread extends Thread
{
private Display display;
public Test3RenderThread(Display display)
{
this.display = display;
}
public void run()
{
display.makeCurrent();
while(!display.isCloseRequested())
{
// rendering
display.update();
}
}
}
public class Test3LoadingThread extends Thread
{
private Display display;
public Test3LoadingThread(Display display)
{
this.display = display;
}
public void run()
{
if(display.isMultiContextAvailable())
display.makeCurrent();
// handle loading of resources!
}
}
This last point is one I think would be
really nice. A simple, one-line way to share contexts would be incredible. In my own projects, I have a method, but it requires several lines of code to share contexts, and seems to have bugs on Windows.
Part 3: Integration with AWT and SwingSometimes, it's true, we want to integrate LWJGL with a Swing application. Whether we want high-performance graphics amidst a bunch of normal GUI data (such as visualization software), or if we want to place some game functionality into a gui control system (like for a level editor), valuable uses exist. Currently, I know of two ways to do this:
- Use Display.setParent: This method is ideal for most implementations. It automatically uses the supplied Canvas as the Display surface, and adapts for resizing, Swing repainting, etc. Another benefit is that this method can be used without any change to the rest of your code. Unfortunately, it only allows for one OpenGL component. For more than one, this method doesn't work.
- Use an AWTGLCanvas: The only option that allows you to have multiple OpenGL surfaces at once. I've used this component for work, and while it "works," it's not close to ideal. It behaves oddly and can have its rendering fall apart in certain circumstances. Additionally, using it is confusing, as its API is very different than using the normal Display.
Looking at the above points, the optimal resolution to this problem is fairly clear: figure out how to get Display.setParent to work on more than one component. By allowing each display to be a separate object, you can have each of them be set to separate Canvases, and the problem resolves itself. An example:
Display display1;
Display display2;
Canvas canvas1;
Canvas canvas2;
// Define the canvases and displays as needed.
display1.setParent(canvas1);
display2.setParent(canvas2);
// Now you can render the Displays separately, and watch the separate canvases get updated.
I feel that this change would be quite a good one overall. Right now it's too much of a pain to make a complicated GUI with LWJGL (again, I've tried at work - you can get results, but it's too annoying). Not only would it make the necessary code easier, but it would also simplify the aesthetics of the API - AWTGLCanvas seems to exist only to allow for muti-display, and this naturally condenses all functionality into a simple set of methods that cover all use cases.
I think that's everything. There may be more ways this could affect the API, but those are all I can come up with right now.
Thoughts? Does this seem like a good/bad idea to you? Any improvements you could offer?