Line data Source code
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/. */
5 :
6 : #include "WebGLContext.h"
7 :
8 : #include "GLContext.h"
9 : #include "WebGLBuffer.h"
10 : #include "WebGLTransformFeedback.h"
11 : #include "WebGLVertexArray.h"
12 :
13 : namespace mozilla {
14 :
15 : WebGLRefPtr<WebGLBuffer>*
16 0 : WebGLContext::ValidateBufferSlot(const char* funcName, GLenum target)
17 : {
18 0 : WebGLRefPtr<WebGLBuffer>* slot = nullptr;
19 :
20 0 : switch (target) {
21 : case LOCAL_GL_ARRAY_BUFFER:
22 0 : slot = &mBoundArrayBuffer;
23 0 : break;
24 :
25 : case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
26 0 : slot = &(mBoundVertexArray->mElementArrayBuffer);
27 0 : break;
28 : }
29 :
30 0 : if (IsWebGL2()) {
31 0 : switch (target) {
32 : case LOCAL_GL_COPY_READ_BUFFER:
33 0 : slot = &mBoundCopyReadBuffer;
34 0 : break;
35 :
36 : case LOCAL_GL_COPY_WRITE_BUFFER:
37 0 : slot = &mBoundCopyWriteBuffer;
38 0 : break;
39 :
40 : case LOCAL_GL_PIXEL_PACK_BUFFER:
41 0 : slot = &mBoundPixelPackBuffer;
42 0 : break;
43 :
44 : case LOCAL_GL_PIXEL_UNPACK_BUFFER:
45 0 : slot = &mBoundPixelUnpackBuffer;
46 0 : break;
47 :
48 : case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
49 0 : slot = &(mBoundTransformFeedback->mGenericBufferBinding);
50 0 : break;
51 :
52 : case LOCAL_GL_UNIFORM_BUFFER:
53 0 : slot = &mBoundUniformBuffer;
54 0 : break;
55 : }
56 : }
57 :
58 0 : if (!slot) {
59 0 : ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
60 0 : return nullptr;
61 : }
62 :
63 0 : return slot;
64 : }
65 :
66 : WebGLBuffer*
67 0 : WebGLContext::ValidateBufferSelection(const char* funcName, GLenum target)
68 : {
69 0 : const auto& slot = ValidateBufferSlot(funcName, target);
70 0 : if (!slot)
71 0 : return nullptr;
72 0 : const auto& buffer = *slot;
73 :
74 0 : if (!buffer) {
75 0 : ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
76 0 : return nullptr;
77 : }
78 :
79 0 : if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
80 0 : if (mBoundTransformFeedback->IsActiveAndNotPaused()) {
81 : ErrorInvalidOperation("%s: Cannot select TRANSFORM_FEEDBACK_BUFFER when"
82 : " transform feedback is active and unpaused.",
83 0 : funcName);
84 0 : return nullptr;
85 : }
86 0 : if (buffer->IsBoundForNonTF()) {
87 : ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for"
88 : " non-transform-feedback.",
89 0 : funcName);
90 0 : return nullptr;
91 : }
92 : } else {
93 0 : if (buffer->IsBoundForTF()) {
94 : ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for"
95 : " transform feedback.",
96 0 : funcName);
97 0 : return nullptr;
98 : }
99 : }
100 :
101 0 : return buffer.get();
102 : }
103 :
104 : IndexedBufferBinding*
105 0 : WebGLContext::ValidateIndexedBufferSlot(const char* funcName, GLenum target, GLuint index)
106 : {
107 : decltype(mIndexedUniformBufferBindings)* bindings;
108 : const char* maxIndexEnum;
109 0 : switch (target) {
110 : case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
111 0 : bindings = &(mBoundTransformFeedback->mIndexedBindings);
112 0 : maxIndexEnum = "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS";
113 0 : break;
114 :
115 : case LOCAL_GL_UNIFORM_BUFFER:
116 0 : bindings = &mIndexedUniformBufferBindings;
117 0 : maxIndexEnum = "MAX_UNIFORM_BUFFER_BINDINGS";
118 0 : break;
119 :
120 : default:
121 0 : ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
122 0 : return nullptr;
123 : }
124 :
125 0 : if (index >= bindings->size()) {
126 0 : ErrorInvalidValue("%s: `index` >= %s.", funcName, maxIndexEnum);
127 0 : return nullptr;
128 : }
129 :
130 0 : return &(*bindings)[index];
131 : }
132 :
133 : ////////////////////////////////////////
134 :
135 : void
136 0 : WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer)
137 : {
138 0 : const char funcName[] = "bindBuffer";
139 0 : if (IsContextLost())
140 0 : return;
141 :
142 0 : if (buffer && !ValidateObject(funcName, *buffer))
143 0 : return;
144 :
145 0 : const auto& slot = ValidateBufferSlot(funcName, target);
146 0 : if (!slot)
147 0 : return;
148 :
149 0 : if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
150 0 : return;
151 :
152 0 : gl->MakeCurrent();
153 0 : gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
154 :
155 0 : WebGLBuffer::SetSlot(target, buffer, slot);
156 0 : if (buffer) {
157 0 : buffer->SetContentAfterBind(target);
158 : }
159 :
160 0 : switch (target) {
161 : case LOCAL_GL_PIXEL_PACK_BUFFER:
162 : case LOCAL_GL_PIXEL_UNPACK_BUFFER:
163 0 : gl->fBindBuffer(target, 0);
164 0 : break;
165 : }
166 : }
167 :
168 : ////////////////////////////////////////
169 :
170 : bool
171 0 : WebGLContext::ValidateIndexedBufferBinding(const char* funcName, GLenum target,
172 : GLuint index,
173 : WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
174 : IndexedBufferBinding** const out_indexedBinding)
175 : {
176 0 : *out_genericBinding = ValidateBufferSlot(funcName, target);
177 0 : if (!*out_genericBinding)
178 0 : return false;
179 :
180 0 : *out_indexedBinding = ValidateIndexedBufferSlot(funcName, target, index);
181 0 : if (!*out_indexedBinding)
182 0 : return false;
183 :
184 0 : if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
185 0 : mBoundTransformFeedback->mIsActive)
186 : {
187 : ErrorInvalidOperation("%s: Cannot update indexed buffer bindings on active"
188 : " transform feedback objects.",
189 0 : funcName);
190 0 : return false;
191 : }
192 :
193 0 : return true;
194 : }
195 :
196 : void
197 0 : WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
198 : {
199 0 : const char funcName[] = "bindBufferBase";
200 0 : if (IsContextLost())
201 0 : return;
202 :
203 0 : if (buffer && !ValidateObject(funcName, *buffer))
204 0 : return;
205 :
206 : WebGLRefPtr<WebGLBuffer>* genericBinding;
207 : IndexedBufferBinding* indexedBinding;
208 0 : if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
209 : &indexedBinding))
210 : {
211 0 : return;
212 : }
213 :
214 0 : if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
215 0 : return;
216 :
217 : ////
218 :
219 0 : gl->MakeCurrent();
220 0 : gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
221 :
222 : ////
223 :
224 0 : WebGLBuffer::SetSlot(target, buffer, genericBinding);
225 0 : WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
226 0 : indexedBinding->mRangeStart = 0;
227 0 : indexedBinding->mRangeSize = 0;
228 :
229 0 : if (buffer) {
230 0 : buffer->SetContentAfterBind(target);
231 : }
232 : }
233 :
234 : void
235 0 : WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
236 : WebGLintptr offset, WebGLsizeiptr size)
237 : {
238 0 : const char funcName[] = "bindBufferRange";
239 0 : if (IsContextLost())
240 0 : return;
241 :
242 0 : if (buffer && !ValidateObject(funcName, *buffer))
243 0 : return;
244 :
245 0 : if (!ValidateNonNegative(funcName, "offset", offset) ||
246 0 : !ValidateNonNegative(funcName, "size", size))
247 : {
248 0 : return;
249 : }
250 :
251 : WebGLRefPtr<WebGLBuffer>* genericBinding;
252 : IndexedBufferBinding* indexedBinding;
253 0 : if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
254 : &indexedBinding))
255 : {
256 0 : return;
257 : }
258 :
259 0 : if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
260 0 : return;
261 :
262 0 : if (buffer && !size) {
263 0 : ErrorInvalidValue("%s: size must be non-zero for non-null buffer.", funcName);
264 0 : return;
265 : }
266 :
267 : ////
268 :
269 0 : gl->MakeCurrent();
270 :
271 0 : switch (target) {
272 : case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
273 0 : if (offset % 4 != 0 || size % 4 != 0) {
274 : ErrorInvalidValue("%s: For %s, `offset` and `size` must be multiples of 4.",
275 0 : funcName, "TRANSFORM_FEEDBACK_BUFFER");
276 0 : return;
277 : }
278 0 : break;
279 :
280 : case LOCAL_GL_UNIFORM_BUFFER:
281 : {
282 0 : GLuint offsetAlignment = 0;
283 0 : gl->GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment);
284 0 : if (offset % offsetAlignment != 0) {
285 : ErrorInvalidValue("%s: For %s, `offset` must be a multiple of %s.",
286 : funcName, "UNIFORM_BUFFER",
287 0 : "UNIFORM_BUFFER_OFFSET_ALIGNMENT");
288 0 : return;
289 : }
290 : }
291 0 : break;
292 : }
293 :
294 : ////
295 :
296 : #ifdef XP_MACOSX
297 : if (buffer && buffer->Content() == WebGLBuffer::Kind::Undefined &&
298 : gl->WorkAroundDriverBugs())
299 : {
300 : // BindBufferRange will fail if the buffer's contents is undefined.
301 : // Bind so driver initializes the buffer.
302 : gl->fBindBuffer(target, buffer->mGLName);
303 : }
304 : #endif
305 :
306 0 : gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);
307 :
308 : ////
309 :
310 0 : WebGLBuffer::SetSlot(target, buffer, genericBinding);
311 0 : WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
312 0 : indexedBinding->mRangeStart = offset;
313 0 : indexedBinding->mRangeSize = size;
314 :
315 0 : if (buffer) {
316 0 : buffer->SetContentAfterBind(target);
317 : }
318 : }
319 :
320 : ////////////////////////////////////////
321 :
322 : void
323 0 : WebGLContext::BufferDataImpl(GLenum target, size_t dataLen, const uint8_t* data,
324 : GLenum usage)
325 : {
326 0 : const char funcName[] = "bufferData";
327 :
328 0 : const auto& buffer = ValidateBufferSelection(funcName, target);
329 0 : if (!buffer)
330 0 : return;
331 :
332 0 : buffer->BufferData(target, dataLen, data, usage);
333 : }
334 :
335 : ////
336 :
337 : void
338 0 : WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
339 : {
340 0 : const char funcName[] = "bufferData";
341 0 : if (IsContextLost())
342 0 : return;
343 :
344 0 : if (!ValidateNonNegative(funcName, "size", size))
345 0 : return;
346 :
347 : ////
348 :
349 0 : const UniqueBuffer zeroBuffer(calloc(size, 1));
350 0 : if (!zeroBuffer)
351 0 : return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName);
352 :
353 0 : BufferDataImpl(target, size_t(size), (const uint8_t*)zeroBuffer.get(), usage);
354 : }
355 :
356 : void
357 0 : WebGLContext::BufferData(GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeSrc,
358 : GLenum usage)
359 : {
360 0 : if (IsContextLost())
361 0 : return;
362 :
363 0 : if (!ValidateNonNull("bufferData", maybeSrc))
364 0 : return;
365 0 : const auto& src = maybeSrc.Value();
366 :
367 0 : src.ComputeLengthAndData();
368 0 : BufferDataImpl(target, src.LengthAllowShared(), src.DataAllowShared(), usage);
369 : }
370 :
371 : void
372 0 : WebGLContext::BufferData(GLenum target, const dom::ArrayBufferView& src, GLenum usage,
373 : GLuint srcElemOffset, GLuint srcElemCountOverride)
374 : {
375 0 : const char funcName[] = "bufferData";
376 0 : if (IsContextLost())
377 0 : return;
378 :
379 : uint8_t* bytes;
380 : size_t byteLen;
381 0 : if (!ValidateArrayBufferView(funcName, src, srcElemOffset, srcElemCountOverride,
382 : &bytes, &byteLen))
383 : {
384 0 : return;
385 : }
386 :
387 0 : BufferDataImpl(target, byteLen, bytes, usage);
388 : }
389 :
390 : ////////////////////////////////////////
391 :
392 : void
393 0 : WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset,
394 : size_t dataLen, const uint8_t* data)
395 : {
396 0 : const char funcName[] = "bufferSubData";
397 :
398 0 : if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset))
399 0 : return;
400 :
401 0 : const auto& buffer = ValidateBufferSelection(funcName, target);
402 0 : if (!buffer)
403 0 : return;
404 :
405 0 : buffer->BufferSubData(target, size_t(dstByteOffset), dataLen, data);
406 : }
407 :
408 : ////
409 :
410 : void
411 0 : WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
412 : const dom::ArrayBuffer& src)
413 : {
414 0 : if (IsContextLost())
415 0 : return;
416 :
417 0 : src.ComputeLengthAndData();
418 0 : BufferSubDataImpl(target, dstByteOffset, src.LengthAllowShared(),
419 0 : src.DataAllowShared());
420 : }
421 :
422 : void
423 0 : WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
424 : const dom::ArrayBufferView& src, GLuint srcElemOffset,
425 : GLuint srcElemCountOverride)
426 : {
427 0 : const char funcName[] = "bufferSubData";
428 0 : if (IsContextLost())
429 0 : return;
430 :
431 : uint8_t* bytes;
432 : size_t byteLen;
433 0 : if (!ValidateArrayBufferView(funcName, src, srcElemOffset, srcElemCountOverride,
434 : &bytes, &byteLen))
435 : {
436 0 : return;
437 : }
438 :
439 0 : BufferSubDataImpl(target, dstByteOffset, byteLen, bytes);
440 : }
441 :
442 : ////////////////////////////////////////
443 :
444 : already_AddRefed<WebGLBuffer>
445 0 : WebGLContext::CreateBuffer()
446 : {
447 0 : if (IsContextLost())
448 0 : return nullptr;
449 :
450 0 : GLuint buf = 0;
451 0 : MakeContextCurrent();
452 0 : gl->fGenBuffers(1, &buf);
453 :
454 0 : RefPtr<WebGLBuffer> globj = new WebGLBuffer(this, buf);
455 0 : return globj.forget();
456 : }
457 :
458 : void
459 0 : WebGLContext::DeleteBuffer(WebGLBuffer* buffer)
460 : {
461 0 : if (!ValidateDeleteObject("deleteBuffer", buffer))
462 0 : return;
463 :
464 : ////
465 :
466 0 : const auto fnClearIfBuffer = [&](GLenum target, WebGLRefPtr<WebGLBuffer>& bindPoint) {
467 0 : if (bindPoint == buffer) {
468 0 : WebGLBuffer::SetSlot(target, nullptr, &bindPoint);
469 : }
470 0 : };
471 :
472 0 : fnClearIfBuffer(0, mBoundArrayBuffer);
473 0 : fnClearIfBuffer(0, mBoundVertexArray->mElementArrayBuffer);
474 :
475 0 : for (auto& cur : mBoundVertexArray->mAttribs) {
476 0 : fnClearIfBuffer(0, cur.mBuf);
477 : }
478 :
479 : // WebGL binding points
480 0 : if (IsWebGL2()) {
481 0 : fnClearIfBuffer(0, mBoundCopyReadBuffer);
482 0 : fnClearIfBuffer(0, mBoundCopyWriteBuffer);
483 0 : fnClearIfBuffer(0, mBoundPixelPackBuffer);
484 0 : fnClearIfBuffer(0, mBoundPixelUnpackBuffer);
485 0 : fnClearIfBuffer(0, mBoundUniformBuffer);
486 0 : fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER,
487 0 : mBoundTransformFeedback->mGenericBufferBinding);
488 :
489 0 : if (!mBoundTransformFeedback->mIsActive) {
490 0 : for (auto& binding : mBoundTransformFeedback->mIndexedBindings) {
491 0 : fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER,
492 0 : binding.mBufferBinding);
493 : }
494 : }
495 :
496 0 : for (auto& binding : mIndexedUniformBufferBindings) {
497 0 : fnClearIfBuffer(0, binding.mBufferBinding);
498 : }
499 : }
500 :
501 : ////
502 :
503 0 : buffer->RequestDelete();
504 : }
505 :
506 : bool
507 0 : WebGLContext::IsBuffer(WebGLBuffer* buffer)
508 : {
509 0 : if (!ValidateIsObject("isBuffer", buffer))
510 0 : return false;
511 :
512 0 : MakeContextCurrent();
513 0 : return gl->fIsBuffer(buffer->mGLName);
514 : }
515 :
516 : } // namespace mozilla
|