Browse Source

added memory managing: native resources are now automatically released

shamanDevel 9 năm trước cách đây
mục cha
commit
0c47bf18c9

+ 5 - 1
jme3-core/src/main/java/com/jme3/opencl/Buffer.java

@@ -37,7 +37,7 @@ import java.nio.ByteBuffer;
  *
  * @author Sebastian Weiss
  */
-public abstract class Buffer {
+public abstract class Buffer implements OpenCLObject {
 
     public abstract int getSize();
 
@@ -154,4 +154,8 @@ public abstract class Buffer {
     }
     
     public abstract Event copyToImageAsync(CommandQueue queue, Image dest, long srcOffset, long[] destOrigin, long[] destRegion);
+    
+    public abstract Event acquireBufferForSharingAsync(CommandQueue queue);
+    public abstract Event releaseBufferForSharingAsync(CommandQueue queue);
+    //TODO: add variants of the above two methods that don't create the event object, but release the event immediately
 }

+ 1 - 3
jme3-core/src/main/java/com/jme3/opencl/CommandQueue.java

@@ -31,13 +31,11 @@
  */
 package com.jme3.opencl;
 
-import java.nio.ByteBuffer;
-
 /**
  *
  * @author Sebastian Weiss
  */
-public interface CommandQueue {
+public interface CommandQueue extends OpenCLObject {
 
     void flush();
 

+ 1 - 2
jme3-core/src/main/java/com/jme3/opencl/Context.java

@@ -38,7 +38,6 @@ import com.jme3.opencl.Image.ImageDescriptor;
 import com.jme3.opencl.Image.ImageFormat;
 import com.jme3.opencl.Image.ImageType;
 import com.jme3.scene.VertexBuffer;
-import com.jme3.scene.mesh.IndexBuffer;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Texture;
 import java.io.BufferedReader;
@@ -55,7 +54,7 @@ import java.util.logging.Logger;
  *
  * @author Sebastian Weiss
  */
-public abstract class Context {
+public abstract class Context implements OpenCLObject {
     private static final Logger LOG = Logger.getLogger(Context.class.getName());
 
     public abstract List<? extends Device> getDevices();

+ 1 - 1
jme3-core/src/main/java/com/jme3/opencl/Event.java

@@ -35,7 +35,7 @@ package com.jme3.opencl;
  *
  * @author Sebastian Weiss
  */
-public interface Event {
+public interface Event extends OpenCLObject {
 	
 	void waitForFinished();
 	

+ 2 - 1
jme3-core/src/main/java/com/jme3/opencl/Image.java

@@ -39,7 +39,7 @@ import java.util.Objects;
  *
  * @author Sebastian Weiss
  */
-public interface Image {
+public interface Image extends OpenCLObject {
     
     public static enum ImageChannelType {
         SNORM_INT8,
@@ -237,4 +237,5 @@ public interface Image {
     
     Event acquireImageForSharingAsync(CommandQueue queue);
     Event releaseImageForSharingAsync(CommandQueue queue);
+    //TODO: add variants of the above two methods that don't create the event object, but release the event immediately
 }

+ 3 - 1
jme3-core/src/main/java/com/jme3/opencl/Kernel.java

@@ -40,7 +40,7 @@ import java.nio.ByteBuffer;
  *
  * @author Sebastian Weiss
  */
-public abstract class Kernel {
+public abstract class Kernel implements OpenCLObject {
     protected final WorkSize globalWorkSize;
     protected final WorkSize workGroupSize;
 
@@ -180,4 +180,6 @@ public abstract class Kernel {
         return Run(queue);
     }
 
+    //TODO: add variants of the above three methods that don't create the event object, but release the event immediately
+    
 }

+ 45 - 0
jme3-core/src/main/java/com/jme3/opencl/OpenCLObject.java

@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * 
+ * @author Sebastian Weiss
+ */
+public interface OpenCLObject {
+    
+    public static interface ObjectReleaser {
+        void release();
+    }
+    ObjectReleaser getReleaser();
+
+}

+ 118 - 0
jme3-core/src/main/java/com/jme3/opencl/OpenCLObjectManager.java

@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.HashSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Sebastian Weiss
+ */
+public class OpenCLObjectManager {
+    private static final Logger LOG = Logger.getLogger(OpenCLObjectManager.class.getName());
+    private static final Level LOG_LEVEL1 = Level.INFO;
+    private static final Level LOG_LEVEL2 = Level.INFO;
+    /**
+     * Call Runtime.getRuntime().gc() every these frames
+     */
+    private static final int GC_FREQUENCY = 10;
+    
+    private static final OpenCLObjectManager INSTANCE = new OpenCLObjectManager();
+    private OpenCLObjectManager() {}
+    
+    public static OpenCLObjectManager getInstance() {
+        return INSTANCE;
+    }
+    
+    private ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
+    private HashSet<OpenCLObjectRef> activeObjects = new HashSet<OpenCLObjectRef>();
+    private int gcCounter = 0;
+    
+    private static class OpenCLObjectRef extends PhantomReference<Object> {
+        
+        private OpenCLObject.ObjectReleaser releaser;
+
+        public OpenCLObjectRef(ReferenceQueue<Object> refQueue, OpenCLObject obj){
+            super(obj, refQueue);
+            releaser = obj.getReleaser();
+        }
+    }
+    
+    public void registerObject(OpenCLObject obj) {
+        OpenCLObjectRef ref = new OpenCLObjectRef(refQueue, obj);
+        activeObjects.add(ref);
+        LOG.log(LOG_LEVEL1, "registered OpenCL object: {0}", obj);
+    }
+    
+    private void deleteObject(OpenCLObjectRef ref) {
+        LOG.log(LOG_LEVEL1, "deleting OpenCL object by: {0}", ref.releaser);
+        ref.releaser.release();
+        activeObjects.remove(ref);
+    }
+        
+    public void deleteUnusedObjects() {
+        gcCounter++;
+        if (gcCounter >= GC_FREQUENCY) {
+            //The program is that the OpenCLObjects are so small that they are 
+            //enqueued for finalization very late. Therefore, without this
+            //hack, we are running out of host memory on the OpenCL side quickly.
+            gcCounter = 0;
+            Runtime.getRuntime().gc();
+        }
+        
+        int removed = 0;
+        while (true) {
+            // Remove objects reclaimed by GC.
+            OpenCLObjectRef ref = (OpenCLObjectRef) refQueue.poll();
+            if (ref == null) {
+                break;
+            }
+            deleteObject(ref);
+            removed++;
+        }
+        if (removed >= 1) {
+            LOG.log(LOG_LEVEL2, "NativeObjectManager: {0} native objects were removed from native", removed);
+        }
+    }
+    
+    public void deleteAllObjects() {
+        for (OpenCLObjectRef ref : activeObjects) {
+            LOG.log(LOG_LEVEL1, "deleting OpenCL object by: {0}", ref.releaser);
+            ref.releaser.release();
+        }
+        activeObjects.clear();
+    }
+}

+ 1 - 1
jme3-core/src/main/java/com/jme3/opencl/Program.java

@@ -35,7 +35,7 @@ package com.jme3.opencl;
  *
  * @author Sebastian Weiss
  */
-public interface Program {
+public interface Program extends OpenCLObject {
 	
 	void build(String args) throws KernelCompilationException;
 	void build() throws KernelCompilationException;

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

@@ -35,6 +35,7 @@ import com.jme3.material.RenderState;
 import com.jme3.material.RenderState.StencilOperation;
 import com.jme3.material.RenderState.TestFunction;
 import com.jme3.math.*;
+import com.jme3.opencl.OpenCLObjectManager;
 import com.jme3.renderer.*;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.Mesh.Mode;
@@ -552,6 +553,7 @@ public final class GLRenderer implements Renderer {
     public void cleanup() {
         logger.log(Level.FINE, "Deleting objects and invalidating state");
         objManager.deleteAllObjects(this);
+        OpenCLObjectManager.getInstance().deleteAllObjects();
         statistics.clearMemory();
         invalidateState();
     }
@@ -935,6 +937,7 @@ public final class GLRenderer implements Renderer {
 
     public void postFrame() {
         objManager.deleteUnused(this);
+        OpenCLObjectManager.getInstance().deleteUnusedObjects();
         gl.resetStats();
     }
 

+ 9 - 0
jme3-examples/src/main/java/jme3test/opencl/TestWriteToTexture.java

@@ -61,6 +61,7 @@ public class TestWriteToTexture extends SimpleApplication implements AnalogListe
     private Vector2f C;
     private Image texCL;
     private boolean dragging;
+    private int gcCounter;
 
     public static void main(String[] args){
         TestWriteToTexture app = new TestWriteToTexture();
@@ -84,6 +85,7 @@ public class TestWriteToTexture extends SimpleApplication implements AnalogListe
         guiNode.attachChild(pic);
         
         initCounter = 0;
+        gcCounter = 0;
         
         flyCam.setEnabled(false);
         inputManager.setCursorVisible(true);
@@ -111,6 +113,13 @@ public class TestWriteToTexture extends SimpleApplication implements AnalogListe
         } else {
             updateOpenCL(tpf);
         }
+        
+        gcCounter++;
+        if (gcCounter > 10) {
+            Runtime.getRuntime().gc();
+            gcCounter = 0;
+        }
+//        Runtime.getRuntime().runFinalization();
     }
     
     private void initOpenCL1() {

+ 41 - 1
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java

@@ -45,6 +45,7 @@ public class LwjglBuffer extends Buffer {
 
     public LwjglBuffer(CLMem buffer) {
         this.buffer = buffer;
+        OpenCLObjectManager.getInstance().registerObject(this);
     }
     public CLMem getBuffer() {
         return buffer;
@@ -183,5 +184,44 @@ public class LwjglBuffer extends Buffer {
         long event = Utils.pointerBuffers[0].get(0);
         return new LwjglEvent(q.getCLEvent(event));
     }
-    
+
+    @Override
+    public Event acquireBufferForSharingAsync(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public Event releaseBufferForSharingAsync(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(buffer);
+    }
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLMem mem;
+        private ReleaserImpl(CLMem mem) {
+            this.mem = mem;
+        }
+        @Override
+        public void release() {
+            if (mem != null) {
+                int ret = CL10.clReleaseMemObject(mem);
+                mem = null;
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
 }

+ 20 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java

@@ -32,6 +32,7 @@
 package com.jme3.opencl.lwjgl;
 
 import com.jme3.opencl.CommandQueue;
+import com.jme3.opencl.OpenCLObjectManager;
 import org.lwjgl.opencl.CL10;
 import org.lwjgl.opencl.CLCommandQueue;
 
@@ -45,6 +46,7 @@ public class LwjglCommandQueue implements CommandQueue {
 
     public LwjglCommandQueue(CLCommandQueue queue) {
         this.queue = queue;
+        OpenCLObjectManager.getInstance().registerObject(this);
     }
     
     public CLCommandQueue getQueue() {
@@ -63,4 +65,22 @@ public class LwjglCommandQueue implements CommandQueue {
         Utils.checkError(ret, "clFinish");
     }
     
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(queue);
+    }
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLCommandQueue queue;
+        private ReleaserImpl(CLCommandQueue queue) {
+            this.queue = queue;
+        }
+        @Override
+        public void release() {
+            if (queue != null) {
+                int ret = CL10.clReleaseCommandQueue(queue);
+                queue = null;
+                Utils.reportError(ret, "clReleaseCommandQueue");
+            }
+        }
+    }
 }

+ 24 - 2
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java

@@ -36,7 +36,6 @@ import com.jme3.opencl.Context;
 import com.jme3.opencl.Image.ImageDescriptor;
 import com.jme3.opencl.Image.ImageFormat;
 import com.jme3.scene.VertexBuffer;
-import com.jme3.scene.mesh.IndexBuffer;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Texture;
 import java.nio.ByteBuffer;
@@ -60,6 +59,7 @@ public class LwjglContext extends Context {
     public LwjglContext(CLContext context, List<LwjglDevice> devices) {
         this.context = context;
         this.devices = devices;
+        OpenCLObjectManager.getInstance().registerObject(this);
     }
 
     public CLContext getContext() {
@@ -207,5 +207,27 @@ public class LwjglContext extends Context {
         Utils.checkError(Utils.errorBuffer, "clCreateProgramWithSource");
         return new LwjglProgram(p, this);
     }
-    
+
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(context, devices);
+    }
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLContext context;
+        private final List<LwjglDevice> devices;
+        private ReleaserImpl(CLContext mem, List<LwjglDevice> devices) {
+            this.context = mem;
+            this.devices = devices;
+        }
+        @Override
+        public void release() {
+            if (context != null) {
+                int ret = CL10.clReleaseContext(context);
+                context = null;
+                devices.clear();
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
 }

+ 38 - 1
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java

@@ -32,6 +32,7 @@
 package com.jme3.opencl.lwjgl;
 
 import com.jme3.opencl.Event;
+import com.jme3.opencl.OpenCLObjectManager;
 import java.util.logging.Logger;
 import org.lwjgl.opencl.CL10;
 import org.lwjgl.opencl.CLEvent;
@@ -42,18 +43,29 @@ import org.lwjgl.opencl.CLEvent;
  */
 public class LwjglEvent implements Event {
     private static final Logger LOG = Logger.getLogger(LwjglEvent.class.getName());
-    private final CLEvent event;
+    private CLEvent event;
 
     public LwjglEvent(CLEvent event) {
         this.event = event;
         if (event == null) {
             LOG.warning("event is null!");
+        } else {
+            OpenCLObjectManager.getInstance().registerObject(this);
         }
     }
 
     public CLEvent getEvent() {
         return event;
     }
+    
+    protected void release() {
+        if (event != null && event.isValid()) {
+            int ret = CL10.clReleaseEvent(event);
+            event = null;
+            Utils.reportError(ret, "clReleaseEvent");
+            LOG.finer("Event deleted");
+        }
+    }
 
     @Override
     public void waitForFinished() {
@@ -61,6 +73,7 @@ public class LwjglEvent implements Event {
             return;
         }
         CL10.clWaitForEvents(event);
+        release(); //short cut to save resources
     }
 
     @Override
@@ -70,6 +83,7 @@ public class LwjglEvent implements Event {
         }
         int status = event.getInfoInt(CL10.CL_EVENT_COMMAND_EXECUTION_STATUS);
         if (status == CL10.CL_SUCCESS) {
+            release(); //short cut to save resources
             return true;
         } else if (status < 0) {
             Utils.checkError(status, "EventStatus");
@@ -78,5 +92,28 @@ public class LwjglEvent implements Event {
             return false;
         }
     }
+
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(event);
+    }
     
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLEvent event;
+
+        private ReleaserImpl(CLEvent event) {
+            this.event = event;
+        }
+        
+        @Override
+        public void release() {
+            if (event != null && event.isValid()) {
+                int ret = CL10.clReleaseEvent(event);
+                event = null;
+                Utils.reportError(ret, "clReleaseEvent");
+                LOG.finer("Event deleted");
+            }
+        }
+        
+    }
 }

+ 21 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java

@@ -50,6 +50,7 @@ public class LwjglImage implements Image {
 
     public LwjglImage(CLMem image) {
         this.image = image;
+        OpenCLObjectManager.getInstance().registerObject(this);
     }
 
     public CLMem getImage() {
@@ -542,4 +543,24 @@ public class LwjglImage implements Image {
         long event = Utils.pointerBuffers[0].get(0);
         return new LwjglEvent(q.getCLEvent(event));
     }
+    
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(image);
+    }
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLMem mem;
+        private ReleaserImpl(CLMem mem) {
+            this.mem = mem;
+        }
+        @Override
+        public void release() {
+            if (mem != null) {
+                int ret = CL10.clReleaseMemObject(mem);
+                mem = null;
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
 }

+ 20 - 1
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java

@@ -52,6 +52,7 @@ public class LwjglKernel extends Kernel {
 
     public LwjglKernel(CLKernel kernel) {
         this.kernel = kernel;
+        OpenCLObjectManager.getInstance().registerObject(this);
     }
 
     public CLKernel getKernel() {
@@ -215,5 +216,23 @@ public class LwjglKernel extends Kernel {
         Utils.checkError(ret, "clEnqueueNDRangeKernel");
         return new LwjglEvent(q.getCLEvent(Utils.pointerBuffers[0].get(0)));
     }
-    
+
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(kernel);
+    }
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLKernel kernel;
+        private ReleaserImpl(CLKernel kernel) {
+            this.kernel = kernel;
+        }
+        @Override
+        public void release() {
+            if (kernel != null) {
+                int ret = CL10.clReleaseKernel(kernel);
+                kernel = null;
+                Utils.reportError(ret, "clReleaseKernel");
+            }
+        }
+    }
 }

+ 1 - 1
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java

@@ -123,5 +123,5 @@ public final class LwjglPlatform implements Platform {
     public Collection<? extends String> getExtensions() {
         return Arrays.asList(platform.getInfoString(CL10.CL_PLATFORM_EXTENSIONS).split(" "));
     }
-    
+
 }

+ 24 - 2
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java

@@ -33,8 +33,8 @@ package com.jme3.opencl.lwjgl;
 
 import com.jme3.opencl.Kernel;
 import com.jme3.opencl.KernelCompilationException;
+import com.jme3.opencl.OpenCLObjectManager;
 import com.jme3.opencl.Program;
-import java.util.ArrayList;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.lwjgl.PointerBuffer;
@@ -53,6 +53,7 @@ public class LwjglProgram implements Program {
     public LwjglProgram(CLProgram program, LwjglContext context) {
         this.program = program;
         this.context = context;
+        OpenCLObjectManager.getInstance().registerObject(this);
     }
 
     public CLProgram getProgram() {
@@ -107,5 +108,26 @@ public class LwjglProgram implements Program {
         }
         return kx;
     }
-    
+
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(program);
+    }
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLProgram program;
+        private ReleaserImpl(CLProgram program) {
+            this.program = program;
+        }
+        @Override
+        public void release() {
+            //LWJGL Bug: releasing a program also released every! kernel associated with that program
+            /*
+            if (program != null) {
+                int ret = CL10.clReleaseProgram(program);
+                program = null;
+                Utils.reportError(ret, "clReleaseProgram");
+            }
+            */
+        }
+    }
 }

+ 38 - 5
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/Utils.java

@@ -34,19 +34,32 @@ package com.jme3.opencl.lwjgl;
 import com.jme3.opencl.MappingAccess;
 import com.jme3.opencl.MemoryAccess;
 import com.jme3.opencl.OpenCLException;
+import java.lang.reflect.Field;
 import java.nio.*;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import org.lwjgl.BufferUtils;
 import org.lwjgl.LWJGLUtil;
 import org.lwjgl.PointerBuffer;
-import org.lwjgl.opencl.CL10;
-import org.lwjgl.opencl.CL12;
-import org.lwjgl.opencl.Util;
+import org.lwjgl.opencl.*;
 
 /**
  *
  * @author Sebastian Weiss
  */
 public class Utils {
+    private static final Logger LOG = Logger.getLogger(Utils.class.getName());
+    private Utils() {}
+    
+    /** Maps OpenCL error token values to their String representations. 
+        Taken directly from org.lwjgl.opencl.Util
+     */
+	private static final Map<Integer, String> CL_ERROR_TOKENS = LWJGLUtil.getClassTokens(new LWJGLUtil.TokenFilter() {
+		public boolean accept(final Field field, final int value) {
+			return value < 0; // Currently, all OpenCL errors have negative values.
+		}
+	}, null, CL10.class, CL11.class, CL12.class, KHRGLSharing.class, KHRICD.class, APPLEGLSharing.class, EXTDeviceFission.class);
     
     public static int getMajorVersion(String version, String prefix) {
         String s = version.substring(prefix.length());
@@ -95,8 +108,27 @@ public class Utils {
         checkError(errorBuffer.get(0), callName);
     }
     public static void checkError(int error, String callName) {
-        //TODO: proper handling
-        Util.checkCLError(error);
+        if (error != CL10.CL_SUCCESS) {
+            String errname = getErrorName(error);
+            if (errname == null) {
+                errname = "UNKNOWN";
+            }
+            throw new OpenCLException("OpenCL error in " + callName + ": " + errname + " (0x" + Integer.toHexString(error) + ")", error);
+        }
+    }
+    
+    public static void reportError(int error, String callName) {
+        if (error != CL10.CL_SUCCESS) {
+            String errname = getErrorName(error);
+            if (errname == null) {
+                errname = "UNKNOWN";
+            }
+            LOG.log(Level.WARNING, "OpenCL error in {0}: {1} (0x{2})", new Object[]{callName, errname, Integer.toHexString(error)});
+        }
+    }
+    
+    public static String getErrorName(int code) {
+        return CL_ERROR_TOKENS.get(code);
     }
     
     public static long getMemoryAccessFlags(MemoryAccess ma) {
@@ -129,4 +161,5 @@ public class Utils {
             default: throw new IllegalArgumentException("Unknown mapping access: "+ma);
         }
     }
+
 }