001/*
002 * JavaCL - Java API and utilities for OpenCL
003 * http://javacl.googlecode.com/
004 *
005 * Copyright (c) 2009-2013, Olivier Chafik (http://ochafik.com/)
006 * All rights reserved.
007 *
008 * Redistribution and use in source and binary forms, with or without
009 * modification, are permitted provided that the following conditions are met:
010 * 
011 *     * Redistributions of source code must retain the above copyright
012 *       notice, this list of conditions and the following disclaimer.
013 *     * Redistributions in binary form must reproduce the above copyright
014 *       notice, this list of conditions and the following disclaimer in the
015 *       documentation and/or other materials provided with the distribution.
016 *     * Neither the name of Olivier Chafik nor the
017 *       names of its contributors may be used to endorse or promote products
018 *       derived from this software without specific prior written permission.
019 * 
020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY
021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030 */
031        
032
033
034
035
036        
037
038        
039        
040
041
042
043        
044
045        
046        
047        
048
049        
050package com.nativelibs4java.opencl;
051import static com.nativelibs4java.opencl.CLException.error;
052import static com.nativelibs4java.opencl.JavaCL.CL;
053import static com.nativelibs4java.opencl.library.IOpenCLLibrary.CL_FALSE;
054import static com.nativelibs4java.opencl.library.IOpenCLLibrary.CL_QUEUE_PROPERTIES;
055import static com.nativelibs4java.opencl.library.IOpenCLLibrary.CL_TRUE;
056
057import java.util.EnumSet;
058
059import com.nativelibs4java.opencl.library.IOpenCLLibrary.cl_command_queue;
060import com.nativelibs4java.opencl.library.IOpenCLLibrary.cl_event;
061import com.nativelibs4java.opencl.library.IOpenCLLibrary.cl_mem;
062import org.bridj.*;
063import static org.bridj.Pointer.*;
064
065/**
066 * OpenCL command queue.<br>
067 * OpenCL objects such as memory, program and kernel objects are created using a context. <br>
068 * Operations on these objects are performed using a command-queue. <br>
069 * The command-queue can be used to queue a set of operations (referred to as commands) in order. <br>
070 * Having multiple command-queues allows applications to queue multiple independent commands without requiring synchronization. <br>
071 * Note that this should work as long as these objects are not being shared. <br>
072 * Sharing of objects across multiple command-queues will require the application to perform appropriate synchronization.<br>
073 * <br>
074 * A queue is bound to a single device.
075 * see {@link CLDevice#createQueue(com.nativelibs4java.opencl.CLContext, com.nativelibs4java.opencl.CLDevice.QueueProperties[]) } 
076 * see {@link CLDevice#createOutOfOrderQueue(com.nativelibs4java.opencl.CLContext) }
077 * see {@link CLDevice#createProfilingQueue(com.nativelibs4java.opencl.CLContext) }
078 * see {@link CLContext#createDefaultQueue(com.nativelibs4java.opencl.CLDevice.QueueProperties[]) }
079 * see {@link CLContext#createDefaultOutOfOrderQueue() }
080 * see {@link CLContext#createDefaultProfilingQueue() }
081 * @author Olivier Chafik
082 *
083 */
084public class CLQueue extends CLAbstractEntity {
085
086        protected static CLInfoGetter infos = new CLInfoGetter() {
087                @Override
088                protected int getInfo(long entity, int infoTypeEnum, long size, Pointer out, Pointer<SizeT> sizeOut) {
089                        return CL.clGetCommandQueueInfo(entity, infoTypeEnum, size, getPeer(out), getPeer(sizeOut));
090                }
091        };
092
093        final CLContext context;
094        final CLDevice device;
095
096    CLQueue(CLContext context, long entity, CLDevice device) {
097        super(entity);
098        this.context = context;
099                this.device = device;
100    }
101    
102    public CLContext getContext() {
103        return context;
104    }
105        public CLDevice getDevice() {
106                return device;
107        }
108        
109        volatile Boolean outOfOrder;
110        public synchronized boolean isOutOfOrder() {
111                if (outOfOrder == null)
112                        outOfOrder = getProperties().contains(CLDevice.QueueProperties.OutOfOrderExecModeEnable);
113                return outOfOrder;
114        }
115
116        @InfoName("CL_QUEUE_PROPERTIES")
117        public EnumSet<CLDevice.QueueProperties> getProperties() {
118                return CLDevice.QueueProperties.getEnumSet(infos.getIntOrLong(getEntity(), CL_QUEUE_PROPERTIES));
119        }
120
121        @SuppressWarnings("deprecation")
122        public void setProperty(CLDevice.QueueProperties property, boolean enabled) {
123                context.getPlatform().requireMinVersionValue("clSetCommandQueueProperty", 1.0, 1.1);
124                error(CL.clSetCommandQueueProperty(getEntity(), property.value(), enabled ? CL_TRUE : CL_FALSE, 0));
125        }
126        
127
128    @Override
129    protected void clear() {
130        error(CL.clReleaseCommandQueue(getEntity()));
131    }
132
133    /**
134  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clFinish.html">clFinish</a>.<br>
135         * Blocks until all previously queued OpenCL commands in this queue are issued to the associated device and have completed. <br>
136         * finish() does not return until all queued commands in this queue have been processed and completed. <br>
137         * finish() is also a synchronization point.
138         */
139    public void finish() {
140        error(CL.clFinish(getEntity()));
141    }
142
143    /**
144  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clFlush.html">clFlush</a>.<br>
145         * Issues all previously queued OpenCL commands in this queue to the device associated with this queue. <br>
146         * flush() only guarantees that all queued commands in this queue get issued to the appropriate device. <br>
147         * There is no guarantee that they will be complete after flush() returns.
148         */
149    public void flush() {
150        error(CL.clFlush(getEntity()));
151    }
152
153        /**
154  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueWaitForEvents.html">clEnqueueWaitForEvents</a>.<br>
155         * Enqueues a wait for a specific event or a list of events to complete before any future commands queued in the this queue are executed.
156         */
157        public void enqueueWaitForEvents(CLEvent... eventsToWaitFor) {
158                context.getPlatform().requireMinVersionValue("clEnqueueWaitForEvents", 1.1, 1.2);
159                        ReusablePointers ptrs = ReusablePointers.get();
160                        int[] eventsInCount = ptrs.int1Array;
161        Pointer<cl_event> eventsIn = 
162                CLAbstractEntity.copyNonNullEntities(eventsToWaitFor, eventsInCount, ptrs.events_in);
163        if (eventsIn == null)
164            return;
165        error(CL.clEnqueueWaitForEvents(getEntity(),  eventsInCount[0], getPeer(eventsIn) ));
166        }
167        
168
169        /**
170  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueMigrateMemObjects.html">clEnqueueMigrateMemObjects</a>.<br>
171         * Enqueues a command to indicate which device a set of memory objects should be associated with.
172         */
173        public CLEvent enqueueMigrateMemObjects(CLMem[] memObjects, EnumSet<CLMem.Migration> flags, CLEvent... eventsToWaitFor) {
174                context.getPlatform().requireMinVersionValue("clEnqueueMigrateMemObjects", 1.2);
175                                ReusablePointers ptrs = ReusablePointers.get();
176                int[] eventsInCount = ptrs.int1Array;
177        Pointer<cl_event> eventsIn = 
178                CLAbstractEntity.copyNonNullEntities(eventsToWaitFor, eventsInCount, ptrs.events_in);
179                Pointer<cl_event> eventOut = 
180                eventsToWaitFor == null || CLEvent.containsFireAndForget(eventsToWaitFor) 
181                ? null 
182                : ptrs.event_out;
183                int[] n = ptrs.int1Array;
184                Pointer<SizeT> pMems = pointerToEntities(memObjects, n);
185                error(CL.clEnqueueMigrateMemObjects(
186                        getEntity(),
187                        n[0],
188                        getPeer(pMems),
189                        CLMem.Migration.getValue(flags),
190                          eventsInCount[0], getPeer(eventsIn) ,  getPeer(eventOut)              ));
191                 return  CLEvent.createEventFromPointer(this, eventOut) ;       }
192
193        // 
194        public interface NativeKernel {
195                void execute(Pointer[] bufferPointers);
196        }
197
198        private static Pointer<SizeT> pointerToEntities(CLAbstractEntity[] entities, int[] n) {
199                int nn = 0;
200                Pointer<SizeT> pEntities = allocateSizeTs(entities.length);
201                for (CLAbstractEntity entity : entities) {
202                        if (entity != null) {
203                                pEntities.setSizeTAtIndex(nn++, entity.getEntity());
204                        }
205                }
206                n[0] = nn;
207                return pEntities;
208        }
209        /**
210  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueNativeKernel.html">clEnqueueNativeKernel</a>.<br>
211         * Enqueues a command to execute a Java callback with direct access to buffer memory.
212         */
213        /*
214        public CLEvent enqueueNativeKernel(NativeKernel kernel, CLMem[] buffers, CLEvent... eventsToWaitFor) {
215                // TODO check 1.1 or 1.2?
216                context.getPlatform().requireMinVersionValue("clEnqueueNativeKernel", 1.2);
217                                ReusablePointers ptrs = ReusablePointers.get();
218                int[] eventsInCount = ptrs.int1Array;
219        Pointer<cl_event> eventsIn = 
220                CLAbstractEntity.copyNonNullEntities(eventsToWaitFor, eventsInCount, ptrs.events_in);
221                Pointer<cl_event> eventOut = 
222                eventsToWaitFor == null || CLEvent.containsFireAndForget(eventsToWaitFor) 
223                ? null 
224                : ptrs.event_out;
225                int[] n = ptrs.int1Array;
226                Pointer<SizeT> pMems = pointerToEntities(buffers, n);
227                error(CL.clEnqueueNativeKernel(
228                        getEntity(),
229                        n[0],
230                        getPeer(pMems),
231                        CLMem.Migration.getValue(flags),
232                          eventsInCount[0], getPeer(eventsIn) ,  getPeer(eventOut)              ));
233                 return  CLEvent.createEventFromPointer(this, eventOut) ;       }
234        */
235
236        /**
237  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueBarrierWithWaitList.html">clEnqueueBarrierWithWaitList</a>.<br>
238         * Enqueue a barrier operation.<br>
239         * The enqueueBarrier() command ensures that all queued commands in command_queue have finished execution before the next batch of commands can begin execution. <br>
240         * enqueueBarrier() is a synchronization point.
241         * @param eventsToWaitFor Events that need to complete before this particular command can be executed. Special value {@link CLEvent#FIRE_AND_FORGET} can be used to avoid returning a CLEvent.  
242     * @return Event object that identifies this command and can be used to query or queue a wait for the command to complete, or null if eventsToWaitFor contains {@link CLEvent#FIRE_AND_FORGET}.
243         */
244        public CLEvent enqueueBarrier(CLEvent... eventsToWaitFor) {
245                if (context.getPlatform().getVersionValue() >= 1.2 ||
246                        eventsToWaitFor != null && eventsToWaitFor.length > 0)
247                {
248                        context.getPlatform().requireMinVersionValue("clEnqueueBarrierWithWaitList", 1.2);
249                                        ReusablePointers ptrs = ReusablePointers.get();
250                int[] eventsInCount = ptrs.int1Array;
251        Pointer<cl_event> eventsIn = 
252                CLAbstractEntity.copyNonNullEntities(eventsToWaitFor, eventsInCount, ptrs.events_in);
253                Pointer<cl_event> eventOut = 
254                eventsToWaitFor == null || CLEvent.containsFireAndForget(eventsToWaitFor) 
255                ? null 
256                : ptrs.event_out;
257                        error(CL.clEnqueueBarrierWithWaitList(
258                                getEntity(),
259                                  eventsInCount[0], getPeer(eventsIn) ,  getPeer(eventOut)                      ));
260                         return  CLEvent.createEventFromPointer(this, eventOut) ;               } else {
261                        context.getPlatform().requireMinVersionValue("clEnqueueBarrier", 1.1, 1.2);
262                        error(CL.clEnqueueBarrier(getEntity()));
263                        return null;
264                }
265        }
266
267        /**
268  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueMarkerWithWaitList.html">clEnqueueMarkerWithWaitList</a>.<br>
269         * Enqueue a marker command to command_queue. <br>
270         * The marker command returns an event which can be used by to queue a wait on this marker event i.e. wait for all commands queued before the marker command to complete.
271         * @param eventsToWaitFor Events that need to complete before this particular command can be executed. Special value {@link CLEvent#FIRE_AND_FORGET} can be used to avoid returning a CLEvent.  
272     * @return Event object that identifies this command and can be used to query or queue a wait for the command to complete, or null if eventsToWaitFor contains {@link CLEvent#FIRE_AND_FORGET}.
273         */
274        @Deprecated
275        public CLEvent enqueueMarker(CLEvent... eventsToWaitFor) {
276                if (context.getPlatform().getVersionValue() >= 1.2 ||
277                        eventsToWaitFor != null && eventsToWaitFor.length > 0)
278                {
279                        context.getPlatform().requireMinVersionValue("clEnqueueMarkerWithWaitList", 1.2);
280                                        ReusablePointers ptrs = ReusablePointers.get();
281                int[] eventsInCount = ptrs.int1Array;
282        Pointer<cl_event> eventsIn = 
283                CLAbstractEntity.copyNonNullEntities(eventsToWaitFor, eventsInCount, ptrs.events_in);
284                Pointer<cl_event> eventOut = 
285                eventsToWaitFor == null || CLEvent.containsFireAndForget(eventsToWaitFor) 
286                ? null 
287                : ptrs.event_out;
288                error(CL.clEnqueueMarkerWithWaitList(
289                                getEntity(),
290                                  eventsInCount[0], getPeer(eventsIn) ,  getPeer(eventOut)                      ));
291                         return  CLEvent.createEventFromPointer(this, eventOut) ;               } else {
292                        context.getPlatform().requireMinVersionValue("clEnqueueMarker", 1.1, 1.2);
293                                ReusablePointers ptrs = ReusablePointers.get();
294                        Pointer<cl_event> eventOut = ptrs.event_out;
295                        error(CL.clEnqueueMarker(getEntity(), getPeer(eventOut)));
296                         return  CLEvent.createEventFromPointer(this, eventOut) ;               }
297        }
298
299        /**
300  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueAcquireGLObjects.html">clEnqueueAcquireGLObjects</a>.<br>
301         * Used to acquire OpenCL memory objects that have been created from OpenGL objects. <br>
302         * These objects need to be acquired before they can be used by any OpenCL commands queued to a command-queue. <br>
303         * The OpenGL objects are acquired by the OpenCL context associated with this queue and can therefore be used by all command-queues associated with the OpenCL context.
304         * @param objects CL memory objects that correspond to GL objects.
305         * @param eventsToWaitFor Events that need to complete before this particular command can be executed. Special value {@link CLEvent#FIRE_AND_FORGET} can be used to avoid returning a CLEvent.  
306     * @return Event object that identifies this command and can be used to query or queue a wait for the command to complete, or null if eventsToWaitFor contains {@link CLEvent#FIRE_AND_FORGET}.
307         */
308        public CLEvent enqueueAcquireGLObjects(CLMem[] objects, CLEvent... eventsToWaitFor) {
309                        ReusablePointers ptrs = ReusablePointers.get();
310                int[] eventsInCount = ptrs.int1Array;
311        Pointer<cl_event> eventsIn = 
312                CLAbstractEntity.copyNonNullEntities(eventsToWaitFor, eventsInCount, ptrs.events_in);
313                Pointer<cl_event> eventOut = 
314                eventsToWaitFor == null || CLEvent.containsFireAndForget(eventsToWaitFor) 
315                ? null 
316                : ptrs.event_out;
317                Pointer<SizeT> mems = allocateSizeTs(objects.length);
318                for (int i = 0; i < objects.length; i++) {
319                        mems.setSizeTAtIndex(i, objects[i].getEntity());
320                }
321        error(CL.clEnqueueAcquireGLObjects(
322                        getEntity(), 
323                        objects.length,
324                        getPeer(mems),
325                          eventsInCount[0], getPeer(eventsIn) ,  getPeer(eventOut)              ));
326                 return  CLEvent.createEventFromPointer(this, eventOut) ;       }
327
328        /**
329  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueReleaseGLObjects.html">clEnqueueReleaseGLObjects</a>.<br>
330         * Used to release OpenCL memory objects that have been created from OpenGL objects. <br>
331         * These objects need to be released before they can be used by OpenGL. <br>
332         * The OpenGL objects are released by the OpenCL context associated with this queue.
333         * @param objects CL memory objects that correpond to GL objects.
334         * @param eventsToWaitFor Events that need to complete before this particular command can be executed. Special value {@link CLEvent#FIRE_AND_FORGET} can be used to avoid returning a CLEvent.  
335     * @return Event object that identifies this command and can be used to query or queue a wait for the command to complete, or null if eventsToWaitFor contains {@link CLEvent#FIRE_AND_FORGET}.
336         */
337        public CLEvent enqueueReleaseGLObjects(CLMem[] objects, CLEvent... eventsToWaitFor) {
338                        ReusablePointers ptrs = ReusablePointers.get();
339                int[] eventsInCount = ptrs.int1Array;
340        Pointer<cl_event> eventsIn = 
341                CLAbstractEntity.copyNonNullEntities(eventsToWaitFor, eventsInCount, ptrs.events_in);
342                Pointer<cl_event> eventOut = 
343                eventsToWaitFor == null || CLEvent.containsFireAndForget(eventsToWaitFor) 
344                ? null 
345                : ptrs.event_out;
346                Pointer<?> mems = getEntities(objects, (Pointer)allocateSizeTs(objects.length));
347        error(CL.clEnqueueReleaseGLObjects(
348                        getEntity(), 
349                        objects.length, 
350                        getPeer(mems),
351                          eventsInCount[0], getPeer(eventsIn) ,  getPeer(eventOut)              ));
352                 return  CLEvent.createEventFromPointer(this, eventOut) ;       }
353}