/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
{
decltype(SamplerByTexUnit::sampler) prevSamplerForTexUnit = nullptr; for (constauto& cur : samplerByTexUnit) { if (cur.texUnit == texUnit) {
prevSamplerForTexUnit = cur.sampler;
}
} if (!prevSamplerForTexUnit) {
prevSamplerForTexUnit = &uniform;
MOZ_RELEASE_ASSERT(samplerByTexUnit.append(
SamplerByTexUnit{texUnit, prevSamplerForTexUnit}));
}
if (MOZ_UNLIKELY(&uniform.texListForType !=
&prevSamplerForTexUnit->texListForType)) { // Pointing to different tex lists means different types! constauto linkInfo = mWebGL->mActiveProgramLinkInfo; constauto LocInfoBySampler = [&](const webgl::SamplerUniformInfo* p)
-> const webgl::LocationInfo* { for (constauto& pair : linkInfo->locationMap) { constauto& locInfo = pair.second; if (locInfo.samplerInfo == p) { return &locInfo;
}
}
MOZ_CRASH("Can't find sampler location.");
}; constauto& cur = *LocInfoBySampler(&uniform); constauto& prev = *LocInfoBySampler(prevSamplerForTexUnit);
mWebGL->ErrorInvalidOperation( "Tex unit %u referenced by samplers of different types:" " %s (via %s) and %s (via %s).",
texUnit, EnumString(cur.info.info.elemType).c_str(),
cur.PrettyName().c_str(),
EnumString(prev.info.info.elemType).c_str(),
prev.PrettyName().c_str());
*out_error = true; return;
}
}
constauto& tex = texList[texUnit]; if (!tex) continue;
constauto& sampler = mWebGL->mBoundSamplers[texUnit]; constauto& samplingInfo = tex->GetSampleableInfo(sampler.get()); if (MOZ_UNLIKELY(!samplingInfo)) { // There was an error.
*out_error = true; return;
} if (MOZ_UNLIKELY(!samplingInfo->IsComplete())) { if (samplingInfo->incompleteReason) { constauto& targetName = GetEnumName(tex->Target().get());
mWebGL->GenerateWarning("%s at unit %u is incomplete: %s", targetName,
texUnit, samplingInfo->incompleteReason);
}
mRebindRequests.push_back({texUnit, tex}); continue;
}
// We have more validation to do if we're otherwise complete: constauto& texBaseType = samplingInfo->usage->format->baseType; if (MOZ_UNLIKELY(texBaseType != uniformBaseType)) { constauto& targetName = GetEnumName(tex->Target().get()); constauto& srcType = ToString(texBaseType); constauto& dstType = ToString(uniformBaseType);
mWebGL->ErrorInvalidOperation( "%s at unit %u is of type %s, but" " the shader samples as %s.",
targetName, texUnit, srcType, dstType);
*out_error = true; return;
}
if (MOZ_UNLIKELY(uniform.isShadowSampler !=
samplingInfo->isDepthTexCompare)) { constauto& targetName = GetEnumName(tex->Target().get());
mWebGL->ErrorInvalidOperation( "%s at unit %u is%s a depth texture" " with TEXTURE_COMPARE_MODE, but" " the shader sampler is%s a shadow" " sampler.",
targetName, texUnit, samplingInfo->isDepthTexCompare ? "" : " not",
uniform.isShadowSampler ? "" : " not");
*out_error = true; return;
}
constauto& gl = mWebGL->gl; for (constauto& itr : mRebindRequests) {
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + itr.texUnit);
GLuint incompleteTex = 0; // Tex 0 is always incomplete. constauto& overrideTex = webgl->mIncompleteTexOverride; if (overrideTex) { // In all but the simplest cases, this will be incomplete anyway, since // e.g. int-samplers need int-textures. This is useful for e.g. // dom-to-texture failures, though.
incompleteTex = overrideTex->name;
}
gl->fBindTexture(itr.tex->Target().get(), incompleteTex);
}
}
ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw() { if (mRebindRequests.empty()) return;
bool ok = true;
ok &= (fnMask(mStencilWriteMaskFront) == fnMask(mStencilWriteMaskBack));
ok &= (fnMask(mStencilValueMaskFront) == fnMask(mStencilValueMaskBack));
ok &= (fnClamp(mStencilRefFront) == fnClamp(mStencilRefBack));
if (!ok) {
ErrorInvalidOperation( "Stencil front/back state must effectively match." " (before front/back comparison, WRITEMASK and VALUE_MASK" " are masked with (2^s)-1, and REF is clamped to" " [0, (2^s)-1], where `s` is the number of enabled stencil" " bits in the draw framebuffer)");
} return ok;
}
// -
void WebGLContext::GenErrorIllegalUse(const GLenum useTarget, const uint32_t useId, const GLenum boundTarget, const uint32_t boundId) const { constauto fnName = [&](const GLenum target, const uint32_t id) { auto name = nsCString(EnumString(target).c_str()); if (id != static_cast<uint32_t>(-1)) {
name += nsPrintfCString("[%u]", id);
} return name;
}; constauto& useName = fnName(useTarget, useId); constauto& boundName = fnName(boundTarget, boundId);
GenerateError(LOCAL_GL_INVALID_OPERATION, "Illegal use of buffer at %s" " while also bound to %s.",
useName.BeginReading(), boundName.BeginReading());
}
for (constauto i : IntegerRange(mIndexedUniformBufferBindings.size())) { constauto& cur = mIndexedUniformBufferBindings[i];
fnCheck(cur.mBufferBinding.get(), LOCAL_GL_UNIFORM_BUFFER, i);
}
fnCheck(mBoundVertexArray->mElementArrayBuffer.get(),
LOCAL_GL_ELEMENT_ARRAY_BUFFER, -1); for (constauto i : IntegerRange(MaxVertexAttribs())) { constauto& binding = mBoundVertexArray->AttribBinding(i);
fnCheck(binding.buffer.get(), LOCAL_GL_ARRAY_BUFFER, i);
}
template <size_t N> static size_t FindFirstOne(const std::bitset<N>& bs) {
MOZ_ASSERT(bs.any()); // We don't need this to be fast, so don't bother with CLZ intrinsics. for (constauto i : IntegerRange(N)) { if (bs[i]) return i;
} return -1;
}
constauto& fb = webgl->mBoundDrawFramebuffer; if (fb) { constauto& info = *fb->GetCompletenessInfo(); constauto isF32WithBlending = info.isAttachmentF32 & webgl->mBlendEnabled; if (isF32WithBlending.any()) { if (!webgl->IsExtensionEnabled(WebGLExtensionID::EXT_float_blend)) { constauto first = FindFirstOne(isF32WithBlending);
webgl->ErrorInvalidOperation( "Attachment %u is float32 with blending enabled, which requires " "EXT_float_blend.",
uint32_t(first)); return nullptr;
}
webgl->WarnIfImplicit(WebGLExtensionID::EXT_float_blend);
}
}
switch (mode) { case LOCAL_GL_TRIANGLES: case LOCAL_GL_TRIANGLE_STRIP: case LOCAL_GL_TRIANGLE_FAN: case LOCAL_GL_POINTS: case LOCAL_GL_LINE_STRIP: case LOCAL_GL_LINE_LOOP: case LOCAL_GL_LINES: break; default:
webgl->ErrorInvalidEnumInfo("mode", mode); return nullptr;
}
if (!webgl->ValidateStencilParamsForDrawCall()) return nullptr;
if (!webgl->mActiveProgramLinkInfo) {
webgl->ErrorInvalidOperation("The current program is not linked."); return nullptr;
} constauto& linkInfo = webgl->mActiveProgramLinkInfo;
// - // Check UBO sizes.
for (constauto i : IntegerRange(linkInfo->uniformBlocks.size())) { constauto& cur = linkInfo->uniformBlocks[i]; constauto& dataSize = cur.info.dataSize; constauto& binding = cur.binding; if (!binding) {
webgl->ErrorInvalidOperation("Buffer for uniform block is null."); return nullptr;
}
constauto availByteCount = binding->ByteCount(); if (dataSize > availByteCount) {
webgl->ErrorInvalidOperation( "Buffer for uniform block is smaller" " than UNIFORM_BLOCK_DATA_SIZE."); return nullptr;
}
if (!webgl->ValidateBufferForNonTf(binding->mBufferBinding,
LOCAL_GL_UNIFORM_BUFFER, i)) return nullptr;
}
// -
constauto& tfo = webgl->mBoundTransformFeedback; if (tfo && tfo->IsActiveAndNotPaused()) { if (fb) { constauto& info = *fb->GetCompletenessInfo(); if (info.isMultiview) {
webgl->ErrorInvalidOperation( "Cannot render to multiview with transform feedback."); return nullptr;
}
}
if (!webgl->ValidateBuffersForTf(*tfo, *linkInfo)) return nullptr;
}
constauto writable =
hasAttachment & drawBufferEnabled & webgl->mColorWriteMaskNonzero; if (writable.any()) { // Do we have any undefined outputs with real attachments that // aren't masked-out by color write mask or drawBuffers? constauto wouldWriteUndefined = ~linkInfo->hasOutput & writable; if (wouldWriteUndefined.any()) { constauto first = FindFirstOne(wouldWriteUndefined);
webgl->ErrorInvalidOperation( "Program has no frag output at location %u, the" " destination draw buffer has an attached" " image, and its color write mask is not all false," " and DRAW_BUFFER%u is not NONE.",
uint32_t(first), uint32_t(first)); return nullptr;
}
if (tfo) { for (constauto& used : fetchLimits->usedBuffers) {
MOZ_ASSERT(used.buffer); if (!webgl->ValidateBufferForNonTf(*used.buffer, LOCAL_GL_ARRAY_BUFFER,
used.id)) return nullptr;
}
}
if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); if (mPrimRestartTypeBytes != 0) {
mPrimRestartTypeBytes = 0;
// OSX appears to have severe perf issues with leaving this enabled.
gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART);
}
}
// -
constauto fetchLimits = ValidateDraw(this, mode, instanceCount); if (!fetchLimits) return;
// -
constauto totalVertCount_safe = CheckedInt<uint32_t>(first) + vertCount; if (!totalVertCount_safe.isValid()) {
ErrorOutOfMemory("`first+vertCount` out of range."); return;
} auto totalVertCount = totalVertCount_safe.value();
if (vertCount && instanceCount && totalVertCount > fetchLimits->maxVerts) {
ErrorInvalidOperation( "Vertex fetch requires %u, but attribs only supply %u.", totalVertCount,
uint32_t(fetchLimits->maxVerts)); return;
}
if (vertCount > mMaxVertIdsPerDraw) {
ErrorOutOfMemory( "Context's max vertCount is %u, but %u requested. " "[webgl.max-vert-ids-per-draw]",
mMaxVertIdsPerDraw, vertCount); return;
}
// -
bool error = false;
// -
const ScopedResolveTexturesForDraw scopedResolve(this, &error); if (error) return;
const ScopedDrawWithTransformFeedback scopedTF(this, mode, vertCount,
instanceCount, &error); if (error) return;
// On MacOS (Intel?), `first` in glDrawArrays also increases where instanced // attribs are fetched from. There are two ways to fix this: // 1. DrawElements with a [0,1,2,...] index buffer, converting `first` to // `byteOffset` // 2. OR offset all non-instanced vertex attrib pointers back, and call // DrawArrays with first:0. // * But now gl_VertexID will be wrong! So we inject a uniform to offset it // back correctly. // #1 ought to be the lowest overhead for any first>0, // but DrawElements can't be used with transform-feedback, // so we need #2 to also work. // For now, only implement #2.
if (first && mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst) { // This is not particularly optimized, but we can if we need to. bool hasInstancedUserAttrib = false; bool hasVertexAttrib = false; for (constauto& a : activeAttribs) { if (a.location == -1) { if (a.name == "gl_VertexID") {
hasVertexAttrib = true;
} continue;
} constauto& binding = mBoundVertexArray->AttribBinding(a.location); if (binding.layout.divisor) {
hasInstancedUserAttrib = true;
} else {
hasVertexAttrib = true;
}
} if (hasInstancedUserAttrib && hasVertexAttrib) {
driverFirst = 0;
}
} if (driverFirst != first) { for (constauto& a : activeAttribs) { if (a.location == -1) continue; constauto& binding = mBoundVertexArray->AttribBinding(a.location); if (binding.layout.divisor) continue;
if (driverFirst != first) {
gl->fUniform1i(mActiveProgramLinkInfo->webgl_gl_VertexID_Offset, 0);
for (constauto& a : activeAttribs) { if (a.location == -1) continue; constauto& binding = mBoundVertexArray->AttribBinding(a.location); if (binding.layout.divisor) continue;
case LOCAL_GL_UNSIGNED_SHORT:
bytesPerIndex = 2; break;
case LOCAL_GL_UNSIGNED_INT: if (IsWebGL2() ||
IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
bytesPerIndex = 4;
} break;
} if (!bytesPerIndex) {
ErrorInvalidEnumInfo("type", type); return nullptr;
} if (byteOffset % bytesPerIndex != 0) {
ErrorInvalidOperation( "`byteOffset` must be a multiple of the size of `type`"); return nullptr;
}
////
if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); if (mPrimRestartTypeBytes != bytesPerIndex) {
mPrimRestartTypeBytes = bytesPerIndex;
if (indexCount > mMaxVertIdsPerDraw) {
ErrorOutOfMemory( "Context's max indexCount is %u, but %u requested. " "[webgl.max-vert-ids-per-draw]",
mMaxVertIdsPerDraw, indexCount); return;
}
// -
bool error = false;
// -
auto undoAttrib0 = MakeScopeExit([&]() {
MOZ_RELEASE_ASSERT(whatDoesAttrib0Need !=
WebGLVertexAttrib0Status::Default);
UndoFakeVertexAttrib0();
}); if (fakeVertCount) { if (!DoFakeVertexAttrib0(fakeVertCount, whatDoesAttrib0Need)) {
error = true;
undoAttrib0.release();
}
} else { // No fake-verts needed.
undoAttrib0.release();
}
// -
const ScopedResolveTexturesForDraw scopedResolve(this, &error); if (error) return;
{
ScopedDrawCallWrapper wrapper(*this);
{
std::unique_ptr<gl::GLContext::LocalErrorScope> errorScope; if (MOZ_UNLIKELY(gl->IsANGLE() &&
gl->mDebugFlags &
gl::GLContext::DebugFlagAbortOnError)) { // ANGLE does range validation even when it doesn't need to. // With MOZ_GL_ABORT_ON_ERROR, we need to catch it or hit assertions.
errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
}
// Let's check for a really common error: Viewport is larger than the actual // destination framebuffer.
uint32_t destWidth;
uint32_t destHeight; if (mBoundDrawFramebuffer) { constauto& info = mBoundDrawFramebuffer->GetCompletenessInfo();
destWidth = info->width;
destHeight = info->height;
} else {
destWidth = mDefaultFB->mSize.width;
destHeight = mDefaultFB->mSize.height;
}
if (mViewportWidth > int32_t(destWidth) ||
mViewportHeight > int32_t(destHeight)) { if (!mAlreadyWarnedAboutViewportLargerThanDest) {
GenerateWarning( "Drawing to a destination rect smaller than the viewport" " rect. (This warning will only be given once)");
mAlreadyWarnedAboutViewportLargerThanDest = true;
}
}
}
bool legacyAttrib0 = mNeedsLegacyVertexAttrib0Handling; if (gl->WorkAroundDriverBugs() && kIsMacOS) { // Also programs with no attribs: // conformance/attribs/gl-vertex-attrib-unconsumed-out-of-bounds.html constauto& activeAttribs = mActiveProgramLinkInfo->active.activeAttribs; bool hasNonInstancedUserAttrib = false; for (constauto& a : activeAttribs) { if (a.location == -1) continue; constauto& layout = mBoundVertexArray->AttribBinding(a.location).layout; if (layout.divisor == 0) {
hasNonInstancedUserAttrib = true;
}
}
legacyAttrib0 |= !hasNonInstancedUserAttrib;
}
if (!legacyAttrib0) return WebGLVertexAttrib0Status::Default;
MOZ_RELEASE_ASSERT(mMaybeNeedsLegacyVertexAttrib0Handling, "Invariant need because this turns on index buffer " "validation, needed for fake-attrib0.");
if (!mActiveProgramLinkInfo->attrib0Active) { // Attrib0 unused, so just ensure that the legacy code has enough buffer. return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
}
if (gl->WorkAroundDriverBugs() && gl->IsMesa()) { // Padded/strided to vec4, so 4x4bytes. constauto effectiveVertAttribBytes =
CheckedInt<int32_t>(fakeVertexCount) * 4 * 4; if (!effectiveVertAttribBytes.isValid()) {
ErrorOutOfMemory("`offset + count` too large for Mesa."); returnfalse;
}
}
if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
GenerateWarning( "Drawing without vertex attrib 0 array enabled forces the browser " "to do expensive emulation work when running on desktop OpenGL " "platforms, for example on Mac. It is preferable to always draw " "with vertex attrib 0 array enabled, by using bindAttribLocation " "to bind some always-used attribute to location 0.");
mAlreadyWarnedAboutFakeVertexAttrib0 = true;
}
switch (mGenericVertexAttribTypes[0]) { case webgl::AttribBaseType::Boolean: case webgl::AttribBaseType::Float:
gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, false, 0, 0); break;
case webgl::AttribBaseType::Int:
gl->fVertexAttribIPointer(0, 4, LOCAL_GL_INT, 0, 0); break;
case webgl::AttribBaseType::Uint:
gl->fVertexAttribIPointer(0, 4, LOCAL_GL_UNSIGNED_INT, 0, 0); break;
}
////
constauto maxFakeVerts = StaticPrefs::webgl_fake_verts_max(); if (fakeVertexCount > maxFakeVerts) {
ErrorOutOfMemory( "Draw requires faking a vertex attrib 0 array, but required vert count" " (%" PRIu64 ") is more than webgl.fake-verts.max (%u).",
fakeVertexCount, maxFakeVerts); returnfalse;
}
constauto bytesPerVert = sizeof(mFakeVertexAttrib0Data); constauto checked_dataSize =
CheckedInt<intptr_t>(fakeVertexCount) * bytesPerVert; if (!checked_dataSize.isValid()) {
ErrorOutOfMemory( "Integer overflow trying to construct a fake vertex attrib 0" " array for a draw-operation with %" PRIu64 " vertices. Try" " reducing the number of vertices.",
fakeVertexCount); returnfalse;
} constauto dataSize = checked_dataSize.value();
if (mFakeVertexAttrib0BufferObjectSize < dataSize) {
gl::GLContext::LocalErrorScope errorScope(*gl);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.