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