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