WebGLContextBuffers.cpp (8237B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "ClientWebGLContext.h" 7 #include "GLContext.h" 8 #include "WebGLBuffer.h" 9 #include "WebGLContext.h" 10 #include "WebGLTransformFeedback.h" 11 #include "WebGLVertexArray.h" 12 13 namespace mozilla { 14 15 RefPtr<WebGLBuffer>* WebGLContext::ValidateBufferSlot(GLenum target) { 16 RefPtr<WebGLBuffer>* slot = nullptr; 17 18 switch (target) { 19 case LOCAL_GL_ARRAY_BUFFER: 20 slot = &mBoundArrayBuffer; 21 break; 22 23 case LOCAL_GL_ELEMENT_ARRAY_BUFFER: 24 slot = &(mBoundVertexArray->mElementArrayBuffer); 25 break; 26 } 27 28 if (IsWebGL2()) { 29 switch (target) { 30 case LOCAL_GL_COPY_READ_BUFFER: 31 slot = &mBoundCopyReadBuffer; 32 break; 33 34 case LOCAL_GL_COPY_WRITE_BUFFER: 35 slot = &mBoundCopyWriteBuffer; 36 break; 37 38 case LOCAL_GL_PIXEL_PACK_BUFFER: 39 slot = &mBoundPixelPackBuffer; 40 break; 41 42 case LOCAL_GL_PIXEL_UNPACK_BUFFER: 43 slot = &mBoundPixelUnpackBuffer; 44 break; 45 46 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: 47 slot = &mBoundTransformFeedbackBuffer; 48 break; 49 50 case LOCAL_GL_UNIFORM_BUFFER: 51 slot = &mBoundUniformBuffer; 52 break; 53 } 54 } 55 56 if (!slot) { 57 ErrorInvalidEnumInfo("target", target); 58 return nullptr; 59 } 60 61 return slot; 62 } 63 64 WebGLBuffer* WebGLContext::ValidateBufferSelection(GLenum target) const { 65 const auto& slot = 66 const_cast<WebGLContext*>(this)->ValidateBufferSlot(target); 67 if (!slot) return nullptr; 68 const auto& buffer = *slot; 69 70 if (!buffer) { 71 ErrorInvalidOperation("Buffer for `target` is null."); 72 return nullptr; 73 } 74 75 if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) { 76 if (mBoundTransformFeedback->IsActiveAndNotPaused()) { 77 ErrorInvalidOperation( 78 "Cannot select TRANSFORM_FEEDBACK_BUFFER when" 79 " transform feedback is active and unpaused."); 80 return nullptr; 81 } 82 const auto tfBuffers = std::vector<webgl::BufferAndIndex>{{ 83 {buffer}, 84 }}; 85 86 if (!ValidateBuffersForTf(tfBuffers)) return nullptr; 87 } else { 88 if (mBoundTransformFeedback && !ValidateBufferForNonTf(buffer, target)) 89 return nullptr; 90 } 91 92 return buffer.get(); 93 } 94 95 IndexedBufferBinding* WebGLContext::ValidateIndexedBufferSlot(GLenum target, 96 GLuint index) { 97 decltype(mIndexedUniformBufferBindings)* bindings; 98 const char* maxIndexEnum; 99 switch (target) { 100 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: 101 if (mBoundTransformFeedback->mIsActive && 102 !mBoundTransformFeedback->mIsPaused) { 103 ErrorInvalidOperation("Transform feedback active and not paused."); 104 return nullptr; 105 } 106 bindings = &(mBoundTransformFeedback->mIndexedBindings); 107 maxIndexEnum = "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS"; 108 break; 109 110 case LOCAL_GL_UNIFORM_BUFFER: 111 bindings = &mIndexedUniformBufferBindings; 112 maxIndexEnum = "MAX_UNIFORM_BUFFER_BINDINGS"; 113 break; 114 115 default: 116 ErrorInvalidEnumInfo("target", target); 117 return nullptr; 118 } 119 120 if (index >= bindings->size()) { 121 ErrorInvalidValue("`index` >= %s.", maxIndexEnum); 122 return nullptr; 123 } 124 125 return &(*bindings)[index]; 126 } 127 128 //////////////////////////////////////// 129 130 void WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer) { 131 FuncScope funcScope(*this, "bindBuffer"); 132 if (IsContextLost()) return; 133 funcScope.mBindFailureGuard = true; 134 135 if (buffer && !ValidateObject("buffer", *buffer)) return; 136 137 const auto& slot = ValidateBufferSlot(target); 138 if (!slot) return; 139 140 if (buffer && !buffer->ValidateCanBindToTarget(target)) return; 141 142 if (!IsBufferTargetLazilyBound(target)) { 143 gl->fBindBuffer(target, buffer ? buffer->mGLName : 0); 144 } 145 146 *slot = buffer; 147 if (buffer) { 148 buffer->SetContentAfterBind(target); 149 } 150 151 funcScope.mBindFailureGuard = false; 152 } 153 154 //////////////////////////////////////// 155 156 bool WebGLContext::ValidateIndexedBufferBinding( 157 GLenum target, GLuint index, RefPtr<WebGLBuffer>** const out_genericBinding, 158 IndexedBufferBinding** const out_indexedBinding) { 159 *out_genericBinding = ValidateBufferSlot(target); 160 if (!*out_genericBinding) return false; 161 162 *out_indexedBinding = ValidateIndexedBufferSlot(target, index); 163 if (!*out_indexedBinding) return false; 164 165 if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER && 166 mBoundTransformFeedback->mIsActive) { 167 ErrorInvalidOperation( 168 "Cannot update indexed buffer bindings on active" 169 " transform feedback objects."); 170 return false; 171 } 172 173 return true; 174 } 175 176 void WebGLContext::BindBufferRange(GLenum target, GLuint index, 177 WebGLBuffer* buffer, uint64_t offset, 178 uint64_t size) { 179 FuncScope funcScope(*this, "bindBufferBase/Range"); 180 if (buffer && !ValidateObject("buffer", *buffer)) return; 181 funcScope.mBindFailureGuard = true; 182 183 RefPtr<WebGLBuffer>* genericBinding; 184 IndexedBufferBinding* indexedBinding; 185 if (!ValidateIndexedBufferBinding(target, index, &genericBinding, 186 &indexedBinding)) { 187 return; 188 } 189 190 if (buffer && !buffer->ValidateCanBindToTarget(target)) return; 191 192 const auto& limits = Limits(); 193 auto err = 194 CheckBindBufferRange(target, index, bool(buffer), offset, size, limits); 195 if (err) return; 196 197 //// 198 199 bool needsPrebind = false; 200 needsPrebind |= gl->IsANGLE(); 201 #ifdef XP_MACOSX 202 needsPrebind = true; 203 #endif 204 205 if (gl->WorkAroundDriverBugs() && buffer && needsPrebind) { 206 // BindBufferBase/Range will fail (on some drivers) if the buffer name has 207 // never been bound. (GenBuffers makes a name, but BindBuffer initializes 208 // that name as a real buffer object) 209 gl->fBindBuffer(target, buffer->mGLName); 210 } 211 212 if (size) { 213 gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, 214 size); 215 } else { 216 gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0); 217 } 218 219 if (buffer) { 220 gl->fBindBuffer(target, 0); // Reset generic. 221 } 222 223 //// 224 225 *genericBinding = buffer; 226 indexedBinding->mBufferBinding = buffer; 227 indexedBinding->mRangeStart = offset; 228 indexedBinding->mRangeSize = size; 229 230 if (buffer) { 231 buffer->SetContentAfterBind(target); 232 } 233 234 funcScope.mBindFailureGuard = false; 235 } 236 237 //////////////////////////////////////// 238 239 void WebGLContext::BufferData(GLenum target, uint64_t dataLen, 240 const uint8_t* data, GLenum usage) const { 241 // `data` may be null. 242 const FuncScope funcScope(*this, "bufferData"); 243 if (IsContextLost()) return; 244 245 const auto& buffer = ValidateBufferSelection(target); 246 if (!buffer) return; 247 248 buffer->BufferData(target, dataLen, data, usage); 249 } 250 251 void WebGLContext::UninitializedBufferData_SizeOnly(GLenum target, 252 uint64_t dataLen, 253 GLenum usage) const { 254 const FuncScope funcScope(*this, "bufferData"); 255 if (IsContextLost()) return; 256 257 const auto& buffer = ValidateBufferSelection(target); 258 if (!buffer) return; 259 260 buffer->BufferData(target, dataLen, nullptr, usage, true); 261 } 262 263 //////////////////////////////////////// 264 265 void WebGLContext::BufferSubData(GLenum target, uint64_t dstByteOffset, 266 uint64_t dataLen, const uint8_t* data, 267 bool unsynchronized) const { 268 MOZ_ASSERT(data || !dataLen); 269 const FuncScope funcScope(*this, "bufferSubData"); 270 if (IsContextLost()) return; 271 272 const auto& buffer = ValidateBufferSelection(target); 273 if (!buffer) return; 274 buffer->BufferSubData(target, dstByteOffset, dataLen, data, unsynchronized); 275 } 276 277 //////////////////////////////////////// 278 279 RefPtr<WebGLBuffer> WebGLContext::CreateBuffer() { 280 const FuncScope funcScope(*this, "createBuffer"); 281 if (IsContextLost()) return nullptr; 282 283 GLuint buf = 0; 284 gl->fGenBuffers(1, &buf); 285 286 return new WebGLBuffer(this, buf); 287 } 288 289 } // namespace mozilla