1 /*
2  * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
3  * Copyright (c) 2010 JogAmp Community. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * - Redistribution of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistribution in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32  *
33  * You acknowledge that this software is not designed or intended for use
34  * in the design, construction, operation or maintenance of any nuclear
35  * facility.
36  *
37  * Sun gratefully acknowledges that this software was originally authored
38  * and developed by Kenneth Bradley Russell and Christopher John Kline.
39  */
40 
41 package jogamp.opengl;
42 
43 import com.jogamp.opengl.*;
44 import com.jogamp.common.util.IntIntHashMap;
45 import com.jogamp.common.util.PropertyAccess;
46 
47 /**
48  * Tracks as closely as possible which OpenGL buffer object is bound
49  * to which binding target in the current OpenGL context.
50  * GLBufferStateTracker objects are allocated on a per-OpenGL-context basis.
51  * This class is used to verify that e.g. the vertex
52  * buffer object extension is in use when the glVertexPointer variant
53  * taking a long as argument is called.
54  * <p>
55  * The buffer binding state is local to it's OpenGL context,
56  * i.e. not shared across multiple OpenGL context.
57  * Hence this code is thread safe due to no multithreading usage.
58  * </p>
59  * <p>
60  * Note that because the enumerated value used for the binding of a
61  * buffer object (e.g. GL_ARRAY_BUFFER) is different than that used to
62  * query the binding using glGetIntegerv (e.g.
63  * GL_ARRAY_BUFFER_BINDING), then in the face of new binding targets
64  * being added to the GL (e.g. GL_TRANSFORM_FEEDBACK_BUFFER_NV) it is
65  * impossible to set up a query of the buffer object currently bound
66  * to a particular state. It turns out that for some uses, such as
67  * finding the size of the currently bound buffer, this doesn't
68  * matter, though of course without knowing the buffer object we can't
69  * re-associate the queried size with the buffer object ID.
70  * </p>
71  * <p>
72  * For <i>unknown</i> targets such as GL_TRANSFORM_FEEDBACK_BUFFER_NV we return
73  * false from this, but we also clear the valid bit and later refresh
74  * the binding state if glPushClientAttrib / glPopClientAttrib are
75  * called, since we don't want the complexity of tracking stacks of
76  * these attributes.
77  * </p>
78  */
79 
80 public class GLBufferStateTracker {
81   protected static final boolean DEBUG;
82 
83   static {
Debug.initSingleton()84       Debug.initSingleton();
85       DEBUG = PropertyAccess.isPropertyDefined("jogl.debug.GLBufferStateTracker", true);
86   }
87 
88   // Maps binding targets to buffer objects. A null value indicates
89   // that the binding is unknown. A zero value indicates that it is
90   // known that no buffer is bound to the target, according to the
91   // OpenGL specifications.
92   // http://www.opengl.org/sdk/docs/man/xhtml/glBindBuffer.xml
93   private final IntIntHashMap bindingMap;
94   private static final int bindingNotFound = 0xFFFFFFFF;
95 
96   private final int[] bufTmp = new int[1];
97 
GLBufferStateTracker()98   public GLBufferStateTracker() {
99     bindingMap = new IntIntHashMap();
100     bindingMap.setKeyNotFoundValue(bindingNotFound);
101 
102     // Start with known unbound targets for known keys
103     // setBoundBufferObject(GL2ES3.GL_VERTEX_ARRAY_BINDING, 0); // not using default VAO (removed in GL3 core) - only explicit
104     setBoundBufferObject(GL.GL_ARRAY_BUFFER,         0);
105     setBoundBufferObject(GL3ES3.GL_DRAW_INDIRECT_BUFFER, 0);
106     setBoundBufferObject(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
107     setBoundBufferObject(GL2ES3.GL_PIXEL_PACK_BUFFER,   0);
108     setBoundBufferObject(GL2ES3.GL_PIXEL_UNPACK_BUFFER, 0);
109   }
110 
111 
112   /**
113    *  GL_ARRAY_BUFFER​,
114    *  GL_ATOMIC_COUNTER_BUFFER​,
115    *  GL_COPY_READ_BUFFER​,
116    *  GL_COPY_WRITE_BUFFER​,
117    *  GL_DRAW_INDIRECT_BUFFER​,
118    *  GL_DISPATCH_INDIRECT_BUFFER​,
119    *  GL_ELEMENT_ARRAY_BUFFER​,
120    *  GL_PIXEL_PACK_BUFFER​,
121    *  GL_PIXEL_UNPACK_BUFFER​,
122    *  GL_SHADER_STORAGE_BUFFER​,
123    *  GL_TEXTURE_BUFFER​,
124    *  GL_TRANSFORM_FEEDBACK_BUFFER​ or
125    *  GL_UNIFORM_BUFFER​.
126    *
127    *  GL_VERTEX_ARRAY_BINDING
128    *
129    */
getQueryName(final int target)130   private static final int getQueryName(final int target) {
131       switch (target) {
132         case GL.GL_ARRAY_BUFFER:                  return GL.GL_ARRAY_BUFFER_BINDING;
133         case GL2ES3.GL_ATOMIC_COUNTER_BUFFER:     return GL2ES3.GL_ATOMIC_COUNTER_BUFFER_BINDING;
134         case GL2ES3.GL_COPY_READ_BUFFER:          return GL2ES3.GL_COPY_READ_BUFFER_BINDING;
135         case GL2ES3.GL_COPY_WRITE_BUFFER:         return GL2ES3.GL_COPY_WRITE_BUFFER_BINDING;
136         case GL3ES3.GL_DRAW_INDIRECT_BUFFER:      return GL3ES3.GL_DRAW_INDIRECT_BUFFER_BINDING;
137         case GL3ES3.GL_DISPATCH_INDIRECT_BUFFER:  return GL3ES3.GL_DISPATCH_INDIRECT_BUFFER_BINDING;
138         case GL.GL_ELEMENT_ARRAY_BUFFER:          return GL.GL_ELEMENT_ARRAY_BUFFER_BINDING;
139         case GL2ES3.GL_PIXEL_PACK_BUFFER:         return GL2ES3.GL_PIXEL_PACK_BUFFER_BINDING;
140         case GL2ES3.GL_PIXEL_UNPACK_BUFFER:       return GL2ES3.GL_PIXEL_UNPACK_BUFFER_BINDING;
141         case GL4.GL_QUERY_BUFFER:                 return GL4.GL_QUERY_BUFFER_BINDING;
142         case GL3ES3.GL_SHADER_STORAGE_BUFFER:     return GL3ES3.GL_SHADER_STORAGE_BUFFER_BINDING;
143         case GL2GL3.GL_TEXTURE_BUFFER:            return GL2GL3.GL_TEXTURE_BINDING_BUFFER;
144         case GL2ES3.GL_TRANSFORM_FEEDBACK_BUFFER: return GL2ES3.GL_TRANSFORM_FEEDBACK_BUFFER_BINDING;
145         case GL2ES3.GL_UNIFORM_BUFFER:            return GL2ES3.GL_UNIFORM_BUFFER_BINDING;
146 
147         case GL2ES3.GL_VERTEX_ARRAY_BINDING:      return GL2ES3.GL_VERTEX_ARRAY_BINDING;
148 
149         default:
150             throw new GLException(String.format("GL_INVALID_ENUM​: Invalid binding target 0x%X", target));
151       }
152   }
checkTargetName(final int target)153   private static final void checkTargetName(final int target) {
154       switch (target) {
155         case GL.GL_ARRAY_BUFFER:
156         case GL2ES3.GL_ATOMIC_COUNTER_BUFFER:
157         case GL2ES3.GL_COPY_READ_BUFFER:
158         case GL2ES3.GL_COPY_WRITE_BUFFER:
159         case GL3ES3.GL_DRAW_INDIRECT_BUFFER:
160         case GL3ES3.GL_DISPATCH_INDIRECT_BUFFER:
161         case GL.GL_ELEMENT_ARRAY_BUFFER:
162         case GL2ES3.GL_PIXEL_PACK_BUFFER:
163         case GL2ES3.GL_PIXEL_UNPACK_BUFFER:
164         case GL4.GL_QUERY_BUFFER:
165         case GL3ES3.GL_SHADER_STORAGE_BUFFER:
166         case GL2GL3.GL_TEXTURE_BUFFER:
167         case GL2ES3.GL_TRANSFORM_FEEDBACK_BUFFER:
168         case GL2ES3.GL_UNIFORM_BUFFER:
169 
170         case GL2ES3.GL_VERTEX_ARRAY_BINDING:
171             return;
172 
173         default:
174             throw new GLException(String.format("GL_INVALID_ENUM​: Invalid binding target 0x%X", target));
175       }
176   }
177 
178   /**
179    * Must be called when binding a buffer, e.g.:
180    * <ul>
181    *   <li><code>glBindBuffer</code></li>
182    *   <li><code>glBindBufferBase</code></li>
183    *   <li><code>glBindBufferRange</code></li>
184    * </ul>
185    * @param target
186    * @param bufferName
187    */
setBoundBufferObject(final int target, final int bufferName)188   public final void setBoundBufferObject(final int target, final int bufferName) {
189     checkTargetName(target);
190     final int oldBufferName = bindingMap.put(target, bufferName);
191     /***
192      * Test for clearing bound buffer states when unbinding VAO,
193      * Bug 692 Comment 5 is invalid, i.e. <https://jogamp.org/bugzilla/show_bug.cgi?id=692#c5>.
194      * However spec doesn't mention such behavior, and rendering w/ CPU sourced data
195      * after unbinding a VAO w/o unbinding the VBOs resulted to no visible image.
196      * Leaving code in here for discussion - in case I am wrong.
197      *
198     final int pre = bindingMap.put(target, bufferName);
199     if( GL2ES3.GL_VERTEX_ARRAY_BINDING == target && keyNotFound != pre && 0 == bufferName ) {
200         // Unbinding a previous bound VAO leads to unbinding of all buffers!
201         bindingMap.put(GL.GL_ARRAY_BUFFER,         0);
202         bindingMap.put(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
203         bindingMap.put(GL2.GL_PIXEL_PACK_BUFFER,   0);
204         bindingMap.put(GL2.GL_PIXEL_UNPACK_BUFFER, 0);
205         bindingMap.put(GL4.GL_DRAW_INDIRECT_BUFFER, 0);
206     } */
207     if (DEBUG) {
208       System.err.println("GLBufferStateTracker.setBoundBufferObject() target " +
209                          toHexString(target) + ": " + toHexString(oldBufferName) + " -> " + toHexString(bufferName));
210       // Thread.dumpStack();
211     }
212   }
213 
214   /** Note: returns an unspecified value if the binding for the
215       specified target (e.g. GL_ARRAY_BUFFER) is currently unknown.
216       You must use isBoundBufferObjectKnown() to see whether the
217       return value is valid. */
getBoundBufferObject(final int target, final GL caller)218   public final int getBoundBufferObject(final int target, final GL caller) {
219     int value = bindingMap.get(target);
220     if (bindingNotFound == value) {
221       // User probably either called glPushClientAttrib /
222       // glPopClientAttrib or is querying an unknown target. See
223       // whether we know how to fetch this state.
224       final int queryTarget = getQueryName(target);
225       if ( 0 != queryTarget ) {
226         final int glerrPre = caller.glGetError(); // clear
227         caller.glGetIntegerv(queryTarget, bufTmp, 0);
228         final int glerrPost = caller.glGetError(); // be safe, e.g. GL '3.0 Mesa 8.0.4' may produce an error querying GL_PIXEL_UNPACK_BUFFER_BINDING, ignore value
229         if(GL.GL_NO_ERROR == glerrPost) {
230             value = bufTmp[0];
231         } else {
232             value = 0;
233         }
234         if (DEBUG) {
235           System.err.println("GLBufferStateTracker.getBoundBufferObject() glerr[pre "+toHexString(glerrPre)+", post "+toHexString(glerrPost)+"], [queried value]: target " +
236                              toHexString(target) + " / query "+toHexString(queryTarget)+
237                              " -> mapped bound buffer " + toHexString(value));
238         }
239         setBoundBufferObject(target, value);
240         return value;
241       }
242       return 0;
243     }
244     if (DEBUG) {
245       System.err.println("GLBufferStateTracker.getBoundBufferObject() [mapped value]: target 0x" +
246                          Integer.toHexString(target) + " -> mapped bound buffer 0x" +
247                          Integer.toHexString(value));
248     }
249     return value;
250   }
251 
252   /** Clears out the known/unknown state of the various buffer object
253       binding states. These will be refreshed later on an as-needed
254       basis. This is called by the implementations of
255       glPushClientAttrib / glPopClientAttrib. Might want to call this
256       from GLContext.makeCurrent() in the future to possibly increase
257       the robustness of these caches in the face of external native
258       code manipulating OpenGL state. */
clear()259   public final void clear() {
260     if (DEBUG) {
261       System.err.println("GLBufferStateTracker.clear() - Thread "+Thread.currentThread().getName());
262       // Thread.dumpStack();
263     }
264     bindingMap.clear();
265   }
toHexString(final int i)266   private final String toHexString(final int i) { return Integer.toHexString(i); }
267 }
268