1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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"
8 #include "MozFramebuffer.h"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/ProfilerLabels.h"
12 #include "mozilla/UniquePtrExtensions.h"
13 #include "nsPrintfCString.h"
14 #include "WebGLBuffer.h"
15 #include "WebGLContextUtils.h"
16 #include "WebGLFramebuffer.h"
17 #include "WebGLProgram.h"
18 #include "WebGLRenderbuffer.h"
19 #include "WebGLShader.h"
20 #include "WebGLTexture.h"
21 #include "WebGLTransformFeedback.h"
22 #include "WebGLVertexArray.h"
28 // For a Tegra workaround.
29 static const int MAX_DRAW_CALLS_SINCE_FLUSH
= 100;
31 ////////////////////////////////////////
33 class ScopedResolveTexturesForDraw
{
34 struct TexRebindRequest
{
39 WebGLContext
* const mWebGL
;
40 std::vector
<TexRebindRequest
> mRebindRequests
;
43 ScopedResolveTexturesForDraw(WebGLContext
* webgl
, bool* const out_error
);
44 ~ScopedResolveTexturesForDraw();
47 static bool ValidateNoSamplingFeedback(const WebGLTexture
& tex
,
48 const uint32_t sampledLevels
,
49 const WebGLFramebuffer
* const fb
,
50 const uint32_t texUnit
) {
53 const auto& texAttachments
= fb
->GetCompletenessInfo()->texAttachments
;
54 for (const auto& attach
: texAttachments
) {
55 if (attach
->Texture() != &tex
) continue;
57 const auto& srcBase
= tex
.Es3_level_base();
58 const auto srcLast
= srcBase
+ sampledLevels
- 1;
59 const auto& dstLevel
= attach
->MipLevel();
60 if (MOZ_UNLIKELY(srcBase
<= dstLevel
&& dstLevel
<= srcLast
)) {
61 const auto& webgl
= tex
.mContext
;
62 const auto& texTargetStr
= EnumString(tex
.Target().get());
63 const auto& attachStr
= EnumString(attach
->mAttachmentPoint
);
64 webgl
->ErrorInvalidOperation(
65 "Texture level %u would be read by %s unit %u,"
66 " but written by framebuffer attachment %s,"
67 " which would be illegal feedback.",
68 dstLevel
, texTargetStr
.c_str(), texUnit
, attachStr
.c_str());
75 ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(
76 WebGLContext
* webgl
, bool* const out_error
)
78 const auto& fb
= mWebGL
->mBoundDrawFramebuffer
;
80 MOZ_ASSERT(mWebGL
->mActiveProgramLinkInfo
);
81 const auto& samplerUniforms
= mWebGL
->mActiveProgramLinkInfo
->samplerUniforms
;
82 for (const auto& pUniform
: samplerUniforms
) {
83 const auto& uniform
= *pUniform
;
84 const auto& texList
= uniform
.texListForType
;
86 const auto& uniformBaseType
= uniform
.texBaseType
;
87 for (const auto& texUnit
: uniform
.texUnits
) {
88 MOZ_ASSERT(texUnit
< texList
.Length());
90 const auto& tex
= texList
[texUnit
];
93 const auto& sampler
= mWebGL
->mBoundSamplers
[texUnit
];
94 const auto& samplingInfo
= tex
->GetSampleableInfo(sampler
.get());
95 if (!samplingInfo
) { // There was an error.
99 if (!samplingInfo
->IsComplete()) {
100 if (samplingInfo
->incompleteReason
) {
101 const auto& targetName
= GetEnumName(tex
->Target().get());
102 mWebGL
->GenerateWarning("%s at unit %u is incomplete: %s", targetName
,
103 texUnit
, samplingInfo
->incompleteReason
);
105 mRebindRequests
.push_back({texUnit
, tex
});
109 // We have more validation to do if we're otherwise complete:
110 const auto& texBaseType
= samplingInfo
->usage
->format
->baseType
;
111 if (texBaseType
!= uniformBaseType
) {
112 const auto& targetName
= GetEnumName(tex
->Target().get());
113 const auto& srcType
= ToString(texBaseType
);
114 const auto& dstType
= ToString(uniformBaseType
);
115 mWebGL
->ErrorInvalidOperation(
116 "%s at unit %u is of type %s, but"
117 " the shader samples as %s.",
118 targetName
, texUnit
, srcType
, dstType
);
123 if (uniform
.isShadowSampler
!= samplingInfo
->isDepthTexCompare
) {
124 const auto& targetName
= GetEnumName(tex
->Target().get());
125 mWebGL
->ErrorInvalidOperation(
126 "%s at unit %u is%s a depth texture"
127 " with TEXTURE_COMPARE_MODE, but"
128 " the shader sampler is%s a shadow"
130 targetName
, texUnit
, samplingInfo
->isDepthTexCompare
? "" : " not",
131 uniform
.isShadowSampler
? "" : " not");
136 if (!ValidateNoSamplingFeedback(*tex
, samplingInfo
->levels
, fb
.get(),
144 const auto& gl
= mWebGL
->gl
;
145 for (const auto& itr
: mRebindRequests
) {
146 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ itr
.texUnit
);
147 GLuint incompleteTex
= 0; // Tex 0 is always incomplete.
148 const auto& overrideTex
= webgl
->mIncompleteTexOverride
;
150 // In all but the simplest cases, this will be incomplete anyway, since
151 // e.g. int-samplers need int-textures. This is useful for e.g.
152 // dom-to-texture failures, though.
153 incompleteTex
= overrideTex
->name
;
155 gl
->fBindTexture(itr
.tex
->Target().get(), incompleteTex
);
159 ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw() {
160 if (mRebindRequests
.empty()) return;
162 gl::GLContext
* gl
= mWebGL
->gl
;
164 for (const auto& itr
: mRebindRequests
) {
165 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ itr
.texUnit
);
166 gl
->fBindTexture(itr
.tex
->Target().get(), itr
.tex
->mGLName
);
169 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ mWebGL
->mActiveTexture
);
172 ////////////////////////////////////////
174 bool WebGLContext::ValidateStencilParamsForDrawCall() const {
175 const auto stencilBits
= [&]() -> uint8_t {
176 if (!mStencilTestEnabled
) return 0;
178 if (!mBoundDrawFramebuffer
) return mOptions
.stencil
? 8 : 0;
180 if (mBoundDrawFramebuffer
->StencilAttachment().HasAttachment()) return 8;
182 if (mBoundDrawFramebuffer
->DepthStencilAttachment().HasAttachment())
187 const uint32_t stencilMax
= (1 << stencilBits
) - 1;
189 const auto fnMask
= [&](const uint32_t x
) { return x
& stencilMax
; };
190 const auto fnClamp
= [&](const int32_t x
) {
191 return std::max(0, std::min(x
, (int32_t)stencilMax
));
195 ok
&= (fnMask(mStencilWriteMaskFront
) == fnMask(mStencilWriteMaskBack
));
196 ok
&= (fnMask(mStencilValueMaskFront
) == fnMask(mStencilValueMaskBack
));
197 ok
&= (fnClamp(mStencilRefFront
) == fnClamp(mStencilRefBack
));
200 ErrorInvalidOperation(
201 "Stencil front/back state must effectively match."
202 " (before front/back comparison, WRITEMASK and VALUE_MASK"
203 " are masked with (2^s)-1, and REF is clamped to"
204 " [0, (2^s)-1], where `s` is the number of enabled stencil"
205 " bits in the draw framebuffer)");
212 void WebGLContext::GenErrorIllegalUse(const GLenum useTarget
,
213 const uint32_t useId
,
214 const GLenum boundTarget
,
215 const uint32_t boundId
) const {
216 const auto fnName
= [&](const GLenum target
, const uint32_t id
) {
217 auto name
= nsCString(EnumString(target
).c_str());
218 if (id
!= static_cast<uint32_t>(-1)) {
219 name
+= nsPrintfCString("[%u]", id
);
223 const auto& useName
= fnName(useTarget
, useId
);
224 const auto& boundName
= fnName(boundTarget
, boundId
);
225 GenerateError(LOCAL_GL_INVALID_OPERATION
,
226 "Illegal use of buffer at %s"
227 " while also bound to %s.",
228 useName
.BeginReading(), boundName
.BeginReading());
231 bool WebGLContext::ValidateBufferForNonTf(const WebGLBuffer
& nonTfBuffer
,
232 const GLenum nonTfTarget
,
233 const uint32_t nonTfId
) const {
235 const auto& tfAttribs
= mBoundTransformFeedback
->mIndexedBindings
;
236 for (const auto& cur
: tfAttribs
) {
237 dupe
|= (&nonTfBuffer
== cur
.mBufferBinding
.get());
239 if (MOZ_LIKELY(!dupe
)) return true;
242 for (const auto tfId
: IntegerRange(tfAttribs
.size())) {
243 const auto& tfBuffer
= tfAttribs
[tfId
].mBufferBinding
;
244 if (&nonTfBuffer
== tfBuffer
) {
246 GenErrorIllegalUse(nonTfTarget
, nonTfId
,
247 LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
, tfId
);
254 bool WebGLContext::ValidateBuffersForTf(
255 const WebGLTransformFeedback
& tfo
,
256 const webgl::LinkedProgramInfo
& linkInfo
) const {
258 switch (linkInfo
.transformFeedbackBufferMode
) {
259 case LOCAL_GL_INTERLEAVED_ATTRIBS
:
263 case LOCAL_GL_SEPARATE_ATTRIBS
:
264 numUsed
= linkInfo
.active
.activeTfVaryings
.size();
271 std::vector
<webgl::BufferAndIndex
> tfBuffers
;
272 tfBuffers
.reserve(numUsed
);
273 for (const auto i
: IntegerRange(numUsed
)) {
274 tfBuffers
.push_back({tfo
.mIndexedBindings
[i
].mBufferBinding
.get(),
275 static_cast<uint32_t>(i
)});
278 return ValidateBuffersForTf(tfBuffers
);
281 bool WebGLContext::ValidateBuffersForTf(
282 const std::vector
<webgl::BufferAndIndex
>& tfBuffers
) const {
284 const auto fnCheck
= [&](const WebGLBuffer
* const nonTf
,
285 const GLenum nonTfTarget
, const uint32_t nonTfId
) {
286 for (const auto& tf
: tfBuffers
) {
287 dupe
|= (nonTf
&& tf
.buffer
== nonTf
);
290 if (MOZ_LIKELY(!dupe
)) return false;
292 for (const auto& tf
: tfBuffers
) {
293 if (nonTf
&& tf
.buffer
== nonTf
) {
295 GenErrorIllegalUse(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
, tf
.id
,
296 nonTfTarget
, nonTfId
);
302 fnCheck(mBoundArrayBuffer
.get(), LOCAL_GL_ARRAY_BUFFER
, -1);
303 fnCheck(mBoundCopyReadBuffer
.get(), LOCAL_GL_COPY_READ_BUFFER
, -1);
304 fnCheck(mBoundCopyWriteBuffer
.get(), LOCAL_GL_COPY_WRITE_BUFFER
, -1);
305 fnCheck(mBoundPixelPackBuffer
.get(), LOCAL_GL_PIXEL_PACK_BUFFER
, -1);
306 fnCheck(mBoundPixelUnpackBuffer
.get(), LOCAL_GL_PIXEL_UNPACK_BUFFER
, -1);
307 // fnCheck(mBoundTransformFeedbackBuffer.get(),
308 // LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, -1);
309 fnCheck(mBoundUniformBuffer
.get(), LOCAL_GL_UNIFORM_BUFFER
, -1);
311 for (const auto i
: IntegerRange(mIndexedUniformBufferBindings
.size())) {
312 const auto& cur
= mIndexedUniformBufferBindings
[i
];
313 fnCheck(cur
.mBufferBinding
.get(), LOCAL_GL_UNIFORM_BUFFER
, i
);
316 fnCheck(mBoundVertexArray
->mElementArrayBuffer
.get(),
317 LOCAL_GL_ELEMENT_ARRAY_BUFFER
, -1);
318 for (const auto i
: IntegerRange(MaxVertexAttribs())) {
319 const auto& binding
= mBoundVertexArray
->AttribBinding(i
);
320 fnCheck(binding
.buffer
.get(), LOCAL_GL_ARRAY_BUFFER
, i
);
326 ////////////////////////////////////////
328 template <typename T
>
329 static bool DoSetsIntersect(const std::set
<T
>& a
, const std::set
<T
>& b
) {
330 std::vector
<T
> intersection
;
331 std::set_intersection(a
.begin(), a
.end(), b
.begin(), b
.end(),
332 std::back_inserter(intersection
));
333 return bool(intersection
.size());
336 const webgl::CachedDrawFetchLimits
* ValidateDraw(WebGLContext
* const webgl
,
338 const uint32_t instanceCount
) {
339 if (!webgl
->BindCurFBForDraw()) return nullptr;
341 const auto& fb
= webgl
->mBoundDrawFramebuffer
;
342 if (fb
&& webgl
->mBlendEnabled
) {
343 const auto& info
= *fb
->GetCompletenessInfo();
344 if (info
.hasFloat32
) {
345 if (!webgl
->IsExtensionEnabled(WebGLExtensionID::EXT_float_blend
)) {
346 webgl
->ErrorInvalidOperation(
347 "Float32 blending requires EXT_float_blend.");
350 webgl
->WarnIfImplicit(WebGLExtensionID::EXT_float_blend
);
355 case LOCAL_GL_TRIANGLES
:
356 case LOCAL_GL_TRIANGLE_STRIP
:
357 case LOCAL_GL_TRIANGLE_FAN
:
358 case LOCAL_GL_POINTS
:
359 case LOCAL_GL_LINE_STRIP
:
360 case LOCAL_GL_LINE_LOOP
:
364 webgl
->ErrorInvalidEnumInfo("mode", mode
);
368 if (!webgl
->ValidateStencilParamsForDrawCall()) return nullptr;
370 if (!webgl
->mActiveProgramLinkInfo
) {
371 webgl
->ErrorInvalidOperation("The current program is not linked.");
374 const auto& linkInfo
= webgl
->mActiveProgramLinkInfo
;
379 for (const auto i
: IntegerRange(linkInfo
->uniformBlocks
.size())) {
380 const auto& cur
= linkInfo
->uniformBlocks
[i
];
381 const auto& dataSize
= cur
.info
.dataSize
;
382 const auto& binding
= cur
.binding
;
384 webgl
->ErrorInvalidOperation("Buffer for uniform block is null.");
388 const auto availByteCount
= binding
->ByteCount();
389 if (dataSize
> availByteCount
) {
390 webgl
->ErrorInvalidOperation(
391 "Buffer for uniform block is smaller"
392 " than UNIFORM_BLOCK_DATA_SIZE.");
396 if (!webgl
->ValidateBufferForNonTf(binding
->mBufferBinding
,
397 LOCAL_GL_UNIFORM_BUFFER
, i
))
403 const auto& tfo
= webgl
->mBoundTransformFeedback
;
404 if (tfo
&& tfo
->IsActiveAndNotPaused()) {
406 const auto& info
= *fb
->GetCompletenessInfo();
407 if (info
.isMultiview
) {
408 webgl
->ErrorInvalidOperation(
409 "Cannot render to multiview with transform feedback.");
414 if (!webgl
->ValidateBuffersForTf(*tfo
, *linkInfo
)) return nullptr;
419 const auto& fragOutputs
= linkInfo
->fragOutputs
;
420 const auto fnValidateFragOutputType
=
421 [&](const uint8_t loc
, const webgl::TextureBaseType dstBaseType
) {
422 const auto itr
= fragOutputs
.find(loc
);
423 if (MOZ_UNLIKELY(itr
== fragOutputs
.end())) {
424 webgl
->ErrorInvalidOperation(
425 "Program has no frag output at location %u, but"
426 " destination draw buffer has an attached"
432 const auto& info
= itr
->second
;
433 const auto& srcBaseType
= info
.baseType
;
434 if (MOZ_UNLIKELY(dstBaseType
!= srcBaseType
)) {
435 const auto& srcStr
= ToString(srcBaseType
);
436 const auto& dstStr
= ToString(dstBaseType
);
437 webgl
->ErrorInvalidOperation(
438 "Program frag output at location %u is type %s,"
439 " but destination draw buffer is type %s.",
440 uint32_t(loc
), srcStr
, dstStr
);
446 if (!webgl
->mRasterizerDiscardEnabled
) {
447 uint8_t fbZLayerCount
= 1;
449 const auto& info
= *fb
->GetCompletenessInfo();
450 fbZLayerCount
= info
.zLayerCount
;
453 if (fbZLayerCount
!= linkInfo
->zLayerCount
) {
454 webgl
->ErrorInvalidOperation(
455 "Multiview count mismatch: shader: %u, framebuffer: %u",
456 uint32_t{linkInfo
->zLayerCount
}, uint32_t{fbZLayerCount
});
460 if (webgl
->mColorWriteMask
) {
462 for (const auto& attach
: fb
->ColorDrawBuffers()) {
464 uint8_t(attach
->mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
);
465 const auto& imageInfo
= attach
->GetImageInfo();
466 if (!imageInfo
) continue;
467 const auto& dstBaseType
= imageInfo
->mFormat
->format
->baseType
;
468 if (!fnValidateFragOutputType(i
, dstBaseType
)) return nullptr;
471 if (!fnValidateFragOutputType(0, webgl::TextureBaseType::Float
))
479 const auto fetchLimits
= linkInfo
->GetDrawFetchLimits();
480 if (!fetchLimits
) return nullptr;
482 if (instanceCount
> fetchLimits
->maxInstances
) {
483 webgl
->ErrorInvalidOperation(
484 "Instance fetch requires %u, but attribs only"
486 instanceCount
, uint32_t(fetchLimits
->maxInstances
));
491 for (const auto& used
: fetchLimits
->usedBuffers
) {
492 MOZ_ASSERT(used
.buffer
);
493 if (!webgl
->ValidateBufferForNonTf(*used
.buffer
, LOCAL_GL_ARRAY_BUFFER
,
501 webgl
->RunContextLossTimer();
506 ////////////////////////////////////////
508 class ScopedFakeVertexAttrib0 final
{
509 WebGLContext
* const mWebGL
;
510 bool mDidFake
= false;
513 ScopedFakeVertexAttrib0(WebGLContext
* const webgl
, const uint64_t vertexCount
,
514 bool* const out_error
)
518 if (!mWebGL
->DoFakeVertexAttrib0(vertexCount
)) {
525 ~ScopedFakeVertexAttrib0() {
527 mWebGL
->UndoFakeVertexAttrib0();
532 ////////////////////////////////////////
534 static uint32_t UsedVertsForTFDraw(GLenum mode
, uint32_t vertCount
) {
535 uint8_t vertsPerPrim
;
538 case LOCAL_GL_POINTS
:
544 case LOCAL_GL_TRIANGLES
:
551 return vertCount
/ vertsPerPrim
* vertsPerPrim
;
554 class ScopedDrawWithTransformFeedback final
{
555 WebGLContext
* const mWebGL
;
556 WebGLTransformFeedback
* const mTFO
;
561 ScopedDrawWithTransformFeedback(WebGLContext
* webgl
, GLenum mode
,
562 uint32_t vertCount
, uint32_t instanceCount
,
563 bool* const out_error
)
565 mTFO(mWebGL
->mBoundTransformFeedback
),
566 mWithTF(mTFO
&& mTFO
->mIsActive
&& !mTFO
->mIsPaused
),
569 if (!mWithTF
) return;
571 if (mode
!= mTFO
->mActive_PrimMode
) {
572 mWebGL
->ErrorInvalidOperation(
573 "Drawing with transform feedback requires"
574 " `mode` to match BeginTransformFeedback's"
575 " `primitiveMode`.");
580 const auto usedVertsPerInstance
= UsedVertsForTFDraw(mode
, vertCount
);
581 const auto usedVerts
=
582 CheckedInt
<uint32_t>(usedVertsPerInstance
) * instanceCount
;
584 const auto remainingCapacity
=
585 mTFO
->mActive_VertCapacity
- mTFO
->mActive_VertPosition
;
586 if (!usedVerts
.isValid() || usedVerts
.value() > remainingCapacity
) {
587 mWebGL
->ErrorInvalidOperation(
588 "Insufficient buffer capacity remaining for"
589 " transform feedback.");
594 mUsedVerts
= usedVerts
.value();
597 void Advance() const {
598 if (!mWithTF
) return;
600 mTFO
->mActive_VertPosition
+= mUsedVerts
;
602 for (const auto& cur
: mTFO
->mIndexedBindings
) {
603 const auto& buffer
= cur
.mBufferBinding
;
605 buffer
->ResetLastUpdateFenceId();
611 static bool HasInstancedDrawing(const WebGLContext
& webgl
) {
612 return webgl
.IsWebGL2() ||
613 webgl
.IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays
);
616 ////////////////////////////////////////
618 void WebGLContext::DrawArraysInstanced(GLenum mode
, GLint first
,
620 GLsizei instanceCount
) {
621 const FuncScope
funcScope(*this, "drawArraysInstanced");
622 AUTO_PROFILER_LABEL("WebGLContext::DrawArraysInstanced", GRAPHICS
);
623 if (IsContextLost()) return;
624 const gl::GLContext::TlsScope
inTls(gl
);
628 if (!ValidateNonNegative("first", first
) ||
629 !ValidateNonNegative("vertCount", vertCount
) ||
630 !ValidateNonNegative("instanceCount", instanceCount
)) {
634 if (IsWebGL2() && !gl
->IsSupported(gl::GLFeature::prim_restart_fixed
)) {
635 MOZ_ASSERT(gl
->IsSupported(gl::GLFeature::prim_restart
));
636 if (mPrimRestartTypeBytes
!= 0) {
637 mPrimRestartTypeBytes
= 0;
639 // OSX appears to have severe perf issues with leaving this enabled.
640 gl
->fDisable(LOCAL_GL_PRIMITIVE_RESTART
);
646 const auto fetchLimits
= ValidateDraw(this, mode
, instanceCount
);
647 if (!fetchLimits
) return;
651 const auto totalVertCount_safe
= CheckedInt
<uint32_t>(first
) + vertCount
;
652 if (!totalVertCount_safe
.isValid()) {
653 ErrorOutOfMemory("`first+vertCount` out of range.");
656 auto totalVertCount
= totalVertCount_safe
.value();
658 if (vertCount
&& instanceCount
&& totalVertCount
> fetchLimits
->maxVerts
) {
659 ErrorInvalidOperation(
660 "Vertex fetch requires %u, but attribs only supply %u.", totalVertCount
,
661 uint32_t(fetchLimits
->maxVerts
));
668 const ScopedFakeVertexAttrib0
attrib0(this, totalVertCount
, &error
);
671 const ScopedResolveTexturesForDraw
scopedResolve(this, &error
);
674 const ScopedDrawWithTransformFeedback
scopedTF(this, mode
, vertCount
,
675 instanceCount
, &error
);
679 ScopedDrawCallWrapper
wrapper(*this);
680 if (vertCount
&& instanceCount
) {
681 AUTO_PROFILER_LABEL("glDrawArraysInstanced", GRAPHICS
);
682 if (HasInstancedDrawing(*this)) {
683 gl
->fDrawArraysInstanced(mode
, first
, vertCount
, instanceCount
);
685 MOZ_ASSERT(instanceCount
== 1);
686 gl
->fDrawArrays(mode
, first
, vertCount
);
695 ////////////////////////////////////////
697 WebGLBuffer
* WebGLContext::DrawElements_check(const GLsizei rawIndexCount
,
699 const WebGLintptr byteOffset
,
700 const GLsizei instanceCount
) {
701 if (mBoundTransformFeedback
&& mBoundTransformFeedback
->mIsActive
&&
702 !mBoundTransformFeedback
->mIsPaused
) {
703 ErrorInvalidOperation(
704 "DrawElements* functions are incompatible with"
705 " transform feedback.");
709 if (!ValidateNonNegative("vertCount", rawIndexCount
) ||
710 !ValidateNonNegative("byteOffset", byteOffset
) ||
711 !ValidateNonNegative("instanceCount", instanceCount
)) {
714 const auto indexCount
= uint32_t(rawIndexCount
);
716 uint8_t bytesPerIndex
= 0;
718 case LOCAL_GL_UNSIGNED_BYTE
:
722 case LOCAL_GL_UNSIGNED_SHORT
:
726 case LOCAL_GL_UNSIGNED_INT
:
728 IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint
)) {
733 if (!bytesPerIndex
) {
734 ErrorInvalidEnumInfo("type", type
);
737 if (byteOffset
% bytesPerIndex
!= 0) {
738 ErrorInvalidOperation(
739 "`byteOffset` must be a multiple of the size of `type`");
745 if (IsWebGL2() && !gl
->IsSupported(gl::GLFeature::prim_restart_fixed
)) {
746 MOZ_ASSERT(gl
->IsSupported(gl::GLFeature::prim_restart
));
747 if (mPrimRestartTypeBytes
!= bytesPerIndex
) {
748 mPrimRestartTypeBytes
= bytesPerIndex
;
750 const uint32_t ones
= UINT32_MAX
>> (32 - 8 * mPrimRestartTypeBytes
);
751 gl
->fEnable(LOCAL_GL_PRIMITIVE_RESTART
);
752 gl
->fPrimitiveRestartIndex(ones
);
759 const auto& indexBuffer
= mBoundVertexArray
->mElementArrayBuffer
;
761 ErrorInvalidOperation("Index buffer not bound.");
765 const size_t availBytes
= indexBuffer
->ByteLength();
766 const auto availIndices
=
767 AvailGroups(availBytes
, byteOffset
, bytesPerIndex
, bytesPerIndex
);
768 if (instanceCount
&& indexCount
> availIndices
) {
769 ErrorInvalidOperation("Index buffer too small.");
773 return indexBuffer
.get();
776 static void HandleDrawElementsErrors(
777 WebGLContext
* webgl
, gl::GLContext::LocalErrorScope
& errorScope
) {
778 const auto err
= errorScope
.GetError();
779 if (err
== LOCAL_GL_INVALID_OPERATION
) {
780 webgl
->ErrorInvalidOperation(
781 "Driver rejected indexed draw call, possibly"
782 " due to out-of-bounds indices.");
788 webgl
->ErrorImplementationBug(
789 "Unexpected driver error during indexed draw"
790 " call. Please file a bug.");
795 void WebGLContext::DrawElementsInstanced(GLenum mode
, GLsizei indexCount
,
796 GLenum type
, WebGLintptr byteOffset
,
797 GLsizei instanceCount
) {
798 const FuncScope
funcScope(*this, "drawElementsInstanced");
799 AUTO_PROFILER_LABEL("WebGLContext::DrawElementsInstanced", GRAPHICS
);
800 if (IsContextLost()) return;
802 const gl::GLContext::TlsScope
inTls(gl
);
804 const auto indexBuffer
=
805 DrawElements_check(indexCount
, type
, byteOffset
, instanceCount
);
806 if (!indexBuffer
) return;
810 const auto fetchLimits
= ValidateDraw(this, mode
, instanceCount
);
811 if (!fetchLimits
) return;
813 bool collapseToDrawArrays
= false;
814 auto fakeVertCount
= fetchLimits
->maxVerts
;
815 if (fetchLimits
->maxVerts
== UINT64_MAX
) {
816 // This isn't observable, and keeps FakeVertexAttrib0 sane.
817 collapseToDrawArrays
= true;
824 uint64_t indexCapacity
= indexBuffer
->ByteLength();
826 case LOCAL_GL_UNSIGNED_BYTE
:
828 case LOCAL_GL_UNSIGNED_SHORT
:
831 case LOCAL_GL_UNSIGNED_INT
:
836 uint32_t maxVertId
= 0;
837 const auto isFetchValid
= [&]() {
838 if (!indexCount
|| !instanceCount
) return true;
840 const auto globalMaxVertId
=
841 indexBuffer
->GetIndexedFetchMaxVert(type
, 0, indexCapacity
);
842 if (!globalMaxVertId
) return true;
843 if (globalMaxVertId
.value() < fetchLimits
->maxVerts
) return true;
845 const auto exactMaxVertId
=
846 indexBuffer
->GetIndexedFetchMaxVert(type
, byteOffset
, indexCount
);
847 maxVertId
= exactMaxVertId
.value();
848 return maxVertId
< fetchLimits
->maxVerts
;
851 ErrorInvalidOperation(
852 "Indexed vertex fetch requires %u vertices, but"
853 " attribs only supply %u.",
854 maxVertId
+ 1, uint32_t(fetchLimits
->maxVerts
));
862 const ScopedFakeVertexAttrib0
attrib0(this, fakeVertCount
, &error
);
865 const ScopedResolveTexturesForDraw
scopedResolve(this, &error
);
869 ScopedDrawCallWrapper
wrapper(*this);
871 UniquePtr
<gl::GLContext::LocalErrorScope
> errorScope
;
872 if (MOZ_UNLIKELY(gl
->IsANGLE() &&
874 gl::GLContext::DebugFlagAbortOnError
)) {
875 // ANGLE does range validation even when it doesn't need to.
876 // With MOZ_GL_ABORT_ON_ERROR, we need to catch it or hit assertions.
877 errorScope
.reset(new gl::GLContext::LocalErrorScope(*gl
));
880 if (indexCount
&& instanceCount
) {
881 AUTO_PROFILER_LABEL("glDrawElementsInstanced", GRAPHICS
);
882 if (HasInstancedDrawing(*this)) {
883 if (MOZ_UNLIKELY(collapseToDrawArrays
)) {
884 gl
->fDrawArraysInstanced(mode
, 0, 1, instanceCount
);
886 gl
->fDrawElementsInstanced(mode
, indexCount
, type
,
887 reinterpret_cast<GLvoid
*>(byteOffset
),
891 MOZ_ASSERT(instanceCount
== 1);
892 if (MOZ_UNLIKELY(collapseToDrawArrays
)) {
893 gl
->fDrawArrays(mode
, 0, 1);
895 gl
->fDrawElements(mode
, indexCount
, type
,
896 reinterpret_cast<GLvoid
*>(byteOffset
));
902 HandleDrawElementsErrors(this, *errorScope
);
910 ////////////////////////////////////////
912 void WebGLContext::Draw_cleanup() {
913 if (gl
->WorkAroundDriverBugs()) {
914 if (gl
->Renderer() == gl::GLRenderer::Tegra
) {
915 mDrawCallsSinceLastFlush
++;
917 if (mDrawCallsSinceLastFlush
>= MAX_DRAW_CALLS_SINCE_FLUSH
) {
919 mDrawCallsSinceLastFlush
= 0;
924 // Let's check for a really common error: Viewport is larger than the actual
925 // destination framebuffer.
928 if (mBoundDrawFramebuffer
) {
929 const auto& info
= mBoundDrawFramebuffer
->GetCompletenessInfo();
930 destWidth
= info
->width
;
931 destHeight
= info
->height
;
933 destWidth
= mDefaultFB
->mSize
.width
;
934 destHeight
= mDefaultFB
->mSize
.height
;
937 if (mViewportWidth
> int32_t(destWidth
) ||
938 mViewportHeight
> int32_t(destHeight
)) {
939 if (!mAlreadyWarnedAboutViewportLargerThanDest
) {
941 "Drawing to a destination rect smaller than the viewport"
942 " rect. (This warning will only be given once)");
943 mAlreadyWarnedAboutViewportLargerThanDest
= true;
948 WebGLVertexAttrib0Status
WebGLContext::WhatDoesVertexAttrib0Need() const {
949 MOZ_ASSERT(mCurrentProgram
);
950 MOZ_ASSERT(mActiveProgramLinkInfo
);
952 bool legacyAttrib0
= gl
->IsCompatibilityProfile();
954 if (gl
->WorkAroundDriverBugs()) {
955 // Failures in conformance/attribs/gl-disabled-vertex-attrib.
956 // Even in Core profiles on NV. Sigh.
957 legacyAttrib0
|= (gl
->Vendor() == gl::GLVendor::NVIDIA
);
959 // Also programs with no attribs:
960 // conformance/attribs/gl-vertex-attrib-unconsumed-out-of-bounds.html
961 legacyAttrib0
|= !mActiveProgramLinkInfo
->active
.activeAttribs
.size();
965 if (!legacyAttrib0
) return WebGLVertexAttrib0Status::Default
;
967 if (!mActiveProgramLinkInfo
->attrib0Active
) {
968 // Ensure that the legacy code has enough buffer.
969 return WebGLVertexAttrib0Status::EmulatedUninitializedArray
;
972 const auto& isAttribArray0Enabled
=
973 mBoundVertexArray
->AttribBinding(0).layout
.isArray
;
974 return isAttribArray0Enabled
975 ? WebGLVertexAttrib0Status::Default
976 : WebGLVertexAttrib0Status::EmulatedInitializedArray
;
979 bool WebGLContext::DoFakeVertexAttrib0(const uint64_t vertexCount
) {
980 const auto whatDoesAttrib0Need
= WhatDoesVertexAttrib0Need();
981 if (MOZ_LIKELY(whatDoesAttrib0Need
== WebGLVertexAttrib0Status::Default
))
984 if (!mAlreadyWarnedAboutFakeVertexAttrib0
) {
986 "Drawing without vertex attrib 0 array enabled forces the browser "
987 "to do expensive emulation work when running on desktop OpenGL "
988 "platforms, for example on Mac. It is preferable to always draw "
989 "with vertex attrib 0 array enabled, by using bindAttribLocation "
990 "to bind some always-used attribute to location 0.");
991 mAlreadyWarnedAboutFakeVertexAttrib0
= true;
994 gl
->fEnableVertexAttribArray(0);
996 if (!mFakeVertexAttrib0BufferObject
) {
997 gl
->fGenBuffers(1, &mFakeVertexAttrib0BufferObject
);
998 mFakeVertexAttrib0BufferObjectSize
= 0;
1000 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, mFakeVertexAttrib0BufferObject
);
1004 switch (mGenericVertexAttribTypes
[0]) {
1005 case webgl::AttribBaseType::Boolean
:
1006 case webgl::AttribBaseType::Float
:
1007 gl
->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT
, false, 0, 0);
1010 case webgl::AttribBaseType::Int
:
1011 gl
->fVertexAttribIPointer(0, 4, LOCAL_GL_INT
, 0, 0);
1014 case webgl::AttribBaseType::Uint
:
1015 gl
->fVertexAttribIPointer(0, 4, LOCAL_GL_UNSIGNED_INT
, 0, 0);
1021 const auto bytesPerVert
= sizeof(mFakeVertexAttrib0Data
);
1022 const auto checked_dataSize
= CheckedUint32(vertexCount
) * bytesPerVert
;
1023 if (!checked_dataSize
.isValid()) {
1025 "Integer overflow trying to construct a fake vertex attrib 0"
1026 " array for a draw-operation with %" PRIu64
1028 " reducing the number of vertices.",
1032 const auto dataSize
= checked_dataSize
.value();
1034 if (mFakeVertexAttrib0BufferObjectSize
< dataSize
) {
1035 gl
->fBufferData(LOCAL_GL_ARRAY_BUFFER
, dataSize
, nullptr,
1036 LOCAL_GL_DYNAMIC_DRAW
);
1037 mFakeVertexAttrib0BufferObjectSize
= dataSize
;
1038 mFakeVertexAttrib0DataDefined
= false;
1041 if (whatDoesAttrib0Need
==
1042 WebGLVertexAttrib0Status::EmulatedUninitializedArray
)
1047 if (mFakeVertexAttrib0DataDefined
&&
1048 memcmp(mFakeVertexAttrib0Data
, mGenericVertexAttrib0Data
, bytesPerVert
) ==
1055 const UniqueBuffer
data(malloc(dataSize
));
1057 ErrorOutOfMemory("Failed to allocate fake vertex attrib 0 array.");
1060 auto itr
= (uint8_t*)data
.get();
1061 const auto itrEnd
= itr
+ dataSize
;
1062 while (itr
!= itrEnd
) {
1063 memcpy(itr
, mGenericVertexAttrib0Data
, bytesPerVert
);
1064 itr
+= bytesPerVert
;
1068 gl::GLContext::LocalErrorScope
errorScope(*gl
);
1070 gl
->fBufferSubData(LOCAL_GL_ARRAY_BUFFER
, 0, dataSize
, data
.get());
1072 const auto err
= errorScope
.GetError();
1074 ErrorOutOfMemory("Failed to upload fake vertex attrib 0 data.");
1081 memcpy(mFakeVertexAttrib0Data
, mGenericVertexAttrib0Data
, bytesPerVert
);
1082 mFakeVertexAttrib0DataDefined
= true;
1086 void WebGLContext::UndoFakeVertexAttrib0() {
1087 const auto whatDoesAttrib0Need
= WhatDoesVertexAttrib0Need();
1088 if (MOZ_LIKELY(whatDoesAttrib0Need
== WebGLVertexAttrib0Status::Default
))
1091 const auto& binding
= mBoundVertexArray
->AttribBinding(0);
1092 const auto& buffer
= binding
.buffer
;
1094 static_assert(IsBufferTargetLazilyBound(LOCAL_GL_ARRAY_BUFFER
));
1097 const auto& desc
= mBoundVertexArray
->AttribDesc(0);
1099 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, buffer
->mGLName
);
1100 DoVertexAttribPointer(*gl
, 0, desc
);
1101 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, 0);
1105 } // namespace mozilla