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

feat(reactor): make separate graphic scheduler/thread #4865

Open
wants to merge 20 commits into
base: develop
Choose a base branch
from

Conversation

DarkWeird
Copy link
Contributor

@DarkWeird DarkWeird commented Aug 24, 2021

Contains

  • Introducing GRAPHICS Scheduler
  • wraps calls to GL/GLFW to runBlocking at graphics scheduler ( hopping flow to another thread)
  • Splash screen should works at MacOS now. (ref: Follow-ups after LWJGL3 #4206)
  • splitting opengl context between splashscreen and game
  • wrapped calls to runBlocking should tracking by ActivityMonitor
    Ref: Add specific macos flags for window creating SplashScreen#7 has macos specific flags for macos (should helps with macos glitches like red window, wring dpi)

How to test

  1. runs Terasology with splash screen (especial at MacOs)
  2. start any gameplay
  3. try to call maximum rendering stuffs
    3.1. block digging particles
    3.2. different Overlay renderer.

Outstanding before merging

  • docs
  • refactor
  • cleanup

After merge:

  • rethink/refactor reactor usage with LWJGL subsystem, lwjgl's assets and opengl rendering.

* splash screen's loop handles by Flux/Mono
* Most calls to GL/GLFW runs at Graphic Thread via GameScheduler#runBlockingGraphics.
…gerSubsystem' into refactor/remove-usage-ThreadManagerSubsystem
@github-actions github-actions bot added the Type: Improvement Request for or addition/enhancement of a feature label Aug 24, 2021
@DarkWeird
Copy link
Contributor Author

TeraED was broken before this PR :(
seems splashscreen flag was broken at Pico-cli PR (you cannot disable splashscreen)

@skaldarnar
Copy link
Member

I did run gradlew game on Mac OS with this PR. The splash screen is not rendering correctly (it's a flashing, half-transparent red rectangle on screen), but the game starts up fine afterwards.

I could not really play it as I was at 0.6 fps, but that might be related to too much load on my machine...

TeraED was broken before this PR :(

What do you mean by that? I haven't tried to start TeraED for ages, but it at least compiled just fine when running gradlew jar for the whole workspace 🤔

@DarkWeird
Copy link
Contributor Author

DarkWeird commented Aug 24, 2021

What do you mean by that? I haven't tried to start TeraED for ages, but it at least compiled just fine when running gradlew jar for the whole workspace 🤔

TeraED hardcrashes with many nullpointers during initializing MainMenuState. This errors not related with rendering code.

I could not really play it as I was at 0.6 fps, but that might be related to too much load on my machine...

Hmm. Which gpu are you using?
Seems macbooks have 2 gpus(or not)
What about without splash screen?(flag --no-splash not works after picocli(seems))
And how without this pr?

The splash screen is not rendering correctly (it's a flashing, half-transparent red rectangle on screen), but the game starts up fine afterwards.

Sounds familiar. With red rectangle. I will google it.

@DarkWeird
Copy link
Contributor Author

Ok. Splashscreen Rendering issues at macos should be resolved at splashscreen repo.
(I will create issue tomorrow)

Base automatically changed from refactor/remove-usage-ThreadManagerSubsystem to develop August 27, 2021 23:43
Copy link
Member

@keturn keturn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an interesting idea, but seeing it in practice is scary

I think a lot of what I'm seeing is a byproduct of the fact that this is wrapping a lot of stuff in the runBlockingGraphics scheduler to keep it as close as possible to the existing implementation, which is arguably the right way to get something like this done in an incremental way.

but so much blocking
and so much confusion about "do I need to wrap this in runGraphics, or is the method I'm calling going to do that?"

Comment on lines +104 to +108
public static <T> Flux<T> wrapActivity(Scheduler scheduler, String name, Flux<T> mono) {
return Flux.using(
() -> ThreadMonitor.startThreadActivity(name),
activity -> mono,
ThreadActivity::close
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I convinced myself this works for a Runnable turned in to a Mono, because we know the Runnable will execute and be on that thread until it is done, and then the mono is over.

Flux is less clear. There could be lots of time between events. Do we report that as all one thread activity?

also, other stuff could be running on that thread between the events of that Flux, is that going to mess up the assumptions in the ThreadMonitor implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch.
Needs to handle both cases.
Whole flux as thread activity and element of flux as thread activity.

Tasks that running at graphic scheduler works via fifo queue (see threadcapturedscheduler)
Then no one switching during processing.

}

/**
* Wraps {@link Mono} with activity monitor.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines +121 to +125
if (!graphicsScheduler.isSchedulerThread(Thread.currentThread())) {
mono = mono
.doOnSubscribe(s-> GLFW.glfwMakeContextCurrent(LwjglGraphics.windowId))
.doFinally(f -> GLFW.glfwMakeContextCurrent(MemoryUtil.NULL))
.subscribeOn(graphicsScheduler);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not experienced with multithreaded use of GL, so I Am Not An Expert, but I've read like 1.2 papers on it, and I am a little worried about making much use of glfwMakeContextCurrent. I hear the context-switching is generally not worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There required for handling multiple contexts/windows.

At linux and windows you can use separate threads for handling separate gl contexts (like window/cintext per thread)
At macos - not.

Then you should change context i this way.

Splashscreen... (You can see some screenshots with messing gl contexts at #architecture)

.doFinally(f -> GLFW.glfwMakeContextCurrent(MemoryUtil.NULL))
.subscribeOn(graphicsScheduler);
}
return mono.block();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a blocking API for this? I didn't think GL calls were synchronous or had return values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some graphics monos load assets and may corrupt state(concurrent access) in theory.

Another. This places was design and tested to run in sequence

I choose fail safe path then for now

Comment on lines +50 to +51
return GameScheduler.runBlockingGraphics("hasFocus",
() -> GLFW.GLFW_TRUE == GLFW.glfwGetWindowAttrib(GLFW.glfwGetCurrentContext(), GLFW.GLFW_FOCUSED));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, here's that blocking use case I was asking about.

bother, this API is a mashup of things, as some of them are about the GL context, and other things (like focus) are not part of the GL API at all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, I don't think these methods that wrap a single glfw function are worth using GameScheduler.wrapActivity on. They shouldn't take enough time to be worth clocking, and, well, they block the graphics thread, so we're not going to be seeing those as open tasks on any monitor that renders via the graphics thread.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glfw related to context too.

Gl context = glfw window handle.

Rules about gl context at one thread applicable for glfw partially (and there i takes context from static. There needs a refactoring to stores context/window handle and reuse it)

Comment on lines 214 to 215
GameScheduler.runBlockingGraphics("isFullscreen", () ->
glViewport(0, 0, width, height)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cut/paste error in description

@@ -46,16 +42,9 @@
import static org.lwjgl.opengl.GL11.glGenTextures;
import static org.lwjgl.opengl.GL11.glTexParameterf;

public class LwjglGraphicsManager implements LwjglGraphicsProcessing {

private final BlockingDeque<Runnable> displayThreadActions = Queues.newLinkedBlockingDeque();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already had a queue of display thread actions? HMM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See main of terasology.
And ThreadCapturedScheduler.

Comment on lines -156 to +127
asynchToDisplayThread(() -> {
int id = glGenTextures();
reloadTexture2D(id, buffers, wrapMode, filterMode, width, height);
idConsumer.accept(id);
});
int id = glGenTextures();
reloadTexture2D(id, buffers, wrapMode, filterMode, width, height);
idConsumer.accept(id);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these methods lost their async wrappers? why did createTexture3D keep it but not this createTexture2D?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think about this methods like this.

Textures3d runs in graphics now (doReload)
Textures2d do same already.

Needs to revisit this place later

Comment on lines +57 to +61
Renderer renderer = Mono.just(0)
.subscribeOn(GameScheduler.graphics())
.doOnNext(n -> initWindow())
.map((n) -> new Renderer())
.doOnNext(Renderer::init)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting this with just(0) instead of initWindow feels weird. Is that so you have something to subscribeOn? Is this the sort of thing that publishOn is for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I detected mono.fromRunable method too late.

All methods used there should runs at graphics.

@keturn keturn added Category: Performance Requests, Issues and Changes targeting performance Topic: Concurrency Requests, issues, and changes relating to threading, concurrency, parallel execution, etc. Topic: Rendering Requests, Issues and Changes related to lighting, meshes, camera, etc. labels Aug 28, 2021
@DarkWeird
Copy link
Contributor Author

@skaldarnar
recheck at macos plz.
enabled vsync - should helps a bit.

# Conflicts:
#	engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java
…scheduler

# Conflicts:
#	engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglDisplayDevice.java
#	engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphics.java
@skaldarnar
Copy link
Member

skaldarnar commented Mar 23, 2022

Now I get the following error about my Mac not being able to support the required OpenGL version (I think that's what '330' refers to, right?)... 😕 It should be able to meet this requirement, though (MacBook Pro 15, 2016), listed with support for OpenGL v4.1 on Apple's support site.

java.lang.RuntimeException: ERROR: 0:1: '' :  version '330' is not supported
ERROR: 0:1: '' : syntax error: #version

        at org.terasology.splash.glfw.graphics.Shader.checkStatus(Shader.java:87)
        at org.terasology.splash.glfw.graphics.Shader.compile(Shader.java:78)
        at org.terasology.splash.glfw.graphics.Shader.createShader(Shader.java:119)
        at org.terasology.splash.glfw.graphics.Shader.loadShader(Shader.java:146)
        at org.terasology.splash.glfw.graphics.Renderer.setupShaderProgram(Renderer.java:407)
        at org.terasology.splash.glfw.graphics.Renderer.init(Renderer.java:77)
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:185)
        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:120)
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200)
        at reactor.core.publisher.FluxSubscribeOnValue$ScheduledScalar.run(FluxSubscribeOnValue.java:180)
        at org.terasology.engine.core.schedulers.ThreadCaptureScheduler.start(ThreadCaptureScheduler.java:59)
        at org.terasology.engine.Terasology.main(Terasology.java:157)
        Suppressed: java.lang.Exception: #block terminated with an error
                at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:99)
                at reactor.core.publisher.Mono.block(Mono.java:1706)
                at org.terasology.engine.GLFWSplashScreen.run(GLFWSplashScreen.java:64)
                at org.terasology.engine.Terasology.call(Terasology.java:168)
                at org.terasology.engine.Terasology.call(Terasology.java:68)
                at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
                at picocli.CommandLine.access$1200(CommandLine.java:145)
                at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
                at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
                at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
                at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
                at picocli.CommandLine.execute(CommandLine.java:2058)
                at org.terasology.engine.Terasology.lambda$main$0(Terasology.java:146)
                at java.base/java.lang.Thread.run(Thread.java:829)

However, glxinfo also just shows "2.1 ATI-4.7.103" while picking up the Radeon GPU...

OpenGL vendor string: ATI Technologies Inc.
OpenGL renderer string: AMD Radeon Pro 455 OpenGL Engine
OpenGL version string: 2.1 ATI-4.7.103
OpenGL shading language version string: 1.20

Edit: Seems related to MC-153714, siumilar error and similar/same detected version

This question on StackOverflow suggests to replace your code from #include <gl.h> to #include <gl3.h>. Maybe that helps? I tried pluggin in the power cord, and also disable the automatic graphis switching, but both did not help with this...

@DarkWeird
Copy link
Contributor Author

@skaldarnar
Welcome to test :)
pollend update those shaders to 330.. but don't update splashscreen
don't forget MovingBlocks/SplashScreen#7 for resolve graphics glitches at splashscreen (at least several)

@skaldarnar
Copy link
Member

skaldarnar commented Mar 24, 2022

The changes seem to help a bit, but it's still not working on (my) Mac 😕

The splash screen window appears before startup, but is rendered as flickering red triangle. No images or text are shown. I did check this with the MovingBlocks/SplashScreen#7 by doing the following, did not help - do I need to set up/configure anything else?

groovyw lib get SplashScreen
cd libs/SplashScreen
gh pr checkout 7

Eventually, the main menu shows up, but when I try to start/create a world the game crashes before anything is rendered on screen with the following error. The loading bar reaches the end and is "Catching World", it looks like it crashes right before rendering the first in-game frame...

FATAL ERROR in native method: Thread[GRAPHICS,5,main]: No context is current or a function that is not available in the current context was called. The JVM will abort execution.
        at org.lwjgl.opengl.EXTFramebufferObject.glBindFramebufferEXT(Native Method)
        at org.terasology.engine.rendering.dag.stateChanges.BindFbo.process(BindFbo.java:62)
        at org.terasology.engine.rendering.world.WorldRendererImpl$$Lambda$809/0x00000008007e0040.accept(Unknown Source)
        at java.util.ArrayList.forEach(java.base@11.0.11/ArrayList.java:1541)
        at org.terasology.engine.rendering.world.WorldRendererImpl.render(WorldRendererImpl.java:351)
        at org.terasology.engine.core.modes.StateIngame.render(StateIngame.java:223)
        at org.terasology.engine.core.subsystem.lwjgl.LwjglGraphics.lambda$postUpdate$1(LwjglGraphics.java:82)
        at org.terasology.engine.core.subsystem.lwjgl.LwjglGraphics$$Lambda$530/0x0000000800699040.run(Unknown Source)
        at reactor.core.publisher.MonoRunnable.subscribe(MonoRunnable.java:49)
        at reactor.core.publisher.MonoUsing.subscribe(MonoUsing.java:109)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4399)
        at reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.run(MonoSubscribeOn.java:126)
        at org.terasology.engine.core.schedulers.ThreadCaptureScheduler.start(ThreadCaptureScheduler.java:59)
        at org.terasology.engine.Terasology.main(Terasology.java:157)

@jdrueckert jdrueckert added this to the 5.4.0 milestone Sep 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Category: Performance Requests, Issues and Changes targeting performance Topic: Concurrency Requests, issues, and changes relating to threading, concurrency, parallel execution, etc. Topic: Rendering Requests, Issues and Changes related to lighting, meshes, camera, etc. Type: Improvement Request for or addition/enhancement of a feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants