Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/. */
6 #include "WebGLContext.h"
7 #include "GLContext.h"
8 #include "WebGLBuffer.h"
9 #include "WebGLVertexArray.h"
11 using namespace mozilla;
12 using namespace mozilla::dom;
14 void
15 WebGLContext::BindBuffer(GLenum target, WebGLBuffer *buffer)
16 {
17 if (IsContextLost())
18 return;
20 if (!ValidateObjectAllowDeletedOrNull("bindBuffer", buffer))
21 return;
23 // silently ignore a deleted buffer
24 if (buffer && buffer->IsDeleted())
25 return;
27 WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer");
29 if (!bufferSlot) {
30 return;
31 }
33 if (buffer) {
34 if (!buffer->Target()) {
35 buffer->SetTarget(target);
36 buffer->SetHasEverBeenBound(true);
37 } else if (target != buffer->Target()) {
38 return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
39 }
40 }
42 *bufferSlot = buffer;
44 MakeContextCurrent();
46 gl->fBindBuffer(target, buffer ? buffer->GLName() : 0);
47 }
49 void
50 WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
51 {
52 if (IsContextLost())
53 return;
55 if (!ValidateObjectAllowDeletedOrNull("bindBufferBase", buffer))
56 return;
58 // silently ignore a deleted buffer
59 if (buffer && buffer->IsDeleted()) {
60 return;
61 }
63 WebGLRefPtr<WebGLBuffer>* indexedBufferSlot = GetBufferSlotByTargetIndexed(target, index, "bindBufferBase");
65 if (!indexedBufferSlot) {
66 return;
67 }
69 if (buffer) {
70 if (!buffer->Target()) {
71 buffer->SetTarget(target);
72 buffer->SetHasEverBeenBound(true);
73 } else if (target != buffer->Target()) {
74 return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
75 }
76 }
78 WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer");
80 MOZ_ASSERT(bufferSlot, "GetBufferSlotByTarget(Indexed) mismatch");
82 *indexedBufferSlot = buffer;
83 *bufferSlot = buffer;
85 MakeContextCurrent();
87 gl->fBindBufferBase(target, index, buffer ? buffer->GLName() : 0);
88 }
90 void
91 WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
92 WebGLintptr offset, WebGLsizeiptr size)
93 {
94 if (IsContextLost())
95 return;
97 if (!ValidateObjectAllowDeletedOrNull("bindBufferRange", buffer))
98 return;
100 // silently ignore a deleted buffer
101 if (buffer && buffer->IsDeleted())
102 return;
104 WebGLRefPtr<WebGLBuffer>* indexedBufferSlot = GetBufferSlotByTargetIndexed(target, index, "bindBufferBase");
106 if (!indexedBufferSlot) {
107 return;
108 }
110 if (buffer) {
111 if (!buffer->Target()) {
112 buffer->SetTarget(target);
113 buffer->SetHasEverBeenBound(true);
114 } else if (target != buffer->Target()) {
115 return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
116 }
117 CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + size;
118 if (!checked_neededByteLength.isValid() ||
119 checked_neededByteLength.value() > buffer->ByteLength())
120 {
121 return ErrorInvalidValue("bindBufferRange: invalid range");
122 }
123 }
125 WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer");
127 MOZ_ASSERT(bufferSlot, "GetBufferSlotByTarget(Indexed) mismatch");
129 *indexedBufferSlot = buffer;
130 *bufferSlot = buffer;
132 MakeContextCurrent();
134 gl->fBindBufferRange(target, index, buffer ? buffer->GLName() : 0, offset, size);
135 }
137 void
138 WebGLContext::BufferData(GLenum target, WebGLsizeiptr size,
139 GLenum usage)
140 {
141 if (IsContextLost())
142 return;
144 WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferData");
146 if (!bufferSlot) {
147 return;
148 }
150 if (size < 0)
151 return ErrorInvalidValue("bufferData: negative size");
153 if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
154 return;
156 // careful: WebGLsizeiptr is always 64-bit, but GLsizeiptr is like intptr_t.
157 if (!CheckedInt<GLsizeiptr>(size).isValid())
158 return ErrorOutOfMemory("bufferData: bad size");
160 WebGLBuffer* boundBuffer = bufferSlot->get();
162 if (!boundBuffer)
163 return ErrorInvalidOperation("bufferData: no buffer bound!");
165 void* zeroBuffer = calloc(size, 1);
166 if (!zeroBuffer)
167 return ErrorOutOfMemory("bufferData: out of memory");
169 MakeContextCurrent();
170 InvalidateBufferFetching();
172 GLenum error = CheckedBufferData(target, size, zeroBuffer, usage);
173 free(zeroBuffer);
175 if (error) {
176 GenerateWarning("bufferData generated error %s", ErrorName(error));
177 return;
178 }
180 boundBuffer->SetByteLength(size);
181 if (!boundBuffer->ElementArrayCacheBufferData(nullptr, size)) {
182 return ErrorOutOfMemory("bufferData: out of memory");
183 }
184 }
186 void
187 WebGLContext::BufferData(GLenum target,
188 const Nullable<ArrayBuffer> &maybeData,
189 GLenum usage)
190 {
191 if (IsContextLost())
192 return;
194 if (maybeData.IsNull()) {
195 // see http://www.khronos.org/bugzilla/show_bug.cgi?id=386
196 return ErrorInvalidValue("bufferData: null object passed");
197 }
199 WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferData");
201 if (!bufferSlot) {
202 return;
203 }
205 const ArrayBuffer& data = maybeData.Value();
206 data.ComputeLengthAndData();
208 // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
209 // is like intptr_t.
210 if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
211 return ErrorOutOfMemory("bufferData: bad size");
213 if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
214 return;
216 WebGLBuffer* boundBuffer = bufferSlot->get();
218 if (!boundBuffer)
219 return ErrorInvalidOperation("bufferData: no buffer bound!");
221 MakeContextCurrent();
222 InvalidateBufferFetching();
224 GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);
226 if (error) {
227 GenerateWarning("bufferData generated error %s", ErrorName(error));
228 return;
229 }
231 boundBuffer->SetByteLength(data.Length());
232 if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) {
233 return ErrorOutOfMemory("bufferData: out of memory");
234 }
235 }
237 void
238 WebGLContext::BufferData(GLenum target, const ArrayBufferView& data,
239 GLenum usage)
240 {
241 if (IsContextLost())
242 return;
244 WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData");
246 if (!bufferSlot) {
247 return;
248 }
250 if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
251 return;
253 WebGLBuffer* boundBuffer = bufferSlot->get();
255 if (!boundBuffer)
256 return ErrorInvalidOperation("bufferData: no buffer bound!");
258 data.ComputeLengthAndData();
260 // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
261 // is like intptr_t.
262 if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
263 return ErrorOutOfMemory("bufferData: bad size");
265 InvalidateBufferFetching();
266 MakeContextCurrent();
268 GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);
269 if (error) {
270 GenerateWarning("bufferData generated error %s", ErrorName(error));
271 return;
272 }
274 boundBuffer->SetByteLength(data.Length());
275 if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) {
276 return ErrorOutOfMemory("bufferData: out of memory");
277 }
278 }
280 void
281 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
282 const Nullable<ArrayBuffer> &maybeData)
283 {
284 if (IsContextLost())
285 return;
287 if (maybeData.IsNull()) {
288 // see http://www.khronos.org/bugzilla/show_bug.cgi?id=386
289 return;
290 }
292 WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData");
294 if (!bufferSlot) {
295 return;
296 }
298 if (byteOffset < 0)
299 return ErrorInvalidValue("bufferSubData: negative offset");
301 WebGLBuffer* boundBuffer = bufferSlot->get();
303 if (!boundBuffer)
304 return ErrorInvalidOperation("bufferData: no buffer bound!");
306 const ArrayBuffer& data = maybeData.Value();
307 data.ComputeLengthAndData();
309 CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();
310 if (!checked_neededByteLength.isValid())
311 return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
313 if (checked_neededByteLength.value() > boundBuffer->ByteLength())
314 return ErrorInvalidValue("bufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes",
315 checked_neededByteLength.value(), boundBuffer->ByteLength());
317 MakeContextCurrent();
319 boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length());
321 gl->fBufferSubData(target, byteOffset, data.Length(), data.Data());
322 }
324 void
325 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
326 const ArrayBufferView& data)
327 {
328 if (IsContextLost())
329 return;
331 WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData");
333 if (!bufferSlot) {
334 return;
335 }
337 if (byteOffset < 0)
338 return ErrorInvalidValue("bufferSubData: negative offset");
340 WebGLBuffer* boundBuffer = bufferSlot->get();
342 if (!boundBuffer)
343 return ErrorInvalidOperation("bufferSubData: no buffer bound!");
345 data.ComputeLengthAndData();
347 CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();
348 if (!checked_neededByteLength.isValid())
349 return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
351 if (checked_neededByteLength.value() > boundBuffer->ByteLength())
352 return ErrorInvalidValue("bufferSubData: not enough data -- operation requires %d bytes, but buffer only has %d bytes",
353 checked_neededByteLength.value(), boundBuffer->ByteLength());
355 boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length());
357 MakeContextCurrent();
358 gl->fBufferSubData(target, byteOffset, data.Length(), data.Data());
359 }
361 already_AddRefed<WebGLBuffer>
362 WebGLContext::CreateBuffer()
363 {
364 if (IsContextLost())
365 return nullptr;
367 nsRefPtr<WebGLBuffer> globj = new WebGLBuffer(this);
368 return globj.forget();
369 }
371 void
372 WebGLContext::DeleteBuffer(WebGLBuffer *buffer)
373 {
374 if (IsContextLost())
375 return;
377 if (!ValidateObjectAllowDeletedOrNull("deleteBuffer", buffer))
378 return;
380 if (!buffer || buffer->IsDeleted())
381 return;
383 if (mBoundArrayBuffer == buffer) {
384 BindBuffer(LOCAL_GL_ARRAY_BUFFER,
385 static_cast<WebGLBuffer*>(nullptr));
386 }
388 if (mBoundVertexArray->mBoundElementArrayBuffer == buffer) {
389 BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER,
390 static_cast<WebGLBuffer*>(nullptr));
391 }
393 for (int32_t i = 0; i < mGLMaxVertexAttribs; i++) {
394 if (mBoundVertexArray->HasAttrib(i) && mBoundVertexArray->mAttribs[i].buf == buffer)
395 mBoundVertexArray->mAttribs[i].buf = nullptr;
396 }
398 buffer->RequestDelete();
399 }
401 bool
402 WebGLContext::IsBuffer(WebGLBuffer *buffer)
403 {
404 if (IsContextLost())
405 return false;
407 return ValidateObjectAllowDeleted("isBuffer", buffer) &&
408 !buffer->IsDeleted() &&
409 buffer->HasEverBeenBound();
410 }
412 bool
413 WebGLContext::ValidateBufferUsageEnum(GLenum target, const char *infos)
414 {
415 switch (target) {
416 case LOCAL_GL_STREAM_DRAW:
417 case LOCAL_GL_STATIC_DRAW:
418 case LOCAL_GL_DYNAMIC_DRAW:
419 return true;
420 default:
421 break;
422 }
424 ErrorInvalidEnumInfo(infos, target);
425 return false;
426 }
428 WebGLRefPtr<WebGLBuffer>*
429 WebGLContext::GetBufferSlotByTarget(GLenum target, const char* infos)
430 {
431 switch (target) {
432 case LOCAL_GL_ARRAY_BUFFER:
433 return &mBoundArrayBuffer;
435 case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
436 return &mBoundVertexArray->mBoundElementArrayBuffer;
438 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
439 if (!IsWebGL2()) {
440 break;
441 }
442 return &mBoundTransformFeedbackBuffer;
444 default:
445 break;
446 }
448 ErrorInvalidEnum("%s: target: invalid enum value 0x%x", infos, target);
449 return nullptr;
450 }
452 WebGLRefPtr<WebGLBuffer>*
453 WebGLContext::GetBufferSlotByTargetIndexed(GLenum target, GLuint index, const char* infos)
454 {
455 switch (target) {
456 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
457 if (index >= mGLMaxTransformFeedbackSeparateAttribs) {
458 ErrorInvalidValue("%s: index should be less than MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", infos, index);
459 return nullptr;
460 }
461 return nullptr; // See bug 903594
463 default:
464 break;
465 }
467 ErrorInvalidEnum("%s: target: invalid enum value 0x%x", infos, target);
468 return nullptr;
469 }
471 GLenum
472 WebGLContext::CheckedBufferData(GLenum target,
473 GLsizeiptr size,
474 const GLvoid *data,
475 GLenum usage)
476 {
477 #ifdef XP_MACOSX
478 // bug 790879
479 if (gl->WorkAroundDriverBugs() &&
480 int64_t(size) > INT32_MAX) // the cast avoids a potential always-true warning on 32bit
481 {
482 GenerateWarning("Rejecting valid bufferData call with size %lu to avoid a Mac bug", size);
483 return LOCAL_GL_INVALID_VALUE;
484 }
485 #endif
486 WebGLBuffer *boundBuffer = nullptr;
487 if (target == LOCAL_GL_ARRAY_BUFFER) {
488 boundBuffer = mBoundArrayBuffer;
489 } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
490 boundBuffer = mBoundVertexArray->mBoundElementArrayBuffer;
491 }
492 MOZ_ASSERT(boundBuffer != nullptr, "no buffer bound for this target");
494 bool sizeChanges = uint32_t(size) != boundBuffer->ByteLength();
495 if (sizeChanges) {
496 GetAndFlushUnderlyingGLErrors();
497 gl->fBufferData(target, size, data, usage);
498 GLenum error = GetAndFlushUnderlyingGLErrors();
499 return error;
500 } else {
501 gl->fBufferData(target, size, data, usage);
502 return LOCAL_GL_NO_ERROR;
503 }
504 }