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 "mozilla/CheckedInt.h"
10 : #include "mozilla/UniquePtrExtensions.h"
11 : #include "nsPrintfCString.h"
12 : #include "WebGLBuffer.h"
13 : #include "WebGLContextUtils.h"
14 : #include "WebGLFramebuffer.h"
15 : #include "WebGLProgram.h"
16 : #include "WebGLRenderbuffer.h"
17 : #include "WebGLShader.h"
18 : #include "WebGLTexture.h"
19 : #include "WebGLTransformFeedback.h"
20 : #include "WebGLVertexArray.h"
21 : #include "WebGLVertexAttribData.h"
22 :
23 : #include <algorithm>
24 :
25 : namespace mozilla {
26 :
27 : // For a Tegra workaround.
28 : static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
29 :
30 : ////////////////////////////////////////
31 :
32 : class ScopedResolveTexturesForDraw
33 : {
34 : struct TexRebindRequest
35 : {
36 : uint32_t texUnit;
37 : WebGLTexture* tex;
38 : };
39 :
40 : WebGLContext* const mWebGL;
41 : std::vector<TexRebindRequest> mRebindRequests;
42 :
43 : public:
44 : ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName,
45 : bool* const out_error);
46 : ~ScopedResolveTexturesForDraw();
47 : };
48 :
49 : bool
50 0 : WebGLTexture::IsFeedback(WebGLContext* webgl, const char* funcName, uint32_t texUnit,
51 : const std::vector<const WebGLFBAttachPoint*>& fbAttachments) const
52 : {
53 0 : auto itr = fbAttachments.cbegin();
54 0 : for (; itr != fbAttachments.cend(); ++itr) {
55 0 : const auto& attach = *itr;
56 0 : if (attach->Texture() == this)
57 0 : break;
58 : }
59 :
60 0 : if (itr == fbAttachments.cend())
61 0 : return false;
62 :
63 : ////
64 :
65 0 : const auto minLevel = mBaseMipmapLevel;
66 : uint32_t maxLevel;
67 0 : if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel)) {
68 : // No valid mips. Will need fake-black.
69 0 : return false;
70 : }
71 :
72 : ////
73 :
74 0 : for (; itr != fbAttachments.cend(); ++itr) {
75 0 : const auto& attach = *itr;
76 0 : if (attach->Texture() != this)
77 0 : continue;
78 :
79 0 : const auto dstLevel = attach->MipLevel();
80 :
81 0 : if (minLevel <= dstLevel && dstLevel <= maxLevel) {
82 0 : webgl->ErrorInvalidOperation("%s: Feedback loop detected between tex target"
83 : " 0x%04x, tex unit %u, levels %u-%u; and"
84 : " framebuffer attachment 0x%04x, level %u.",
85 : funcName, mTarget.get(), texUnit, minLevel,
86 0 : maxLevel, attach->mAttachmentPoint, dstLevel);
87 0 : return true;
88 : }
89 : }
90 :
91 0 : return false;
92 : }
93 :
94 0 : ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
95 : const char* funcName,
96 0 : bool* const out_error)
97 0 : : mWebGL(webgl)
98 : {
99 0 : MOZ_ASSERT(mWebGL->gl->IsCurrent());
100 :
101 0 : if (!mWebGL->mActiveProgramLinkInfo) {
102 0 : mWebGL->ErrorInvalidOperation("%s: The current program is not linked.", funcName);
103 0 : *out_error = true;
104 0 : return;
105 : }
106 :
107 0 : const std::vector<const WebGLFBAttachPoint*>* attachList = nullptr;
108 0 : const auto& fb = mWebGL->mBoundDrawFramebuffer;
109 0 : if (fb) {
110 0 : if (!fb->ValidateAndInitAttachments(funcName)) {
111 0 : *out_error = true;
112 0 : return;
113 : }
114 :
115 0 : attachList = &(fb->ResolvedCompleteData()->texDrawBuffers);
116 : } else {
117 0 : webgl->ClearBackbufferIfNeeded();
118 : }
119 :
120 0 : MOZ_ASSERT(mWebGL->mActiveProgramLinkInfo);
121 0 : const auto& uniformSamplers = mWebGL->mActiveProgramLinkInfo->uniformSamplers;
122 0 : for (const auto& uniform : uniformSamplers) {
123 0 : const auto& texList = *(uniform->mSamplerTexList);
124 :
125 0 : for (const auto& texUnit : uniform->mSamplerValues) {
126 0 : if (texUnit >= texList.Length())
127 0 : continue;
128 :
129 0 : const auto& tex = texList[texUnit];
130 0 : if (!tex)
131 0 : continue;
132 :
133 0 : if (attachList &&
134 0 : tex->IsFeedback(mWebGL, funcName, texUnit, *attachList))
135 : {
136 0 : *out_error = true;
137 0 : return;
138 : }
139 :
140 : FakeBlackType fakeBlack;
141 0 : if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack)) {
142 0 : mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.",
143 0 : funcName);
144 0 : *out_error = true;
145 0 : return;
146 : }
147 :
148 0 : if (fakeBlack == FakeBlackType::None)
149 0 : continue;
150 :
151 0 : if (!mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack)) {
152 0 : mWebGL->ErrorOutOfMemory("%s: Failed to create fake black texture.",
153 0 : funcName);
154 0 : *out_error = true;
155 0 : return;
156 : }
157 :
158 0 : mRebindRequests.push_back({texUnit, tex});
159 : }
160 : }
161 :
162 0 : *out_error = false;
163 : }
164 :
165 0 : ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw()
166 : {
167 0 : if (!mRebindRequests.size())
168 0 : return;
169 :
170 0 : gl::GLContext* gl = mWebGL->gl;
171 :
172 0 : for (const auto& itr : mRebindRequests) {
173 0 : gl->fActiveTexture(LOCAL_GL_TEXTURE0 + itr.texUnit);
174 0 : gl->fBindTexture(itr.tex->Target().get(), itr.tex->mGLName);
175 : }
176 :
177 0 : gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mWebGL->mActiveTexture);
178 0 : }
179 :
180 : bool
181 0 : WebGLContext::BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack)
182 : {
183 0 : MOZ_ASSERT(fakeBlack == FakeBlackType::RGBA0000 ||
184 : fakeBlack == FakeBlackType::RGBA0001);
185 :
186 0 : const auto fnGetSlot = [this, target, fakeBlack]() -> UniquePtr<FakeBlackTexture>*
187 0 : {
188 0 : switch (fakeBlack) {
189 : case FakeBlackType::RGBA0000:
190 0 : switch (target.get()) {
191 0 : case LOCAL_GL_TEXTURE_2D : return &mFakeBlack_2D_0000;
192 0 : case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0000;
193 0 : case LOCAL_GL_TEXTURE_3D : return &mFakeBlack_3D_0000;
194 0 : case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0000;
195 0 : default: return nullptr;
196 : }
197 :
198 : case FakeBlackType::RGBA0001:
199 0 : switch (target.get()) {
200 0 : case LOCAL_GL_TEXTURE_2D : return &mFakeBlack_2D_0001;
201 0 : case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0001;
202 0 : case LOCAL_GL_TEXTURE_3D : return &mFakeBlack_3D_0001;
203 0 : case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0001;
204 0 : default: return nullptr;
205 : }
206 :
207 : default:
208 0 : return nullptr;
209 : }
210 0 : };
211 :
212 0 : UniquePtr<FakeBlackTexture>* slot = fnGetSlot();
213 0 : if (!slot) {
214 0 : MOZ_CRASH("GFX: fnGetSlot failed.");
215 : }
216 0 : UniquePtr<FakeBlackTexture>& fakeBlackTex = *slot;
217 :
218 0 : if (!fakeBlackTex) {
219 0 : fakeBlackTex = FakeBlackTexture::Create(gl, target, fakeBlack);
220 0 : if (!fakeBlackTex) {
221 0 : return false;
222 : }
223 : }
224 :
225 0 : gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
226 0 : gl->fBindTexture(target.get(), fakeBlackTex->mGLName);
227 0 : gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
228 0 : return true;
229 : }
230 :
231 : ////////////////////////////////////////
232 :
233 : bool
234 0 : WebGLContext::DrawInstanced_check(const char* info)
235 : {
236 0 : MOZ_ASSERT(IsWebGL2() ||
237 : IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays));
238 0 : if (!mBufferFetchingHasPerVertex) {
239 : /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt
240 : * If all of the enabled vertex attribute arrays that are bound to active
241 : * generic attributes in the program have a non-zero divisor, the draw
242 : * call should return INVALID_OPERATION.
243 : *
244 : * NB: This also appears to apply to NV_instanced_arrays, though the
245 : * INVALID_OPERATION emission is not explicitly stated.
246 : * ARB_instanced_arrays does not have this restriction.
247 : */
248 0 : ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info);
249 0 : return false;
250 : }
251 :
252 0 : return true;
253 : }
254 :
255 : bool
256 0 : WebGLContext::DrawArrays_check(const char* funcName, GLenum mode, GLint first,
257 : GLsizei vertCount, GLsizei instanceCount)
258 : {
259 0 : if (!ValidateDrawModeEnum(mode, funcName))
260 0 : return false;
261 :
262 0 : if (!ValidateNonNegative(funcName, "first", first) ||
263 0 : !ValidateNonNegative(funcName, "vertCount", vertCount) ||
264 0 : !ValidateNonNegative(funcName, "instanceCount", instanceCount))
265 : {
266 0 : return false;
267 : }
268 :
269 0 : if (!ValidateStencilParamsForDrawCall())
270 0 : return false;
271 :
272 0 : if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
273 0 : MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
274 0 : if (mPrimRestartTypeBytes != 0) {
275 0 : mPrimRestartTypeBytes = 0;
276 :
277 : // OSX appears to have severe perf issues with leaving this enabled.
278 0 : gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART);
279 : }
280 : }
281 :
282 0 : if (!vertCount || !instanceCount)
283 0 : return false; // No error, just early out.
284 :
285 0 : if (!ValidateBufferFetching(funcName))
286 0 : return false;
287 :
288 0 : const auto checked_firstPlusCount = CheckedInt<GLsizei>(first) + vertCount;
289 0 : if (!checked_firstPlusCount.isValid()) {
290 0 : ErrorInvalidOperation("%s: overflow in first+vertCount", funcName);
291 0 : return false;
292 : }
293 :
294 0 : if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) {
295 : ErrorInvalidOperation("%s: Bound vertex attribute buffers do not have sufficient"
296 : " size for given first and count.",
297 0 : funcName);
298 0 : return false;
299 : }
300 :
301 0 : return true;
302 : }
303 :
304 : ////////////////////////////////////////
305 :
306 : template<typename T>
307 : static bool
308 : DoSetsIntersect(const std::set<T>& a, const std::set<T>& b)
309 : {
310 : std::vector<T> intersection;
311 : std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
312 : std::back_inserter(intersection));
313 : return bool(intersection.size());
314 : }
315 :
316 : class ScopedDrawHelper final
317 : {
318 : WebGLContext* const mWebGL;
319 : bool mDidFake;
320 :
321 : public:
322 0 : ScopedDrawHelper(WebGLContext* webgl, const char* funcName, uint32_t firstVertex,
323 : uint32_t vertCount, uint32_t instanceCount, bool* const out_error)
324 0 : : mWebGL(webgl)
325 0 : , mDidFake(false)
326 : {
327 0 : if (instanceCount > mWebGL->mMaxFetchedInstances) {
328 0 : mWebGL->ErrorInvalidOperation("%s: Bound instance attribute buffers do not"
329 : " have sufficient size for given"
330 : " `instanceCount`.",
331 0 : funcName);
332 0 : *out_error = true;
333 0 : return;
334 : }
335 :
336 0 : MOZ_ASSERT(mWebGL->gl->IsCurrent());
337 :
338 0 : if (mWebGL->mBoundDrawFramebuffer) {
339 0 : if (!mWebGL->mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName)) {
340 0 : *out_error = true;
341 0 : return;
342 : }
343 : } else {
344 0 : mWebGL->ClearBackbufferIfNeeded();
345 : }
346 :
347 : ////
348 :
349 0 : const size_t requiredVerts = firstVertex + vertCount;
350 0 : if (!mWebGL->DoFakeVertexAttrib0(funcName, requiredVerts)) {
351 0 : *out_error = true;
352 0 : return;
353 : }
354 0 : mDidFake = true;
355 :
356 : ////
357 : // Check UBO sizes.
358 :
359 0 : const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
360 :
361 0 : for (const auto& cur : linkInfo->uniformBlocks) {
362 0 : const auto& dataSize = cur->mDataSize;
363 0 : const auto& binding = cur->mBinding;
364 0 : if (!binding) {
365 0 : mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is null.",
366 0 : funcName);
367 0 : *out_error = true;
368 0 : return;
369 : }
370 :
371 0 : const auto availByteCount = binding->ByteCount();
372 0 : if (dataSize > availByteCount) {
373 0 : mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is smaller"
374 : " than UNIFORM_BLOCK_DATA_SIZE.",
375 0 : funcName);
376 0 : *out_error = true;
377 0 : return;
378 : }
379 :
380 0 : if (binding->mBufferBinding->IsBoundForTF()) {
381 0 : mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is bound or"
382 : " in use for transform feedback.",
383 0 : funcName);
384 0 : *out_error = true;
385 0 : return;
386 : }
387 : }
388 :
389 : ////
390 :
391 0 : const auto& tfo = mWebGL->mBoundTransformFeedback;
392 0 : if (tfo && tfo->IsActiveAndNotPaused()) {
393 : uint32_t numUsed;
394 0 : switch (linkInfo->transformFeedbackBufferMode) {
395 : case LOCAL_GL_INTERLEAVED_ATTRIBS:
396 0 : numUsed = 1;
397 0 : break;
398 :
399 : case LOCAL_GL_SEPARATE_ATTRIBS:
400 0 : numUsed = linkInfo->transformFeedbackVaryings.size();
401 0 : break;
402 :
403 : default:
404 0 : MOZ_CRASH();
405 : }
406 :
407 0 : for (uint32_t i = 0; i < numUsed; ++i) {
408 0 : const auto& buffer = tfo->mIndexedBindings[i].mBufferBinding;
409 0 : if (buffer->IsBoundForNonTF()) {
410 0 : mWebGL->ErrorInvalidOperation("%s: Transform feedback varying %u's"
411 : " buffer is bound for"
412 : " non-transform-feedback.",
413 0 : funcName, i);
414 0 : *out_error = true;
415 0 : return;
416 : }
417 : }
418 : }
419 :
420 : ////
421 :
422 0 : for (const auto& progAttrib : linkInfo->attribs) {
423 0 : const auto& loc = progAttrib.mLoc;
424 0 : if (loc == -1)
425 0 : continue;
426 :
427 0 : const auto& attribData = mWebGL->mBoundVertexArray->mAttribs[loc];
428 :
429 : GLenum attribDataBaseType;
430 0 : if (attribData.mEnabled) {
431 0 : attribDataBaseType = attribData.BaseType();
432 :
433 0 : if (attribData.mBuf->IsBoundForTF()) {
434 0 : mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u's buffer is bound"
435 : " or in use for transform feedback.",
436 0 : funcName, loc);
437 0 : *out_error = true;
438 0 : return;
439 : }
440 : } else {
441 0 : attribDataBaseType = mWebGL->mGenericVertexAttribTypes[loc];
442 : }
443 :
444 0 : if (attribDataBaseType != progAttrib.mBaseType) {
445 0 : nsCString progType, dataType;
446 0 : WebGLContext::EnumName(progAttrib.mBaseType, &progType);
447 0 : WebGLContext::EnumName(attribDataBaseType, &dataType);
448 0 : mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u requires data of type"
449 : " %s, but is being supplied with type %s.",
450 : funcName, loc, progType.BeginReading(),
451 0 : dataType.BeginReading());
452 0 : *out_error = true;
453 0 : return;
454 : }
455 : }
456 :
457 : ////
458 :
459 0 : mWebGL->RunContextLossTimer();
460 : }
461 :
462 0 : ~ScopedDrawHelper() {
463 0 : if (mDidFake) {
464 0 : mWebGL->UndoFakeVertexAttrib0();
465 : }
466 0 : }
467 : };
468 :
469 : ////////////////////////////////////////
470 :
471 : static uint32_t
472 0 : UsedVertsForTFDraw(GLenum mode, uint32_t vertCount)
473 : {
474 : uint8_t vertsPerPrim;
475 :
476 0 : switch (mode) {
477 : case LOCAL_GL_POINTS:
478 0 : vertsPerPrim = 1;
479 0 : break;
480 : case LOCAL_GL_LINES:
481 0 : vertsPerPrim = 2;
482 0 : break;
483 : case LOCAL_GL_TRIANGLES:
484 0 : vertsPerPrim = 3;
485 0 : break;
486 : default:
487 0 : MOZ_CRASH("`mode`");
488 : }
489 :
490 0 : return vertCount / vertsPerPrim * vertsPerPrim;
491 : }
492 :
493 : class ScopedDrawWithTransformFeedback final
494 : {
495 : WebGLContext* const mWebGL;
496 : WebGLTransformFeedback* const mTFO;
497 : const bool mWithTF;
498 : uint32_t mUsedVerts;
499 :
500 : public:
501 0 : ScopedDrawWithTransformFeedback(WebGLContext* webgl, const char* funcName,
502 : GLenum mode, uint32_t vertCount,
503 : uint32_t instanceCount, bool* const out_error)
504 0 : : mWebGL(webgl)
505 0 : , mTFO(mWebGL->mBoundTransformFeedback)
506 0 : , mWithTF(mTFO &&
507 0 : mTFO->mIsActive &&
508 0 : !mTFO->mIsPaused)
509 0 : , mUsedVerts(0)
510 : {
511 0 : *out_error = false;
512 0 : if (!mWithTF)
513 0 : return;
514 :
515 0 : if (mode != mTFO->mActive_PrimMode) {
516 0 : mWebGL->ErrorInvalidOperation("%s: Drawing with transform feedback requires"
517 : " `mode` to match BeginTransformFeedback's"
518 : " `primitiveMode`.",
519 0 : funcName);
520 0 : *out_error = true;
521 0 : return;
522 : }
523 :
524 0 : const auto usedVertsPerInstance = UsedVertsForTFDraw(mode, vertCount);
525 0 : const auto usedVerts = CheckedInt<uint32_t>(usedVertsPerInstance) * instanceCount;
526 :
527 0 : const auto remainingCapacity = mTFO->mActive_VertCapacity - mTFO->mActive_VertPosition;
528 0 : if (!usedVerts.isValid() ||
529 0 : usedVerts.value() > remainingCapacity)
530 : {
531 0 : mWebGL->ErrorInvalidOperation("%s: Insufficient buffer capacity remaining for"
532 : " transform feedback.",
533 0 : funcName);
534 0 : *out_error = true;
535 0 : return;
536 : }
537 :
538 0 : mUsedVerts = usedVerts.value();
539 : }
540 :
541 0 : void Advance() const {
542 0 : if (!mWithTF)
543 0 : return;
544 :
545 0 : mTFO->mActive_VertPosition += mUsedVerts;
546 : }
547 : };
548 :
549 : ////////////////////////////////////////
550 :
551 : void
552 0 : WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei vertCount)
553 : {
554 0 : const char funcName[] = "drawArrays";
555 0 : if (IsContextLost())
556 0 : return;
557 :
558 0 : MakeContextCurrent();
559 :
560 0 : bool error = false;
561 0 : ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
562 0 : if (error)
563 0 : return;
564 :
565 0 : const GLsizei instanceCount = 1;
566 0 : if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
567 0 : return;
568 :
569 0 : const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error);
570 0 : if (error)
571 0 : return;
572 :
573 : const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
574 0 : instanceCount, &error);
575 0 : if (error)
576 0 : return;
577 :
578 : {
579 0 : ScopedDrawCallWrapper wrapper(*this);
580 0 : gl->fDrawArrays(mode, first, vertCount);
581 : }
582 :
583 0 : Draw_cleanup(funcName);
584 0 : scopedTF.Advance();
585 : }
586 :
587 : void
588 0 : WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
589 : GLsizei instanceCount)
590 : {
591 0 : const char funcName[] = "drawArraysInstanced";
592 0 : if (IsContextLost())
593 0 : return;
594 :
595 0 : MakeContextCurrent();
596 :
597 0 : bool error = false;
598 0 : ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
599 0 : if (error)
600 0 : return;
601 :
602 0 : if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
603 0 : return;
604 :
605 0 : if (!DrawInstanced_check(funcName))
606 0 : return;
607 :
608 0 : const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error);
609 0 : if (error)
610 0 : return;
611 :
612 : const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
613 0 : instanceCount, &error);
614 0 : if (error)
615 0 : return;
616 :
617 : {
618 0 : ScopedDrawCallWrapper wrapper(*this);
619 0 : gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount);
620 : }
621 :
622 0 : Draw_cleanup(funcName);
623 0 : scopedTF.Advance();
624 : }
625 :
626 : ////////////////////////////////////////
627 :
628 : bool
629 0 : WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount,
630 : GLenum type, WebGLintptr byteOffset,
631 : GLsizei instanceCount)
632 : {
633 0 : if (!ValidateDrawModeEnum(mode, funcName))
634 0 : return false;
635 :
636 0 : if (mBoundTransformFeedback &&
637 0 : mBoundTransformFeedback->mIsActive &&
638 0 : !mBoundTransformFeedback->mIsPaused)
639 : {
640 : ErrorInvalidOperation("%s: DrawElements* functions are incompatible with"
641 : " transform feedback.",
642 0 : funcName);
643 0 : return false;
644 : }
645 :
646 0 : if (!ValidateNonNegative(funcName, "vertCount", vertCount) ||
647 0 : !ValidateNonNegative(funcName, "byteOffset", byteOffset) ||
648 0 : !ValidateNonNegative(funcName, "instanceCount", instanceCount))
649 : {
650 0 : return false;
651 : }
652 :
653 0 : if (!ValidateStencilParamsForDrawCall())
654 0 : return false;
655 :
656 0 : if (!vertCount || !instanceCount)
657 0 : return false; // No error, just early out.
658 :
659 0 : uint8_t bytesPerElem = 0;
660 0 : switch (type) {
661 : case LOCAL_GL_UNSIGNED_BYTE:
662 0 : bytesPerElem = 1;
663 0 : break;
664 :
665 : case LOCAL_GL_UNSIGNED_SHORT:
666 0 : bytesPerElem = 2;
667 0 : break;
668 :
669 : case LOCAL_GL_UNSIGNED_INT:
670 0 : if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
671 0 : bytesPerElem = 4;
672 : }
673 0 : break;
674 : }
675 :
676 0 : if (!bytesPerElem) {
677 0 : ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", funcName, type);
678 0 : return false;
679 : }
680 :
681 0 : if (byteOffset % bytesPerElem != 0) {
682 : ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`",
683 0 : funcName);
684 0 : return false;
685 : }
686 :
687 : ////
688 :
689 0 : if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
690 0 : MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
691 0 : if (mPrimRestartTypeBytes != bytesPerElem) {
692 0 : mPrimRestartTypeBytes = bytesPerElem;
693 :
694 0 : const uint32_t ones = UINT32_MAX >> (32 - 8*mPrimRestartTypeBytes);
695 0 : gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART);
696 0 : gl->fPrimitiveRestartIndex(ones);
697 : }
698 : }
699 :
700 : ////
701 :
702 0 : const GLsizei first = byteOffset / bytesPerElem;
703 0 : const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(vertCount);
704 :
705 0 : if (!checked_byteCount.isValid()) {
706 0 : ErrorInvalidValue("%s: Overflow in byteCount.", funcName);
707 0 : return false;
708 : }
709 :
710 0 : if (!mBoundVertexArray->mElementArrayBuffer) {
711 0 : ErrorInvalidOperation("%s: Must have element array buffer binding.", funcName);
712 0 : return false;
713 : }
714 :
715 0 : WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer;
716 :
717 0 : if (!elemArrayBuffer.ByteLength()) {
718 : ErrorInvalidOperation("%s: Bound element array buffer doesn't have any data.",
719 0 : funcName);
720 0 : return false;
721 : }
722 :
723 0 : CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset;
724 :
725 0 : if (!checked_neededByteCount.isValid()) {
726 0 : ErrorInvalidOperation("%s: Overflow in byteOffset+byteCount.", funcName);
727 0 : return false;
728 : }
729 :
730 0 : if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) {
731 : ErrorInvalidOperation("%s: Bound element array buffer is too small for given"
732 : " count and offset.",
733 0 : funcName);
734 0 : return false;
735 : }
736 :
737 0 : if (!ValidateBufferFetching(funcName))
738 0 : return false;
739 :
740 0 : if (!mMaxFetchedVertices ||
741 0 : !elemArrayBuffer.ValidateIndexedFetch(type, mMaxFetchedVertices, first, vertCount))
742 : {
743 : ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient "
744 : "size for given indices from the bound element array",
745 0 : funcName);
746 0 : return false;
747 : }
748 :
749 0 : return true;
750 : }
751 :
752 : static void
753 0 : HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName,
754 : gl::GLContext::LocalErrorScope& errorScope)
755 : {
756 0 : const auto err = errorScope.GetError();
757 0 : if (err == LOCAL_GL_INVALID_OPERATION) {
758 : webgl->ErrorInvalidOperation("%s: Driver rejected indexed draw call, possibly"
759 0 : " due to out-of-bounds indices.", funcName);
760 0 : return;
761 : }
762 :
763 0 : MOZ_ASSERT(!err);
764 0 : if (err) {
765 : webgl->ErrorImplementationBug("%s: Unexpected driver error during indexed draw"
766 : " call. Please file a bug.",
767 0 : funcName);
768 0 : return;
769 : }
770 : }
771 :
772 : void
773 0 : WebGLContext::DrawElements(GLenum mode, GLsizei vertCount, GLenum type,
774 : WebGLintptr byteOffset, const char* funcName)
775 : {
776 0 : if (!funcName) {
777 0 : funcName = "drawElements";
778 : }
779 :
780 0 : if (IsContextLost())
781 0 : return;
782 :
783 0 : MakeContextCurrent();
784 :
785 0 : bool error = false;
786 0 : ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
787 0 : if (error)
788 0 : return;
789 :
790 0 : const GLsizei instanceCount = 1;
791 0 : if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount))
792 0 : return;
793 :
794 : const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount,
795 0 : &error);
796 0 : if (error)
797 0 : return;
798 :
799 : {
800 0 : ScopedDrawCallWrapper wrapper(*this);
801 : {
802 0 : UniquePtr<gl::GLContext::LocalErrorScope> errorScope;
803 :
804 0 : if (gl->IsANGLE()) {
805 0 : errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
806 : }
807 :
808 0 : gl->fDrawElements(mode, vertCount, type,
809 0 : reinterpret_cast<GLvoid*>(byteOffset));
810 :
811 0 : if (errorScope) {
812 0 : HandleDrawElementsErrors(this, funcName, *errorScope);
813 : }
814 : }
815 : }
816 :
817 0 : Draw_cleanup(funcName);
818 : }
819 :
820 : void
821 0 : WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei vertCount, GLenum type,
822 : WebGLintptr byteOffset, GLsizei instanceCount)
823 : {
824 0 : const char funcName[] = "drawElementsInstanced";
825 0 : if (IsContextLost())
826 0 : return;
827 :
828 0 : MakeContextCurrent();
829 :
830 0 : bool error = false;
831 0 : ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
832 0 : if (error)
833 0 : return;
834 :
835 0 : if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount))
836 0 : return;
837 :
838 0 : if (!DrawInstanced_check(funcName))
839 0 : return;
840 :
841 : const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount,
842 0 : &error);
843 0 : if (error)
844 0 : return;
845 :
846 : {
847 0 : ScopedDrawCallWrapper wrapper(*this);
848 : {
849 0 : UniquePtr<gl::GLContext::LocalErrorScope> errorScope;
850 :
851 0 : if (gl->IsANGLE()) {
852 0 : errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
853 : }
854 :
855 0 : gl->fDrawElementsInstanced(mode, vertCount, type,
856 : reinterpret_cast<GLvoid*>(byteOffset),
857 0 : instanceCount);
858 0 : if (errorScope) {
859 0 : HandleDrawElementsErrors(this, funcName, *errorScope);
860 : }
861 : }
862 : }
863 :
864 0 : Draw_cleanup(funcName);
865 : }
866 :
867 : ////////////////////////////////////////
868 :
869 : void
870 0 : WebGLContext::Draw_cleanup(const char* funcName)
871 : {
872 0 : if (gl->WorkAroundDriverBugs()) {
873 0 : if (gl->Renderer() == gl::GLRenderer::Tegra) {
874 0 : mDrawCallsSinceLastFlush++;
875 :
876 0 : if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
877 0 : gl->fFlush();
878 0 : mDrawCallsSinceLastFlush = 0;
879 : }
880 : }
881 : }
882 :
883 : // Let's check for a really common error: Viewport is larger than the actual
884 : // destination framebuffer.
885 0 : uint32_t destWidth = mViewportWidth;
886 0 : uint32_t destHeight = mViewportHeight;
887 :
888 0 : if (mBoundDrawFramebuffer) {
889 0 : const auto& drawBuffers = mBoundDrawFramebuffer->ColorDrawBuffers();
890 0 : for (const auto& cur : drawBuffers) {
891 0 : if (!cur->IsDefined())
892 0 : continue;
893 0 : cur->Size(&destWidth, &destHeight);
894 0 : break;
895 : }
896 : } else {
897 0 : destWidth = mWidth;
898 0 : destHeight = mHeight;
899 : }
900 :
901 0 : if (mViewportWidth > int32_t(destWidth) ||
902 0 : mViewportHeight > int32_t(destHeight))
903 : {
904 0 : if (!mAlreadyWarnedAboutViewportLargerThanDest) {
905 : GenerateWarning("%s: Drawing to a destination rect smaller than the viewport"
906 : " rect. (This warning will only be given once)",
907 0 : funcName);
908 0 : mAlreadyWarnedAboutViewportLargerThanDest = true;
909 : }
910 : }
911 0 : }
912 :
913 : /*
914 : * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
915 : * that will be legal to be read from bound VBOs.
916 : */
917 :
918 : bool
919 0 : WebGLContext::ValidateBufferFetching(const char* info)
920 : {
921 0 : MOZ_ASSERT(mCurrentProgram);
922 : // Note that mCurrentProgram->IsLinked() is NOT GUARANTEED.
923 0 : MOZ_ASSERT(mActiveProgramLinkInfo);
924 :
925 : #ifdef DEBUG
926 0 : GLint currentProgram = 0;
927 0 : MakeContextCurrent();
928 0 : gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, ¤tProgram);
929 0 : MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->mGLName,
930 : "WebGL: current program doesn't agree with GL state");
931 : #endif
932 :
933 0 : if (mBufferFetchingIsVerified)
934 0 : return true;
935 :
936 0 : bool hasPerVertex = false;
937 0 : uint32_t maxVertices = UINT32_MAX;
938 0 : uint32_t maxInstances = UINT32_MAX;
939 0 : const uint32_t attribCount = mBoundVertexArray->mAttribs.Length();
940 :
941 0 : uint32_t i = 0;
942 0 : for (const auto& vd : mBoundVertexArray->mAttribs) {
943 : // If the attrib array isn't enabled, there's nothing to check;
944 : // it's a static value.
945 0 : if (!vd.mEnabled)
946 0 : continue;
947 :
948 0 : if (!vd.mBuf) {
949 : ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %du!",
950 0 : info, i);
951 0 : return false;
952 : }
953 :
954 0 : ++i;
955 : }
956 :
957 0 : mBufferFetch_IsAttrib0Active = false;
958 :
959 0 : for (const auto& attrib : mActiveProgramLinkInfo->attribs) {
960 0 : if (attrib.mLoc == -1)
961 0 : continue;
962 :
963 0 : const uint32_t attribLoc(attrib.mLoc);
964 0 : if (attribLoc >= attribCount)
965 0 : continue;
966 :
967 0 : if (attribLoc == 0) {
968 0 : mBufferFetch_IsAttrib0Active = true;
969 : }
970 :
971 0 : const auto& vd = mBoundVertexArray->mAttribs[attribLoc];
972 0 : if (!vd.mEnabled)
973 0 : continue;
974 :
975 0 : const auto& bufByteLen = vd.mBuf->ByteLength();
976 0 : if (vd.ByteOffset() > bufByteLen) {
977 0 : maxVertices = 0;
978 0 : maxInstances = 0;
979 0 : break;
980 : }
981 :
982 0 : size_t availBytes = bufByteLen - vd.ByteOffset();
983 0 : if (vd.BytesPerVertex() > availBytes) {
984 0 : maxVertices = 0;
985 0 : maxInstances = 0;
986 0 : break;
987 : }
988 0 : availBytes -= vd.BytesPerVertex(); // Snip off the tail.
989 0 : const size_t vertCapacity = availBytes / vd.ExplicitStride() + 1; // Add +1 for the snipped tail.
990 :
991 0 : if (vd.mDivisor == 0) {
992 0 : if (vertCapacity < maxVertices) {
993 0 : maxVertices = vertCapacity;
994 : }
995 0 : hasPerVertex = true;
996 : } else {
997 0 : const auto curMaxInstances = CheckedInt<size_t>(vertCapacity) * vd.mDivisor;
998 : // If this isn't valid, it's because we overflowed, which means we can support
999 : // *too much*. Don't update maxInstances in this case.
1000 0 : if (curMaxInstances.isValid() &&
1001 0 : curMaxInstances.value() < maxInstances)
1002 : {
1003 0 : maxInstances = curMaxInstances.value();
1004 : }
1005 : }
1006 : }
1007 :
1008 0 : mBufferFetchingIsVerified = true;
1009 0 : mBufferFetchingHasPerVertex = hasPerVertex;
1010 0 : mMaxFetchedVertices = maxVertices;
1011 0 : mMaxFetchedInstances = maxInstances;
1012 :
1013 0 : return true;
1014 : }
1015 :
1016 : WebGLVertexAttrib0Status
1017 0 : WebGLContext::WhatDoesVertexAttrib0Need() const
1018 : {
1019 0 : MOZ_ASSERT(mCurrentProgram);
1020 0 : MOZ_ASSERT(mActiveProgramLinkInfo);
1021 :
1022 0 : const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled;
1023 :
1024 0 : bool legacyAttrib0 = gl->IsCompatibilityProfile();
1025 : #ifdef XP_MACOSX
1026 : if (gl->WorkAroundDriverBugs()) {
1027 : // Failures in conformance/attribs/gl-disabled-vertex-attrib.
1028 : // Even in Core profiles on NV. Sigh.
1029 : legacyAttrib0 |= (gl->Vendor() == gl::GLVendor::NVIDIA);
1030 : }
1031 : #endif
1032 :
1033 0 : if (!legacyAttrib0)
1034 0 : return WebGLVertexAttrib0Status::Default;
1035 :
1036 0 : if (isAttribArray0Enabled && mBufferFetch_IsAttrib0Active)
1037 0 : return WebGLVertexAttrib0Status::Default;
1038 :
1039 0 : if (mBufferFetch_IsAttrib0Active)
1040 0 : return WebGLVertexAttrib0Status::EmulatedInitializedArray;
1041 :
1042 : // Ensure that the legacy code has enough buffer.
1043 0 : return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
1044 : }
1045 :
1046 : bool
1047 0 : WebGLContext::DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount)
1048 : {
1049 0 : if (!vertexCount) {
1050 0 : vertexCount = 1;
1051 : }
1052 :
1053 0 : const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
1054 0 : if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
1055 0 : return true;
1056 :
1057 0 : if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
1058 : GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
1059 : "to do expensive emulation work when running on desktop OpenGL "
1060 : "platforms, for example on Mac. It is preferable to always draw "
1061 : "with vertex attrib 0 array enabled, by using bindAttribLocation "
1062 0 : "to bind some always-used attribute to location 0.");
1063 0 : mAlreadyWarnedAboutFakeVertexAttrib0 = true;
1064 : }
1065 :
1066 0 : gl->fEnableVertexAttribArray(0);
1067 :
1068 0 : if (!mFakeVertexAttrib0BufferObject) {
1069 0 : gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
1070 0 : mFakeVertexAttrib0BufferObjectSize = 0;
1071 : }
1072 0 : gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
1073 :
1074 : ////
1075 :
1076 0 : switch (mGenericVertexAttribTypes[0]) {
1077 : case LOCAL_GL_FLOAT:
1078 0 : gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, false, 0, 0);
1079 0 : break;
1080 :
1081 : case LOCAL_GL_INT:
1082 0 : gl->fVertexAttribIPointer(0, 4, LOCAL_GL_INT, 0, 0);
1083 0 : break;
1084 :
1085 : case LOCAL_GL_UNSIGNED_INT:
1086 0 : gl->fVertexAttribIPointer(0, 4, LOCAL_GL_UNSIGNED_INT, 0, 0);
1087 0 : break;
1088 :
1089 : default:
1090 0 : MOZ_CRASH();
1091 : }
1092 :
1093 : ////
1094 :
1095 0 : const auto bytesPerVert = sizeof(mFakeVertexAttrib0Data);
1096 0 : const auto checked_dataSize = CheckedUint32(vertexCount) * bytesPerVert;
1097 0 : if (!checked_dataSize.isValid()) {
1098 : ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
1099 0 : "with %d vertices. Try reducing the number of vertices.", vertexCount);
1100 0 : return false;
1101 : }
1102 0 : const auto dataSize = checked_dataSize.value();
1103 :
1104 0 : if (mFakeVertexAttrib0BufferObjectSize < dataSize) {
1105 0 : gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
1106 0 : mFakeVertexAttrib0BufferObjectSize = dataSize;
1107 0 : mFakeVertexAttrib0DataDefined = false;
1108 : }
1109 :
1110 0 : if (whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray)
1111 0 : return true;
1112 :
1113 : ////
1114 :
1115 0 : if (mFakeVertexAttrib0DataDefined &&
1116 0 : memcmp(mFakeVertexAttrib0Data, mGenericVertexAttrib0Data, bytesPerVert) == 0)
1117 : {
1118 0 : return true;
1119 : }
1120 :
1121 : ////
1122 :
1123 0 : const UniqueBuffer data(malloc(dataSize));
1124 0 : if (!data) {
1125 : ErrorOutOfMemory("%s: Failed to allocate fake vertex attrib 0 array.",
1126 0 : funcName);
1127 0 : return false;
1128 : }
1129 0 : auto itr = (uint8_t*)data.get();
1130 0 : const auto itrEnd = itr + dataSize;
1131 0 : while (itr != itrEnd) {
1132 0 : memcpy(itr, mGenericVertexAttrib0Data, bytesPerVert);
1133 0 : itr += bytesPerVert;
1134 : }
1135 :
1136 : {
1137 0 : gl::GLContext::LocalErrorScope errorScope(*gl);
1138 :
1139 0 : gl->fBufferSubData(LOCAL_GL_ARRAY_BUFFER, 0, dataSize, data.get());
1140 :
1141 0 : const auto err = errorScope.GetError();
1142 0 : if (err) {
1143 0 : ErrorOutOfMemory("%s: Failed to upload fake vertex attrib 0 data.", funcName);
1144 0 : return false;
1145 : }
1146 : }
1147 :
1148 : ////
1149 :
1150 0 : memcpy(mFakeVertexAttrib0Data, mGenericVertexAttrib0Data, bytesPerVert);
1151 0 : mFakeVertexAttrib0DataDefined = true;
1152 0 : return true;
1153 : }
1154 :
1155 : void
1156 0 : WebGLContext::UndoFakeVertexAttrib0()
1157 : {
1158 0 : const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
1159 0 : if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
1160 0 : return;
1161 :
1162 0 : if (mBoundVertexArray->mAttribs[0].mBuf) {
1163 0 : const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
1164 0 : gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.mBuf->mGLName);
1165 0 : attrib0.DoVertexAttribPointer(gl, 0);
1166 : } else {
1167 0 : gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
1168 : }
1169 :
1170 0 : gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
1171 : }
1172 :
1173 : static GLuint
1174 0 : CreateGLTexture(gl::GLContext* gl)
1175 : {
1176 0 : MOZ_ASSERT(gl->IsCurrent());
1177 0 : GLuint ret = 0;
1178 0 : gl->fGenTextures(1, &ret);
1179 0 : return ret;
1180 : }
1181 :
1182 : UniquePtr<WebGLContext::FakeBlackTexture>
1183 0 : WebGLContext::FakeBlackTexture::Create(gl::GLContext* gl, TexTarget target,
1184 : FakeBlackType type)
1185 : {
1186 : GLenum texFormat;
1187 0 : switch (type) {
1188 : case FakeBlackType::RGBA0000:
1189 0 : texFormat = LOCAL_GL_RGBA;
1190 0 : break;
1191 :
1192 : case FakeBlackType::RGBA0001:
1193 0 : texFormat = LOCAL_GL_RGB;
1194 0 : break;
1195 :
1196 : default:
1197 0 : MOZ_CRASH("GFX: bad type");
1198 : }
1199 :
1200 0 : UniquePtr<FakeBlackTexture> result(new FakeBlackTexture(gl));
1201 0 : gl::ScopedBindTexture scopedBind(gl, result->mGLName, target.get());
1202 :
1203 0 : gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
1204 0 : gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
1205 :
1206 : // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to
1207 : // minimize the risk of running into a driver bug in texImage2D, as it is a bit
1208 : // unusual maybe to create 1x1 textures, and the stack may not have the alignment that
1209 : // TexImage2D expects.
1210 :
1211 0 : const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE};
1212 0 : UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation.
1213 :
1214 0 : MOZ_ASSERT(gl->IsCurrent());
1215 :
1216 0 : if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
1217 0 : for (int i = 0; i < 6; ++i) {
1218 0 : const TexImageTarget curTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
1219 0 : const GLenum error = DoTexImage(gl, curTarget.get(), 0, &dui, 1, 1, 1,
1220 0 : zeros.get());
1221 0 : if (error) {
1222 0 : return nullptr;
1223 : }
1224 : }
1225 : } else {
1226 0 : const GLenum error = DoTexImage(gl, target.get(), 0, &dui, 1, 1, 1,
1227 0 : zeros.get());
1228 0 : if (error) {
1229 0 : return nullptr;
1230 : }
1231 : }
1232 :
1233 0 : return result;
1234 : }
1235 :
1236 0 : WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl)
1237 : : mGL(gl)
1238 0 : , mGLName(CreateGLTexture(gl))
1239 : {
1240 0 : }
1241 :
1242 0 : WebGLContext::FakeBlackTexture::~FakeBlackTexture()
1243 : {
1244 0 : mGL->MakeCurrent();
1245 0 : mGL->fDeleteTextures(1, &mGLName);
1246 0 : }
1247 :
1248 : } // namespace mozilla
|