Closed AquilaAbriel closed 1 year ago
Multiple windows definitely do at least work with a separate DirectContext.makeGL()
for each, as I have apps using that working (both with LWJGL or JWM for the window management). Can't comment on much else though, as I don't really know anything about the subject matter.
Of note is that JWM's included Skija wrapper (JWM and Skija being both under HumbleUI) also makes a new DirectContext
for each surface, and ends up creating a new one any time it's resized.
Thank you very much, for sharing your experience and for pointing me the JWM repository. It's good to know, that multiple windows do indeed work.
I have to say, I'm a bit surprised. When I read about OpenGL context objects I was under the impression, those were intended to be used very selectively and as long as the application is running.
But I will definitely give it a try and see what happens if I use multiple contexts.
And it just works... great. As soon as I changed every window to having its own context, it just started working as intended. I have no Idea why, or if this solution might cause any long term issues but it works. Thank you very much and have good night. [Solved]
I have to say, I'm a bit surprised. When I read about OpenGL context objects I was under the impression, those were intended to be used very selectively and as long as the application is running.
I imagine that's more a "don't create one (or, worse, dozens) per frame without good reason" than "don't make more than one ever".
this wiki page says
Each context can represent a separate viewable surface, like a window in an application.
so it looks like a separate one per surface/window is the intention.
I'm sorry to have to reopen this issue, but it's still not working correctly. Having multiple Contexts just made the issue more complicated.
As long as I only draw primitive objects (squares, circles, lines etc.) everything works fine (on all windows). But as soon as I draw a path, or a texture, everything falls apart. (For some reason, drawing texts doesn't cause any issues...)
Scenario 1: Only one window -All draw calls work as intended.
Scenario 2: Two windows -Only drawing primitive objects works fine on both windows. -Drawing a texture to the primary window shows up black and the secondary window turns black. -Drawing a texture to the secondary window shows up correctly and the primary window continues working fine. -Drawing a path to the primary window doesn't show up and the secondary window turns black. -Drawing a path to the secondary window shows up correctly and the primary window truns black...
I can somewhat mitigate these issues by calling resetGLAll()() on the window's context, when I switch from one window to the other.
Scenario 3: Two windows + reset() context in between windows switching -Only drawing primitive objects works fine on both windows. -Drawing a texture to the primary window shows up black, but the secondary window contiues working fine! -Drawing a texture to the secondary window shows up correctly and the primary window continues working fine. (No change) -Drawing a path to the primary window doesn't show up, but the secondary window contiues working fine! -Drawing a path to the secondary window shows up correctly and the primary window continues working fine! -Drawing a path to both windows only shows up on the secondary window...
It's a mess honestly... And I have no idea, what is going wrong. (As faw as I can tell, GL is not reporting any errors) But since resetting the contexts does change some of the results, I suspect having multiple contexts is the root cause of the issue.
So I'm going back at my initial question: How do I create a shared context and use it across multiple windows?
Could you post the code you got to with multiple contexts? (_draw
mainly)
Sure:
The draw method (part of the window class)
//-----------------------------------------------------------------------------------------------------//
//Draw this windows' content
//-----------------------------------------------------------------------------------------------------//
protected void _draw()
{
if (isAlive())
{
//Assign window to context?
GLFW.glfwMakeContextCurrent(windowHandle);
//Clear window surface
surface.getCanvas().clear(0x00000000);
AESceneComposition composition = sceneManager.getComposition();
if (composition != null)
{
AERenderTarget compositionTarget = composition.getCompositionTarget();
if (compositionTarget != null && !compositionTarget.isDisposed())
{
//--> Draw scene composition target to window surface
compositionTarget.drawTo(surface.getCanvas(), new AERectangleInt(0,0, compositionTarget.getWidth(), compositionTarget.getHeight()), new AERectangleInt(0,0, frameBufferResolution.getX(), frameBufferResolution.getY()));
}
}
//Swap window frame buffers
GLFW.glfwSwapBuffers(windowHandle);
}
}
"composition target" ist just another surface I create as a offscreen render target. The acual draw mehtod of each window then only "overlays" the composition target onto the window surface.
Code from rendertarget:
//-----------------------------------------------------------------------------------------------------//
//Constructor
//-----------------------------------------------------------------------------------------------------//
public AERenderTarget(DirectContext context, int width, int height, int aaSamples)
{
this.context = context;
this.width = width;
this.height = height;
ImageInfo info = new ImageInfo(width, height, ColorType.RGBA_8888, ColorAlphaType.PREMUL);
SurfaceProps props = new SurfaceProps(PixelGeometry.RGB_H);
surface = Surface.makeRenderTarget(this.context, false, info, aaSamples, props);
}
I already checked to make sure, I always pass the "correct" conext object... But there might be an issue here.
Main game loop code:
while (AEGameWindowManager.getPrimaryWindow().isAlive() && alive)
{
//Update game instance
_update();
AEGameWindow[] windows = AEGameWindowManager.getWindows();
for (AEGameWindow window : windows)
{
if (window != AEGameWindowManager.getPrimaryWindow() && !window.isAlive())
{
//--> Terminate sub window if not alive anymore
AEGameWindowManager.terminateWindow(window.getWindowName());
}
else if (window.isAlive())
{
//--> Update engine of alive windows
window.getSceneManager()._input();
window.getSceneManager()._update();
window.getSceneManager()._draw();
//Draw frame to window
window._draw();
//Flush context
window.getContext().flush();
window.getContext().resetGLAll();
}
}
}
The last context.resetGLAll() was just a hack to see if it helps. By the way, thank you very much for taking time to help me figure this out.
Oh god I think I just figured it out... Of course I have to make the windows context current BEFORE I start making new texture objects and paths. glfwMakeContextCurrent() needs to be part of the game loop, not the window draw method.
while (AEGameWindowManager.getPrimaryWindow().isAlive() && alive)
{
//Update game instance
_update();
AEGameWindow[] windows = AEGameWindowManager.getWindows();
for (AEGameWindow window : windows)
{
if (window != AEGameWindowManager.getPrimaryWindow() && !window.isAlive())
{
//--> Terminate sub window if not alive anymore
AEGameWindowManager.terminateWindow(window.getWindowName());
}
else if (window.isAlive())
{
//HERE! I need to make the context current before I start building context relevant object like textures!
GLFW.glfwMakeContextCurrent(window.getWindowHandle());
//--> Update engine of alive windows
window.getSceneManager()._input();
window.getSceneManager()._update();
window.getSceneManager()._draw();
//Draw frame to window
window._draw();
//Flush context
window.getContext().flush();
}
}
}
I think it works now, sorry for bothering you again^^
Ok, it's fixed (again... hopefully) It seems like Skija takes whatever context is current, when I make a new context relevant object like textures and paths.
I'm currently trying to figure out how to draw to multiple windows, but with no avail. All I could achieve so far is one window (the primary one) to render correctly, while the secondary window just displays a black screen. I tried replicating the LWJGL example of how to run multiple GL windows simultaneously, but that wasn't really helpful: https://github.com/LWJGL/lwjgl3/blob/master/modules/samples/src/test/java/org/lwjgl/demo/glfw/MultipleWindows.java Mainly because the LWJGL example relies on multiple context objects (one for each window), which I would like to avoid (if possible).
Some boilerplate code of my general order of operation so far:
Window constructor:
Is my assumption correct, that I have to specify the primary windows' handle as "shared" when I call glfwCreateWindow() on the secondary window creation? (If I want all my windows to use the same context as the primary window)
Window constructor continuation:
Some additional window functions:
Do I have to assign an incremental framebuffer id for each backend render target? (I tried assigning a different id to the secondary window, but that didn't change anything.)
Window draw():
Main loop:
It seems like I have to call DirectContext.flush() once each frame. But when exactly do I have to do this? After each window was drawn, or after all windows have been drawn? If I do this after each window was drawn, all windows stop working and only display a black screen...
My main question is: Are multiple windows even supported by Skija right now? And if yes, Is there an order of operation, I have to comply to? And does it matter if I have one context or multiple ones? The only time I need to access the windows' context is when I create a new Skija Surface. (I assume because the underlying rendertarget resource is held by the context) But if that is the case, why can I create a new Skija Image without referencing a context? (It's just a texture object I assume. It should be assigned to a context?)