Skip to content

Commit 96ade55

Browse files
committed
Add Intro3 and Intro4 which introduce MemoryStack
1 parent 8af3572 commit 96ade55

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: http://lwjgl.org/license.php
4+
*/
5+
package org.lwjgl.demo.intro;
6+
7+
import java.nio.FloatBuffer;
8+
9+
import static org.lwjgl.glfw.GLFW.*;
10+
import static org.lwjgl.opengl.GL.*;
11+
import static org.lwjgl.opengl.GL11.*;
12+
import static org.lwjgl.opengl.GL15.*;
13+
import static org.lwjgl.system.MemoryUtil.*;
14+
import static org.lwjgl.system.MemoryStack.*;
15+
16+
/**
17+
* In Intro2 we learnt how to allocate an off-heap memory buffer using MemoryUtil. This was done by first calling one
18+
* of the memAlloc*() methods which return a Java NIO Buffer instance representing the allocated memory region. Once
19+
* we were done with the buffer, we called the memFree() method to deallocate/free the off-heap memory represented by
20+
* the Java NIO Buffer.
21+
* <p>
22+
* This manual memory management is necessary when a buffer needs to live for an extended amount of time in our
23+
* application, meaning that the time between allocation and deallocation spans beyond one method.
24+
* <p>
25+
* In most scenarios however, the memory will be very short-living. One example was the allocation of the memory to fill
26+
* the VBO in Intro2. Memory was allocated, filled with data, given to OpenGL and then freed again.
27+
* <p>
28+
* LWJGL 3 provides a better way to handle such situations, which is by using the MemoryStack class. This class allows
29+
* to retrieve a small chunk of memory from a pre-allocated thread-local memory region of a fixed size. It is a stack
30+
* because allocations/deallocations must be issued in FIFO order, in that allocations cannot be freed randomly bust
31+
* must be freed in the reverse allocation order. This allows to avoid any heap allocation and compaction strategies.
32+
* <p>
33+
* Also note that the pre-allocated memory of the MemoryStack is per thread. That means, every thread will get its own
34+
* memory region and MemoryStack instances should not be shared among different threads.
35+
*
36+
* @author Kai Burjack
37+
*/
38+
public class Intro3 {
39+
40+
/**
41+
* This sample is the same as Intro2, but uses the MemoryStack to allocate the short-living buffer to fill the VBO.
42+
*/
43+
public static void main(String[] args) {
44+
glfwInit();
45+
long window = createWindow();
46+
47+
/*
48+
* Ask the MemoryStack to create a new "stack frame".
49+
*
50+
* This is basically for house-keeping of the current stack allocation position/pointer. Everytime we allocate
51+
* memory using the MemoryStack (see below), the stack pointer advances (towards address zero). In order to
52+
* "deallocate" memory that was previously allocated by the MemoryStack, all that needs to be done is to reset
53+
* the stack position/pointer to the previous position. This will be done by a call to stackPop().
54+
*
55+
* So, first we need to save the current stack position/pointer using stackPush().
56+
*/
57+
stackPush();
58+
59+
/*
60+
* No we reserve some space on the stack and return it as a new Java NIO Buffer using the MemoryStack's
61+
* stackMallocFloat() method.
62+
* The MemoryStack provides other stackMalloc* methods as well, which return other typed Java NIO Buffer views.
63+
*/
64+
FloatBuffer buffer = stackMallocFloat(3 * 2);
65+
66+
/*
67+
* The following few lines of code from Intro2 are identical. This also means that code _using_ a
68+
* stack-allocated memory buffer does not differ from code using buffers allocated with other allocation
69+
* strategies.
70+
*/
71+
buffer.put(-0.5f).put(-0.5f);
72+
buffer.put(+0.5f).put(-0.5f);
73+
buffer.put(+0.0f).put(+0.5f);
74+
buffer.flip();
75+
int vbo = glGenBuffers();
76+
glBindBuffer(GL_ARRAY_BUFFER, vbo);
77+
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
78+
79+
/*
80+
* Now, that we don't need the allocated buffer anymore, we have to pop the "stack frame".
81+
* This will reset the state of the MemoryStack (i.e. the allocation position/pointer) to where it was before
82+
* we called stackPush() above.
83+
*/
84+
stackPop();
85+
86+
/*
87+
* The following code is just like in Intro2.
88+
*/
89+
glEnableClientState(GL_VERTEX_ARRAY);
90+
glVertexPointer(2, GL_FLOAT, 0, 0L);
91+
while (!glfwWindowShouldClose(window)) {
92+
glfwPollEvents();
93+
glDrawArrays(GL_TRIANGLES, 0, 3);
94+
glfwSwapBuffers(window);
95+
}
96+
glfwTerminate();
97+
}
98+
99+
private static long createWindow() {
100+
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
101+
long window = glfwCreateWindow(800, 600, "Intro3", NULL, NULL);
102+
glfwMakeContextCurrent(window);
103+
createCapabilities();
104+
return window;
105+
}
106+
107+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: http://lwjgl.org/license.php
4+
*/
5+
package org.lwjgl.demo.intro;
6+
7+
import org.lwjgl.system.MemoryStack;
8+
9+
import java.nio.FloatBuffer;
10+
11+
import static org.lwjgl.glfw.GLFW.*;
12+
import static org.lwjgl.opengl.GL.*;
13+
import static org.lwjgl.opengl.GL11.*;
14+
import static org.lwjgl.opengl.GL15.*;
15+
import static org.lwjgl.system.MemoryUtil.*;
16+
import static org.lwjgl.system.MemoryStack.*;
17+
18+
/**
19+
* In Intro3 we learnt how to allocate short-lived memory using the MemoryStack class.
20+
* <p>
21+
* There was one thing missing, though, which is necessary when working with manual memory management, including the
22+
* MemoryStack, which is: Ensuring that the stackPop() call happens eventually.
23+
* This may not be the case when the code between stackPush() and stackPop() throws an exception.
24+
* <p>
25+
* To take care of possible exceptions, we will therefore wrap the code in a try-with-resources statement to ensure that
26+
* stackPop() will get called eventually.
27+
*
28+
* @author Kai Burjack
29+
*/
30+
public class Intro4 {
31+
32+
public static void main(String[] args) {
33+
glfwInit();
34+
long window = createWindow();
35+
36+
/*
37+
* Wrap the code that is using the MemoryStack in a Java 7 try-with-resources statement.
38+
* The nice thing here is that the MemoryStack class itself implements AutoCloseable, so it is applicable to
39+
* being the resource in the try-with-resources statement.
40+
*
41+
* What is also new here is that a call to stackPush() actually returns something, namely an instance of the
42+
* MemoryStack class. This instance represents the thread-local MemoryStack instance, which would otherwise
43+
* be accessed whenever we call stackPush(), stackPop() or one of the static stackMalloc* methods.
44+
*
45+
* So, the code below calls the static method stackPush() on the class MemoryStack, which returns the
46+
* MemoryStack instance of the current thread. At the end of the try-with-resources statement, a call to
47+
* MemoryStack.pop() will be done automatically to undo the allocation.
48+
*/
49+
try (MemoryStack stack = stackPush()) {
50+
/*
51+
* The following code is identical to Intro3.
52+
*/
53+
FloatBuffer buffer = stackMallocFloat(3 * 2);
54+
buffer.put(-0.5f).put(-0.5f);
55+
buffer.put(+0.5f).put(-0.5f);
56+
buffer.put(+0.0f).put(+0.5f);
57+
buffer.flip();
58+
int vbo = glGenBuffers();
59+
glBindBuffer(GL_ARRAY_BUFFER, vbo);
60+
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
61+
62+
/*
63+
* Notice that we do not need a stackPop() here! It will be done automatically at the end of the
64+
* the try-with-resources statement, even in the event of an exception, which was the sole purpose of
65+
* doing it this way, in the first place.
66+
*/
67+
}
68+
69+
glEnableClientState(GL_VERTEX_ARRAY);
70+
glVertexPointer(2, GL_FLOAT, 0, 0L);
71+
while (!glfwWindowShouldClose(window)) {
72+
glfwPollEvents();
73+
glDrawArrays(GL_TRIANGLES, 0, 3);
74+
glfwSwapBuffers(window);
75+
}
76+
glfwTerminate();
77+
}
78+
79+
private static long createWindow() {
80+
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
81+
long window = glfwCreateWindow(800, 600, "Intro3", NULL, NULL);
82+
glfwMakeContextCurrent(window);
83+
createCapabilities();
84+
return window;
85+
}
86+
87+
}

0 commit comments

Comments
 (0)