LWJGL的glGenVertexArrays()阻塞了执行。

huangapple go评论77阅读模式
英文:

LWJGL glGenVertexArrays() is blocking the execution

问题

我最近在论坛上询问了一个关于为什么ExecutorService在获取Future后阻塞了我的程序的问题:
https://stackoverflow.com/questions/63860543/java-executorservice-and-future-are-blocking-the-main-thread

问题在于,经过大量调试,问题并不是出在future.get()这一行,而是在int vaoID = GL30.glGenVertexArrays();这一行。

我来总结一下我尝试的内容,然后发布代码。基本上,我在玩家移动时正在生成地形。为了避免每次生成时游戏卡顿,我使用了CallableExecutorServiceFuture来实现多线程。因此,当我需要生成地形时,我会向ExecutorService提交新的任务(任务是计算地形的所有顶点/法线/颜色),然后对于每个Future,如果已完成,我会获取结果并生成地形的模型。但是在每一帧中,第一个完成的Future会导致卡顿。实际上,导致卡顿的不是Future本身,而是创建模型,更确切地说是int vaoID = GL30.glGenVertexArrays();这个方法。

总之,我认为glGenVertexArrays()在阻塞线程,可能在等待其他线程完成,但我完全不知道发生了什么。我正在使用LWJGL 2和OpenGL 3.0+。

下面是处理多线程的代码:

// 每帧调用的方法
private void checkForFutures(List<Future<SomeClass>> terrainsInCreation) {
    try {
        List<Future<SomeClass>> futuresToRemove = new ArrayList<Future<SomeClass>>();
        for (Future<SomeClass> future : terrainsInCreation) {
            if (future.isDone()) {
                float time = DisplayManager.getCurrentTime();
                try {
                    SomeClass t = future.get();
                    Key key = new Key(t.terrain.getChunkX(), t.terrain.getChunkZ());
                    t.terrain.generateTerrain(loader, t.vertices, t.indices, t.colors, t.normals); // 有问题的行!
                    futuresToRemove.add(future);
                } catch (ExecutionException e) {
                    e.printStackTrace();
                    Thread.sleep(1000000);
                }
            }
        }
        terrainsInCreation.removeAll(futuresToRemove);
    } catch (InterruptedException ie) {
        System.err.println("TERRAIN MANAGER有严重问题");
    }
}

// 有需要生成地形时调用的方法
private void generate(List<Callable<SomeClass>> terrainsToCreate) {
    for (int i = 0; i < terrainsToCreate.size(); i++) {
        terrainsInCreation.add(executor.submit(terrainsToCreate.get(i)));
    }
}

下面是generateTerrain()方法:

int vaoID = createVAO();
bindIndicesBuffer(indices);
storeDataInAttributeList(0, 3, positions);
storeDataInAttributeList(1, 3, colors);
storeDataInAttributeList(2, 3, normals);
unbindVAO();
return new RawModel(vaoID, indices.length);

(我简化了代码)

最后,这是createVAO()方法:

private int createVAO() {
    long start = DisplayManager.getCurrentTime();
    int vaoID = GL30.glGenVertexArrays(); // 每帧的第一个花费 ~40-200ms
    long glGen = DisplayManager.getCurrentTime();
    vaos.add(vaoID);
    long add = DisplayManager.getCurrentTime();
    GL30.glBindVertexArray(vaoID);
    long bind = DisplayManager.getCurrentTime();
    System.out.println(String.format("Vao: gen: %d + add: %d + bind: %d = %d",
            (glGen - start),
            (add - glGen),
            (bind - add),
            (DisplayManager.getCurrentTime() - start)));
    return vaoID;
}

最后,这是正常生成的输出:

NEW CHECK
NEW CHECK
Vao: gen: 7 + add: 0 + bind: 0 = 7
Vao: gen: 1 + add: 0 + bind: 0 = 1
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
NEW CHECK
Vao: gen: 209 + add: 0 + bind: 0 = 209 (为什么???)
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 1 + add: 0 + bind: 0 = 1
Vao: gen: 1 + add: 0 + bind: 0 = 1
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 1 = 1
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
NEW CHECK
Vao: gen: 4 + add: 0 + bind: 0 = 4
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 

<details>
<summary>英文:</summary>

I recently asked a question on the forum on why the ExecutorService was blocking my program after getting the futures: 
https://stackoverflow.com/questions/63860543/java-executorservice-and-future-are-blocking-the-main-thread

The thing is that after a lot of debugging, the guilty line wasn&#39;t the future.get() but ``` int vaoID = GL30.glGenVertexArrays(); ```

I&#39;ll sum up what I&#39;m trying to do, and then post the code. Basically, I&#39;m generating terrains as the player moves. To avoid freezing the game whenever the generation happens, I implemented multithreading with callables and ExecutorService and Futures. So when I have to generate terrains I&#39;m submitting new tasks to the ExecutorService (the task is to calculate all the vertices/normals/colors of the terrain), and then for each future, if it is done I&#39;m getting the result and generate the model of the terrain. But at each frame, the first future that finishes is causing a freeze. Actually, it&#39;s not the future itself that is causing it but the creation of the model, and more precisely the ``` int vaoID = GL30.glGenVertexArrays(); ``` method. 

To sum up, I think that glGenVertexArrays(); is blocking the thread, waiting for maybe the other threads to finish, and I don&#39;t have a single clue of what is going on. I am using LWJGL 2 and OpenGL 3.0+.

**Here is the code that handles the multithreading:**

//method called each frame
private void checkForFutures(List<Future<SomeClass>> terrainsInCreation) {
try
{
List<Future<SomeClass>> futuresToRemove = new ArrayList<Future<SomeClass>>();
for(Future<SomeClass> future:terrainsInCreation) {
if(future.isDone()) {
float time = DisplayManager.getCurrentTime();
try {
SomeClass t = future.get();
Key key = new Key(t.terrain.getChunkX(),t.terrain.getChunkZ());
t.terrain.generateTerrain(loader, t.vertices, t.indices,t.colors, t.normals); // Guilty line !
futuresToRemove.add(future);
} catch (ExecutionException e) {
e.printStackTrace();
Thread.sleep(1000000);
}
}
}
terrainsInCreation.removeAll(futuresToRemove);
}
catch (InterruptedException ie)
{
System.err.println("BIG PROBLEM ON TERRAIN MANAGER");
}
}

//method called when I have terrains to generate
private void generate(List<Callable<SomeClass>> terrainsToCreate) {

	for(int i = 0; i &lt; terrainsToCreate.size();i++) {
terrainsInCreation.add(executor.submit(terrainsToCreate.get(i)));
} 
}

**Here is the generateTerrain() method:**
	int vaoID = createVAO();
bindIndicesBuffer(indices);
storeDataInAttributeList(0,3, positions);
storeDataInAttributeList(1,3, colors);
storeDataInAttributeList(2, 3, normals);
unbindVAO();
return new RawModel(vaoID, indices.length);
(I simplified the code)
**And here is the createVao() method:**

private int createVAO() {
long start = DisplayManager.getCurrentTime();
int vaoID = GL30.glGenVertexArrays(); // The first of each frame takes ~40-200ms
long glGen = DisplayManager.getCurrentTime();

	vaos.add(vaoID);
long add = DisplayManager.getCurrentTime();
GL30.glBindVertexArray(vaoID);
long bind = DisplayManager.getCurrentTime();
System.out.println(String.format(&quot;Vao: gen: %d + add: %d + bind: %d = %d&quot;, 
(glGen-start),
(add-glGen),
(bind-add),
(DisplayManager.getCurrentTime()-start)));
return vaoID;
}

**And finally here is the print output in a regular generation:**

NEW CHECK
NEW CHECK
Vao: gen: 7 + add: 0 + bind: 0 = 7
Vao: gen: 1 + add: 0 + bind: 0 = 1
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
NEW CHECK
Vao: gen: 209 + add: 0 + bind: 0 = 209 (why????)
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 1 + add: 0 + bind: 0 = 1
Vao: gen: 1 + add: 0 + bind: 0 = 1
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 1 = 1
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
NEW CHECK
Vao: gen: 4 + add: 0 + bind: 0 = 4
Vao: gen: 0 + add: 0 + bind: 0 = 0
Vao: gen: 0 + add: 0 + bind: 0 = 0
NEW CHECK
NEW CHECK


Each frame I&#39;m printing &quot;NEW CHECK&quot; and as you can see it is not exactly the first of each frame sometimes it is not but when a generation event occurs the first is always super slow.
My question is what is going on here and what can I do / test / update ?
Thanks for your help.
**EDIT**
I must precise that GL30.glGenVertexArrays() is called in the main thread.
Here is the context code executed before anything else at the start of the program:

ContextAttribs attribs = new ContextAttribs(3,2)
.withForwardCompatible(true)
.withProfileCore(true);
Mouse.setGrabbed(true);

	try {
DisplayMode displayMode = new DisplayMode(WIDTH, HEIGHT);
Display.setDisplayMode(displayMode);
Display.create(new PixelFormat(),attribs);
Display.setTitle(&quot;CubeLand&quot;);
} catch (LWJGLException e) {
e.printStackTrace();
}

**EDIT 2**
I removed the multithreading, by doing everything in the main thread, and about the same freeze happens when generating, which is normal because the calculations are huge, **BUT** the GL30.glGenVertexArrays() is not taking any time to compute.
This proves that this method is waiting for all the other threads to end, but why and how to avoid that??

NEW CHECK
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
Vao gen: 0
NEW CHECK


**EDIT 3**
Some news, when I unplug my laptop, the graphic card is turned off and the motherboard graphic chipset replace it. Suddenly, the issue is gone. Off course I still have freezes because now that my performance are cut off, the processor struggles to generate the terrain even with multithreading, but the vao gen time is back to 0. This is confusing.
</details>
# 答案1
**得分**: 0
好的,这是翻译好的部分:
"Okay, so after 2 weeks of struggling with this, I finally found a workaround. After a long profiling session, I'm 90% sure that glGenVertexArrays() is waiting for some threads to finish their tasks, I don't know why, but it correlates 2/3 times. The workaround is to wait for all the futures to come out, and then generate the terrains (aka call glGenVertexArrays()). This way, the glGenVertexArrays() is not pausing the main thread, and to avoid creating dozens of terrains at the same time I'm only creating like 2-3 per frames, which is unnoticeable."
<details>
<summary>英文:</summary>
Okay, so after 2 weeks of struggling with this, I finally found a workaround. After a long profiling session, I&#39;m 90% sure that glGenVertexArrays() is waiting for some threads to finish their tasks, I don&#39;t know why, but it correlates 2/3 times. The workaround is to wait for all the futures to come out, and then generate the terrains (aka call glGenVertexArrays()). This way, the glGenVertexArrays() is not pausing the main thread, and to avoid creating dozens of terrains at the same time I&#39;m only creating like 2-3 per frames, which is unnoticeable.
</details>

huangapple
  • 本文由 发表于 2020年9月13日 00:04:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/63862253.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定