英文:
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();
这一行。
我来总结一下我尝试的内容,然后发布代码。基本上,我在玩家移动时正在生成地形。为了避免每次生成时游戏卡顿,我使用了Callable
、ExecutorService
和Future
来实现多线程。因此,当我需要生成地形时,我会向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't the future.get() but ``` int vaoID = GL30.glGenVertexArrays(); ```
I'll sum up what I'm trying to do, and then post the code. Basically, I'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'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'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'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'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 < 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("Vao: gen: %d + add: %d + bind: %d = %d",
(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'm printing "NEW CHECK" 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("CubeLand");
} 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'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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论