Skip to content

Commit e4cda6f

Browse files
author
scott
committed
Make GLFence, SSBO, ComputeShader NativeObjects
1 parent c6beb87 commit e4cda6f

File tree

10 files changed

+214
-73
lines changed

10 files changed

+214
-73
lines changed

jme3-core/src/main/java/com/jme3/renderer/Renderer.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
import com.jme3.material.RenderState;
3535
import com.jme3.math.ColorRGBA;
36+
import com.jme3.renderer.opengl.GLFence;
3637
import com.jme3.scene.Mesh;
3738
import com.jme3.scene.VertexBuffer;
3839
import com.jme3.shader.bufferobject.BufferObject;
@@ -552,5 +553,11 @@ public default void pushDebugGroup(String name) {
552553

553554
public void setShaderStorageBufferObject(int bindingPoint, BufferObject bufferObject) ;
554555
public void setUniformBufferObject(int bindingPoint, BufferObject bufferObject) ;
555-
556+
557+
public void deleteFence(GLFence fence);
558+
559+
/**
560+
* Registers a NativeObject to be cleaned up by this renderer.
561+
*/
562+
public void registerNativeObject(NativeObject nativeObject);
556563
}

jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.jme3.math.Vector4f;
3838
import com.jme3.renderer.RendererException;
3939
import com.jme3.util.BufferUtils;
40+
import com.jme3.util.NativeObject;
4041

4142
import java.nio.FloatBuffer;
4243
import java.nio.IntBuffer;
@@ -46,16 +47,28 @@
4647
* <p>
4748
* Compute shaders require OpenGL 4.3 or higher.
4849
*/
49-
public class ComputeShader {
50+
public class ComputeShader extends NativeObject {
5051

5152
private final GL4 gl;
52-
private final int programId;
53+
private final String source;
5354
/**
5455
* Creates a new compute shader from GLSL source code.
5556
*/
5657
public ComputeShader(GL4 gl, String source) {
58+
super();
5759
this.gl = gl;
60+
this.source = source;
61+
//Load this upfront to surface any problems at init time
62+
createComputeShader();
63+
}
64+
private ComputeShader(ComputeShader source){
65+
super();
66+
this.gl = source.gl;
67+
this.id = source.id;
68+
this.source = null;
69+
}
5870

71+
private void createComputeShader(){
5972
// Create and compile the shader
6073
int shaderId = gl.glCreateShader(GL4.GL_COMPUTE_SHADER);
6174
if (shaderId <= 0) {
@@ -78,35 +91,40 @@ public ComputeShader(GL4 gl, String source) {
7891
}
7992

8093
// Create program and link
81-
programId = gl.glCreateProgram();
82-
if (programId <= 0) {
94+
id = gl.glCreateProgram();
95+
if (id <= 0) {
8396
gl.glDeleteShader(shaderId);
8497
throw new RendererException("Failed to create shader program");
8598
}
8699

87-
gl.glAttachShader(programId, shaderId);
88-
gl.glLinkProgram(programId);
100+
gl.glAttachShader(id, shaderId);
101+
gl.glLinkProgram(id);
89102

90103
// Check link status
91-
gl.glGetProgram(programId, GL.GL_LINK_STATUS, intBuf);
104+
gl.glGetProgram(id, GL.GL_LINK_STATUS, intBuf);
92105
if (intBuf.get(0) != GL.GL_TRUE) {
93-
gl.glGetProgram(programId, GL.GL_INFO_LOG_LENGTH, intBuf);
94-
String infoLog = gl.glGetProgramInfoLog(programId, intBuf.get(0));
106+
gl.glGetProgram(id, GL.GL_INFO_LOG_LENGTH, intBuf);
107+
String infoLog = gl.glGetProgramInfoLog(id, intBuf.get(0));
95108
gl.glDeleteShader(shaderId);
96-
gl.glDeleteProgram(programId);
109+
gl.glDeleteProgram(id);
97110
throw new RendererException("Compute shader program linking failed: " + infoLog);
98111
}
99112

100113
// Shader object can be deleted after linking
101114
gl.glDeleteShader(shaderId);
115+
116+
clearUpdateNeeded();
102117
}
103118

104119
/**
105120
* Activates this compute shader for use.
106121
* Must be called before setting uniforms or dispatching.
107122
*/
108123
public void makeActive() {
109-
gl.glUseProgram(programId);
124+
if(isUpdateNeeded()){
125+
createComputeShader();
126+
}
127+
gl.glUseProgram(id);
110128
}
111129

112130
/**
@@ -144,18 +162,35 @@ public void setUniform(int location, Matrix4f value) {
144162
}
145163

146164
public int getUniformLocation(String name) {
147-
return gl.glGetUniformLocation(programId, name);
165+
return gl.glGetUniformLocation(id, name);
148166
}
149167

150168
public void bindShaderStorageBuffer(int location, ShaderStorageBufferObject ssbo) {
151-
gl.glBindBufferBase(GL4.GL_SHADER_STORAGE_BUFFER, location, ssbo.getBufferId());
169+
gl.glBindBufferBase(GL4.GL_SHADER_STORAGE_BUFFER, location, ssbo.getId());
152170
}
153171

154-
/**
155-
* Deletes this compute shader and releases GPU resources.
156-
* The shader should not be used after calling this method.
157-
*/
158-
public void delete() {
159-
gl.glDeleteProgram(programId);
172+
@Override
173+
public void resetObject() {
174+
id = INVALID_ID;
175+
setUpdateNeeded();
176+
}
177+
178+
@Override
179+
public void deleteObject(Object rendererObject) {
180+
if(id != INVALID_ID){
181+
gl.glDeleteProgram(id);
182+
}
183+
resetObject();
184+
}
185+
186+
@Override
187+
public NativeObject createDestructableClone() {
188+
return new ComputeShader(this);
189+
}
190+
191+
@Override
192+
public long getUniqueId() {
193+
//Note this is the same type of ID as a regular shader.
194+
return ((long)OBJTYPE_SHADER << 32) | (0xffffffffL & (long)id);
160195
}
161196
}

jme3-core/src/main/java/com/jme3/renderer/opengl/GLFence.java

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
*/
3232
package com.jme3.renderer.opengl;
3333

34+
import com.jme3.renderer.Renderer;
35+
import com.jme3.util.NativeObject;
36+
37+
import java.util.concurrent.atomic.AtomicInteger;
38+
3439
/**
3540
* Wrapper for an OpenGL sync object (fence).
3641
* <p><a target="_blank" href="https://registry.khronos.org/OpenGL-Refpages/gl4/html/glFenceSync.xhtml">See here.</a></p>
@@ -45,38 +50,36 @@
4550
* @see GL4#glClientWaitSync(GLFence, int, long)
4651
* @see GL4#glDeleteSync(GLFence)
4752
*/
48-
public class GLFence {
49-
50-
private final long handle;
53+
public class GLFence extends NativeObject {
54+
private final static AtomicInteger nextUniqueId = new AtomicInteger(1);
5155
private Object nativeSync;
5256

57+
5358
/**
54-
* Creates a new fence wrapper with the given handle.
55-
*
56-
* @param handle the native sync object handle (pointer)
59+
* Most NativeObject implementations use the int handle GL returns as the NativeObjectId.
60+
* However, fence IDs are actually longs.
61+
* (This probably won't cause overflow issues; you're not likely to have 2 billion fences usefully around at once.)
5762
*/
58-
public GLFence(long handle) {
59-
this.handle = handle;
60-
}
63+
private final long fenceId;
6164

6265
/**
6366
* Creates a new fence wrapper with the given handle and native sync object.
6467
*
65-
* @param handle the native sync object handle (pointer)
66-
* @param nativeSync the backend-specific sync object (e.g., LWJGL2's GLSync)
68+
* @param fenceId the native sync object handle (pointer)
69+
* @param nativeSync the backend-specific sync object (e.g., LWJGL2's GLSync) or null
6770
*/
68-
public GLFence(long handle, Object nativeSync) {
69-
this.handle = handle;
71+
public GLFence(long fenceId, Object nativeSync) {
72+
super();
73+
this.fenceId = fenceId;
74+
this.id = nextUniqueId.getAndIncrement();
7075
this.nativeSync = nativeSync;
76+
clearUpdateNeeded();
7177
}
72-
73-
/**
74-
* Returns the native sync object handle.
75-
*
76-
* @return the sync handle
77-
*/
78-
public long getHandle() {
79-
return handle;
78+
private GLFence(GLFence source) {
79+
super();
80+
this.fenceId = source.fenceId;
81+
this.nativeSync = source.nativeSync;
82+
this.id = source.id;
8083
}
8184

8285
/**
@@ -90,4 +93,29 @@ public long getHandle() {
9093
public Object getNativeSync() {
9194
return nativeSync;
9295
}
96+
public long getFenceId() {
97+
return fenceId;
98+
}
99+
100+
@Override
101+
public void resetObject() {
102+
this.nativeSync = null;
103+
this.id = INVALID_ID;
104+
setUpdateNeeded();
105+
}
106+
107+
@Override
108+
public void deleteObject(Object rendererObject) {
109+
((Renderer)rendererObject).deleteFence(this);
110+
}
111+
112+
@Override
113+
public NativeObject createDestructableClone() {
114+
return new GLFence(this);
115+
}
116+
117+
@Override
118+
public long getUniqueId() {
119+
return ((long) OBJTYPE_FENCE << 32) | (0xffffffffL & (long) id);
120+
}
93121
}

jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3603,4 +3603,17 @@ public boolean isMainFrameBufferSrgb() {
36033603
public GL4 getGl4(){
36043604
return gl4;
36053605
}
3606+
3607+
@Override
3608+
public void deleteFence(GLFence fence) {
3609+
if(gl4 != null && fence.getId() != NativeObject.INVALID_ID){
3610+
gl4.glDeleteSync(fence);
3611+
fence.resetObject();
3612+
}
3613+
}
3614+
3615+
@Override
3616+
public void registerNativeObject(NativeObject nativeObject) {
3617+
objManager.registerObject(nativeObject);
3618+
}
36063619
}

jme3-core/src/main/java/com/jme3/renderer/opengl/ShaderStorageBufferObject.java

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
*/
3232
package com.jme3.renderer.opengl;
3333

34+
import com.jme3.renderer.RendererException;
3435
import com.jme3.util.BufferUtils;
36+
import com.jme3.util.NativeObject;
3537

3638
import java.nio.IntBuffer;
3739

@@ -42,21 +44,33 @@
4244
* SSBOs are buffers that can be read from and written to by shaders.
4345
* SSBOs require OpenGL 4.3 or higher.
4446
*/
45-
public class ShaderStorageBufferObject {
47+
public class ShaderStorageBufferObject extends NativeObject {
4648

4749
private final GL4 gl;
48-
private final int bufferId;
4950

5051
/**
5152
* Creates a new SSBO.
5253
*
5354
* @param gl the GL4 interface (required for glBindBufferBase)
5455
*/
5556
public ShaderStorageBufferObject(GL4 gl) {
57+
super();
5658
this.gl = gl;
57-
IntBuffer buf = BufferUtils.createIntBuffer(1);
58-
gl.glGenBuffers(buf);
59-
this.bufferId = buf.get(0);
59+
ensureBufferReady();
60+
}
61+
private ShaderStorageBufferObject(ShaderStorageBufferObject source){
62+
super();
63+
this.gl = source.gl;
64+
this.id = source.id;
65+
}
66+
67+
private void ensureBufferReady(){
68+
if(isUpdateNeeded()){
69+
IntBuffer buf = BufferUtils.createIntBuffer(1);
70+
gl.glGenBuffers(buf);
71+
this.id = buf.get(0);
72+
clearUpdateNeeded();
73+
}
6074
}
6175

6276
/**
@@ -76,7 +90,8 @@ public void initialize(int[] data) {
7690
* @param data the initial data to upload
7791
*/
7892
public void initialize(IntBuffer data) {
79-
gl.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, bufferId);
93+
ensureBufferReady();
94+
gl.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, id);
8095
gl.glBufferData(GL4.GL_SHADER_STORAGE_BUFFER, data, GL.GL_DYNAMIC_COPY);
8196
}
8297

@@ -109,23 +124,41 @@ public void read(int[] destination) {
109124
* @param destination the buffer to read into
110125
*/
111126
public void read(IntBuffer destination) {
112-
gl.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, bufferId);
127+
if(isUpdateNeeded()){
128+
//If the SSBO was deleted from e.g. context restart, it probably isn't sensible to read from it.
129+
//We could create a fresh empty buffer and read from that, but that might result in garbage data.
130+
throw new RendererException("SSBO was not ready for read");
131+
}
132+
gl.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, id);
113133
gl.glGetBufferSubData(GL4.GL_SHADER_STORAGE_BUFFER, 0, destination);
114134
gl.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, 0);
115135
}
116136

117-
/**
118-
* Deletes this buffer and releases GPU resources.
119-
* The buffer should not be used after calling this method.
120-
*/
121-
public void delete() {
122-
IntBuffer buf = BufferUtils.createIntBuffer(1);
123-
buf.put(bufferId);
124-
buf.flip();
125-
gl.glDeleteBuffers(buf);
137+
138+
@Override
139+
public void resetObject() {
140+
this.id = INVALID_ID;
141+
setUpdateNeeded();
142+
}
143+
144+
@Override
145+
public void deleteObject(Object rendererObject) {
146+
if(id != INVALID_ID){
147+
IntBuffer buf = BufferUtils.createIntBuffer(1);
148+
buf.put(id);
149+
buf.flip();
150+
gl.glDeleteBuffers(buf);
151+
}
152+
resetObject();
153+
}
154+
155+
@Override
156+
public NativeObject createDestructableClone() {
157+
return new ShaderStorageBufferObject(this);
126158
}
127159

128-
public int getBufferId() {
129-
return bufferId;
160+
@Override
161+
public long getUniqueId() {
162+
return ((long) OBJTYPE_BO << 32) | (0xffffffffL & (long) id);
130163
}
131164
}

0 commit comments

Comments
 (0)