1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/src/WebGLContextBuffers.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,504 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "WebGLContext.h" 1.10 +#include "GLContext.h" 1.11 +#include "WebGLBuffer.h" 1.12 +#include "WebGLVertexArray.h" 1.13 + 1.14 +using namespace mozilla; 1.15 +using namespace mozilla::dom; 1.16 + 1.17 +void 1.18 +WebGLContext::BindBuffer(GLenum target, WebGLBuffer *buffer) 1.19 +{ 1.20 + if (IsContextLost()) 1.21 + return; 1.22 + 1.23 + if (!ValidateObjectAllowDeletedOrNull("bindBuffer", buffer)) 1.24 + return; 1.25 + 1.26 + // silently ignore a deleted buffer 1.27 + if (buffer && buffer->IsDeleted()) 1.28 + return; 1.29 + 1.30 + WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer"); 1.31 + 1.32 + if (!bufferSlot) { 1.33 + return; 1.34 + } 1.35 + 1.36 + if (buffer) { 1.37 + if (!buffer->Target()) { 1.38 + buffer->SetTarget(target); 1.39 + buffer->SetHasEverBeenBound(true); 1.40 + } else if (target != buffer->Target()) { 1.41 + return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target"); 1.42 + } 1.43 + } 1.44 + 1.45 + *bufferSlot = buffer; 1.46 + 1.47 + MakeContextCurrent(); 1.48 + 1.49 + gl->fBindBuffer(target, buffer ? buffer->GLName() : 0); 1.50 +} 1.51 + 1.52 +void 1.53 +WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer) 1.54 +{ 1.55 + if (IsContextLost()) 1.56 + return; 1.57 + 1.58 + if (!ValidateObjectAllowDeletedOrNull("bindBufferBase", buffer)) 1.59 + return; 1.60 + 1.61 + // silently ignore a deleted buffer 1.62 + if (buffer && buffer->IsDeleted()) { 1.63 + return; 1.64 + } 1.65 + 1.66 + WebGLRefPtr<WebGLBuffer>* indexedBufferSlot = GetBufferSlotByTargetIndexed(target, index, "bindBufferBase"); 1.67 + 1.68 + if (!indexedBufferSlot) { 1.69 + return; 1.70 + } 1.71 + 1.72 + if (buffer) { 1.73 + if (!buffer->Target()) { 1.74 + buffer->SetTarget(target); 1.75 + buffer->SetHasEverBeenBound(true); 1.76 + } else if (target != buffer->Target()) { 1.77 + return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target"); 1.78 + } 1.79 + } 1.80 + 1.81 + WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer"); 1.82 + 1.83 + MOZ_ASSERT(bufferSlot, "GetBufferSlotByTarget(Indexed) mismatch"); 1.84 + 1.85 + *indexedBufferSlot = buffer; 1.86 + *bufferSlot = buffer; 1.87 + 1.88 + MakeContextCurrent(); 1.89 + 1.90 + gl->fBindBufferBase(target, index, buffer ? buffer->GLName() : 0); 1.91 +} 1.92 + 1.93 +void 1.94 +WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, 1.95 + WebGLintptr offset, WebGLsizeiptr size) 1.96 +{ 1.97 + if (IsContextLost()) 1.98 + return; 1.99 + 1.100 + if (!ValidateObjectAllowDeletedOrNull("bindBufferRange", buffer)) 1.101 + return; 1.102 + 1.103 + // silently ignore a deleted buffer 1.104 + if (buffer && buffer->IsDeleted()) 1.105 + return; 1.106 + 1.107 + WebGLRefPtr<WebGLBuffer>* indexedBufferSlot = GetBufferSlotByTargetIndexed(target, index, "bindBufferBase"); 1.108 + 1.109 + if (!indexedBufferSlot) { 1.110 + return; 1.111 + } 1.112 + 1.113 + if (buffer) { 1.114 + if (!buffer->Target()) { 1.115 + buffer->SetTarget(target); 1.116 + buffer->SetHasEverBeenBound(true); 1.117 + } else if (target != buffer->Target()) { 1.118 + return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target"); 1.119 + } 1.120 + CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + size; 1.121 + if (!checked_neededByteLength.isValid() || 1.122 + checked_neededByteLength.value() > buffer->ByteLength()) 1.123 + { 1.124 + return ErrorInvalidValue("bindBufferRange: invalid range"); 1.125 + } 1.126 + } 1.127 + 1.128 + WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer"); 1.129 + 1.130 + MOZ_ASSERT(bufferSlot, "GetBufferSlotByTarget(Indexed) mismatch"); 1.131 + 1.132 + *indexedBufferSlot = buffer; 1.133 + *bufferSlot = buffer; 1.134 + 1.135 + MakeContextCurrent(); 1.136 + 1.137 + gl->fBindBufferRange(target, index, buffer ? buffer->GLName() : 0, offset, size); 1.138 +} 1.139 + 1.140 +void 1.141 +WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, 1.142 + GLenum usage) 1.143 +{ 1.144 + if (IsContextLost()) 1.145 + return; 1.146 + 1.147 + WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferData"); 1.148 + 1.149 + if (!bufferSlot) { 1.150 + return; 1.151 + } 1.152 + 1.153 + if (size < 0) 1.154 + return ErrorInvalidValue("bufferData: negative size"); 1.155 + 1.156 + if (!ValidateBufferUsageEnum(usage, "bufferData: usage")) 1.157 + return; 1.158 + 1.159 + // careful: WebGLsizeiptr is always 64-bit, but GLsizeiptr is like intptr_t. 1.160 + if (!CheckedInt<GLsizeiptr>(size).isValid()) 1.161 + return ErrorOutOfMemory("bufferData: bad size"); 1.162 + 1.163 + WebGLBuffer* boundBuffer = bufferSlot->get(); 1.164 + 1.165 + if (!boundBuffer) 1.166 + return ErrorInvalidOperation("bufferData: no buffer bound!"); 1.167 + 1.168 + void* zeroBuffer = calloc(size, 1); 1.169 + if (!zeroBuffer) 1.170 + return ErrorOutOfMemory("bufferData: out of memory"); 1.171 + 1.172 + MakeContextCurrent(); 1.173 + InvalidateBufferFetching(); 1.174 + 1.175 + GLenum error = CheckedBufferData(target, size, zeroBuffer, usage); 1.176 + free(zeroBuffer); 1.177 + 1.178 + if (error) { 1.179 + GenerateWarning("bufferData generated error %s", ErrorName(error)); 1.180 + return; 1.181 + } 1.182 + 1.183 + boundBuffer->SetByteLength(size); 1.184 + if (!boundBuffer->ElementArrayCacheBufferData(nullptr, size)) { 1.185 + return ErrorOutOfMemory("bufferData: out of memory"); 1.186 + } 1.187 +} 1.188 + 1.189 +void 1.190 +WebGLContext::BufferData(GLenum target, 1.191 + const Nullable<ArrayBuffer> &maybeData, 1.192 + GLenum usage) 1.193 +{ 1.194 + if (IsContextLost()) 1.195 + return; 1.196 + 1.197 + if (maybeData.IsNull()) { 1.198 + // see http://www.khronos.org/bugzilla/show_bug.cgi?id=386 1.199 + return ErrorInvalidValue("bufferData: null object passed"); 1.200 + } 1.201 + 1.202 + WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferData"); 1.203 + 1.204 + if (!bufferSlot) { 1.205 + return; 1.206 + } 1.207 + 1.208 + const ArrayBuffer& data = maybeData.Value(); 1.209 + data.ComputeLengthAndData(); 1.210 + 1.211 + // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr 1.212 + // is like intptr_t. 1.213 + if (!CheckedInt<GLsizeiptr>(data.Length()).isValid()) 1.214 + return ErrorOutOfMemory("bufferData: bad size"); 1.215 + 1.216 + if (!ValidateBufferUsageEnum(usage, "bufferData: usage")) 1.217 + return; 1.218 + 1.219 + WebGLBuffer* boundBuffer = bufferSlot->get(); 1.220 + 1.221 + if (!boundBuffer) 1.222 + return ErrorInvalidOperation("bufferData: no buffer bound!"); 1.223 + 1.224 + MakeContextCurrent(); 1.225 + InvalidateBufferFetching(); 1.226 + 1.227 + GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage); 1.228 + 1.229 + if (error) { 1.230 + GenerateWarning("bufferData generated error %s", ErrorName(error)); 1.231 + return; 1.232 + } 1.233 + 1.234 + boundBuffer->SetByteLength(data.Length()); 1.235 + if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) { 1.236 + return ErrorOutOfMemory("bufferData: out of memory"); 1.237 + } 1.238 +} 1.239 + 1.240 +void 1.241 +WebGLContext::BufferData(GLenum target, const ArrayBufferView& data, 1.242 + GLenum usage) 1.243 +{ 1.244 + if (IsContextLost()) 1.245 + return; 1.246 + 1.247 + WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData"); 1.248 + 1.249 + if (!bufferSlot) { 1.250 + return; 1.251 + } 1.252 + 1.253 + if (!ValidateBufferUsageEnum(usage, "bufferData: usage")) 1.254 + return; 1.255 + 1.256 + WebGLBuffer* boundBuffer = bufferSlot->get(); 1.257 + 1.258 + if (!boundBuffer) 1.259 + return ErrorInvalidOperation("bufferData: no buffer bound!"); 1.260 + 1.261 + data.ComputeLengthAndData(); 1.262 + 1.263 + // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr 1.264 + // is like intptr_t. 1.265 + if (!CheckedInt<GLsizeiptr>(data.Length()).isValid()) 1.266 + return ErrorOutOfMemory("bufferData: bad size"); 1.267 + 1.268 + InvalidateBufferFetching(); 1.269 + MakeContextCurrent(); 1.270 + 1.271 + GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage); 1.272 + if (error) { 1.273 + GenerateWarning("bufferData generated error %s", ErrorName(error)); 1.274 + return; 1.275 + } 1.276 + 1.277 + boundBuffer->SetByteLength(data.Length()); 1.278 + if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) { 1.279 + return ErrorOutOfMemory("bufferData: out of memory"); 1.280 + } 1.281 +} 1.282 + 1.283 +void 1.284 +WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset, 1.285 + const Nullable<ArrayBuffer> &maybeData) 1.286 +{ 1.287 + if (IsContextLost()) 1.288 + return; 1.289 + 1.290 + if (maybeData.IsNull()) { 1.291 + // see http://www.khronos.org/bugzilla/show_bug.cgi?id=386 1.292 + return; 1.293 + } 1.294 + 1.295 + WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData"); 1.296 + 1.297 + if (!bufferSlot) { 1.298 + return; 1.299 + } 1.300 + 1.301 + if (byteOffset < 0) 1.302 + return ErrorInvalidValue("bufferSubData: negative offset"); 1.303 + 1.304 + WebGLBuffer* boundBuffer = bufferSlot->get(); 1.305 + 1.306 + if (!boundBuffer) 1.307 + return ErrorInvalidOperation("bufferData: no buffer bound!"); 1.308 + 1.309 + const ArrayBuffer& data = maybeData.Value(); 1.310 + data.ComputeLengthAndData(); 1.311 + 1.312 + CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length(); 1.313 + if (!checked_neededByteLength.isValid()) 1.314 + return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length"); 1.315 + 1.316 + if (checked_neededByteLength.value() > boundBuffer->ByteLength()) 1.317 + return ErrorInvalidValue("bufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes", 1.318 + checked_neededByteLength.value(), boundBuffer->ByteLength()); 1.319 + 1.320 + MakeContextCurrent(); 1.321 + 1.322 + boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length()); 1.323 + 1.324 + gl->fBufferSubData(target, byteOffset, data.Length(), data.Data()); 1.325 +} 1.326 + 1.327 +void 1.328 +WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset, 1.329 + const ArrayBufferView& data) 1.330 +{ 1.331 + if (IsContextLost()) 1.332 + return; 1.333 + 1.334 + WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData"); 1.335 + 1.336 + if (!bufferSlot) { 1.337 + return; 1.338 + } 1.339 + 1.340 + if (byteOffset < 0) 1.341 + return ErrorInvalidValue("bufferSubData: negative offset"); 1.342 + 1.343 + WebGLBuffer* boundBuffer = bufferSlot->get(); 1.344 + 1.345 + if (!boundBuffer) 1.346 + return ErrorInvalidOperation("bufferSubData: no buffer bound!"); 1.347 + 1.348 + data.ComputeLengthAndData(); 1.349 + 1.350 + CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length(); 1.351 + if (!checked_neededByteLength.isValid()) 1.352 + return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length"); 1.353 + 1.354 + if (checked_neededByteLength.value() > boundBuffer->ByteLength()) 1.355 + return ErrorInvalidValue("bufferSubData: not enough data -- operation requires %d bytes, but buffer only has %d bytes", 1.356 + checked_neededByteLength.value(), boundBuffer->ByteLength()); 1.357 + 1.358 + boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length()); 1.359 + 1.360 + MakeContextCurrent(); 1.361 + gl->fBufferSubData(target, byteOffset, data.Length(), data.Data()); 1.362 +} 1.363 + 1.364 +already_AddRefed<WebGLBuffer> 1.365 +WebGLContext::CreateBuffer() 1.366 +{ 1.367 + if (IsContextLost()) 1.368 + return nullptr; 1.369 + 1.370 + nsRefPtr<WebGLBuffer> globj = new WebGLBuffer(this); 1.371 + return globj.forget(); 1.372 +} 1.373 + 1.374 +void 1.375 +WebGLContext::DeleteBuffer(WebGLBuffer *buffer) 1.376 +{ 1.377 + if (IsContextLost()) 1.378 + return; 1.379 + 1.380 + if (!ValidateObjectAllowDeletedOrNull("deleteBuffer", buffer)) 1.381 + return; 1.382 + 1.383 + if (!buffer || buffer->IsDeleted()) 1.384 + return; 1.385 + 1.386 + if (mBoundArrayBuffer == buffer) { 1.387 + BindBuffer(LOCAL_GL_ARRAY_BUFFER, 1.388 + static_cast<WebGLBuffer*>(nullptr)); 1.389 + } 1.390 + 1.391 + if (mBoundVertexArray->mBoundElementArrayBuffer == buffer) { 1.392 + BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, 1.393 + static_cast<WebGLBuffer*>(nullptr)); 1.394 + } 1.395 + 1.396 + for (int32_t i = 0; i < mGLMaxVertexAttribs; i++) { 1.397 + if (mBoundVertexArray->HasAttrib(i) && mBoundVertexArray->mAttribs[i].buf == buffer) 1.398 + mBoundVertexArray->mAttribs[i].buf = nullptr; 1.399 + } 1.400 + 1.401 + buffer->RequestDelete(); 1.402 +} 1.403 + 1.404 +bool 1.405 +WebGLContext::IsBuffer(WebGLBuffer *buffer) 1.406 +{ 1.407 + if (IsContextLost()) 1.408 + return false; 1.409 + 1.410 + return ValidateObjectAllowDeleted("isBuffer", buffer) && 1.411 + !buffer->IsDeleted() && 1.412 + buffer->HasEverBeenBound(); 1.413 +} 1.414 + 1.415 +bool 1.416 +WebGLContext::ValidateBufferUsageEnum(GLenum target, const char *infos) 1.417 +{ 1.418 + switch (target) { 1.419 + case LOCAL_GL_STREAM_DRAW: 1.420 + case LOCAL_GL_STATIC_DRAW: 1.421 + case LOCAL_GL_DYNAMIC_DRAW: 1.422 + return true; 1.423 + default: 1.424 + break; 1.425 + } 1.426 + 1.427 + ErrorInvalidEnumInfo(infos, target); 1.428 + return false; 1.429 +} 1.430 + 1.431 +WebGLRefPtr<WebGLBuffer>* 1.432 +WebGLContext::GetBufferSlotByTarget(GLenum target, const char* infos) 1.433 +{ 1.434 + switch (target) { 1.435 + case LOCAL_GL_ARRAY_BUFFER: 1.436 + return &mBoundArrayBuffer; 1.437 + 1.438 + case LOCAL_GL_ELEMENT_ARRAY_BUFFER: 1.439 + return &mBoundVertexArray->mBoundElementArrayBuffer; 1.440 + 1.441 + case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: 1.442 + if (!IsWebGL2()) { 1.443 + break; 1.444 + } 1.445 + return &mBoundTransformFeedbackBuffer; 1.446 + 1.447 + default: 1.448 + break; 1.449 + } 1.450 + 1.451 + ErrorInvalidEnum("%s: target: invalid enum value 0x%x", infos, target); 1.452 + return nullptr; 1.453 +} 1.454 + 1.455 +WebGLRefPtr<WebGLBuffer>* 1.456 +WebGLContext::GetBufferSlotByTargetIndexed(GLenum target, GLuint index, const char* infos) 1.457 +{ 1.458 + switch (target) { 1.459 + case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: 1.460 + if (index >= mGLMaxTransformFeedbackSeparateAttribs) { 1.461 + ErrorInvalidValue("%s: index should be less than MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", infos, index); 1.462 + return nullptr; 1.463 + } 1.464 + return nullptr; // See bug 903594 1.465 + 1.466 + default: 1.467 + break; 1.468 + } 1.469 + 1.470 + ErrorInvalidEnum("%s: target: invalid enum value 0x%x", infos, target); 1.471 + return nullptr; 1.472 +} 1.473 + 1.474 +GLenum 1.475 +WebGLContext::CheckedBufferData(GLenum target, 1.476 + GLsizeiptr size, 1.477 + const GLvoid *data, 1.478 + GLenum usage) 1.479 +{ 1.480 +#ifdef XP_MACOSX 1.481 + // bug 790879 1.482 + if (gl->WorkAroundDriverBugs() && 1.483 + int64_t(size) > INT32_MAX) // the cast avoids a potential always-true warning on 32bit 1.484 + { 1.485 + GenerateWarning("Rejecting valid bufferData call with size %lu to avoid a Mac bug", size); 1.486 + return LOCAL_GL_INVALID_VALUE; 1.487 + } 1.488 +#endif 1.489 + WebGLBuffer *boundBuffer = nullptr; 1.490 + if (target == LOCAL_GL_ARRAY_BUFFER) { 1.491 + boundBuffer = mBoundArrayBuffer; 1.492 + } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) { 1.493 + boundBuffer = mBoundVertexArray->mBoundElementArrayBuffer; 1.494 + } 1.495 + MOZ_ASSERT(boundBuffer != nullptr, "no buffer bound for this target"); 1.496 + 1.497 + bool sizeChanges = uint32_t(size) != boundBuffer->ByteLength(); 1.498 + if (sizeChanges) { 1.499 + GetAndFlushUnderlyingGLErrors(); 1.500 + gl->fBufferData(target, size, data, usage); 1.501 + GLenum error = GetAndFlushUnderlyingGLErrors(); 1.502 + return error; 1.503 + } else { 1.504 + gl->fBufferData(target, size, data, usage); 1.505 + return LOCAL_GL_NO_ERROR; 1.506 + } 1.507 +}