/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
staticbool ValidateBlendFuncEnum(WebGLContext* webgl, GLenum factor, constchar* varName) { switch (factor) { case LOCAL_GL_ZERO: case LOCAL_GL_ONE: case LOCAL_GL_SRC_COLOR: case LOCAL_GL_ONE_MINUS_SRC_COLOR: case LOCAL_GL_DST_COLOR: case LOCAL_GL_ONE_MINUS_DST_COLOR: case LOCAL_GL_SRC_ALPHA: case LOCAL_GL_ONE_MINUS_SRC_ALPHA: case LOCAL_GL_DST_ALPHA: case LOCAL_GL_ONE_MINUS_DST_ALPHA: case LOCAL_GL_CONSTANT_COLOR: case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR: case LOCAL_GL_CONSTANT_ALPHA: case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA: case LOCAL_GL_SRC_ALPHA_SATURATE: returntrue;
void WebGLContext::BlendFuncSeparate(Maybe<GLuint> i, GLenum srcRGB,
GLenum dstRGB, GLenum srcAlpha,
GLenum dstAlpha) { const FuncScope funcScope(*this, "blendFuncSeparate"); if (IsContextLost()) return;
if (!ValidateBlendFuncEnums(this, srcRGB, srcAlpha, dstRGB, dstAlpha)) return;
// note that we only check compatibity for the RGB enums, no need to for the // Alpha enums, see "Section 6.8 forgetting to mention alpha factors?" thread // on the public_webgl mailing list if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "srcRGB and dstRGB")) return;
if (i) {
MOZ_RELEASE_ASSERT(
IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed)); constauto limit = MaxValidDrawBuffers(); if (*i >= limit) {
ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i, "MAX_DRAW_BUFFERS", limit); return;
}
staticbool ValidateComparisonEnum(WebGLContext& webgl, const GLenum func) { switch (func) { case LOCAL_GL_NEVER: case LOCAL_GL_LESS: case LOCAL_GL_LEQUAL: case LOCAL_GL_GREATER: case LOCAL_GL_GEQUAL: case LOCAL_GL_EQUAL: case LOCAL_GL_NOTEQUAL: case LOCAL_GL_ALWAYS: returntrue;
if (fb) return fb->GetAttachmentParameter(attachment, pname);
////////////////////////////////////
if (!IsWebGL2()) {
ErrorInvalidOperation( "Querying against the default framebuffer is not" " allowed in WebGL 1."); return Nothing();
}
switch (attachment) { case LOCAL_GL_BACK: case LOCAL_GL_DEPTH: case LOCAL_GL_STENCIL: break;
default:
ErrorInvalidEnum( "For the default framebuffer, can only query COLOR, DEPTH," " or STENCIL."); return Nothing();
}
switch (pname) { case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: switch (attachment) { case LOCAL_GL_BACK: break; case LOCAL_GL_DEPTH: if (!mOptions.depth) { return Some(LOCAL_GL_NONE);
} break; case LOCAL_GL_STENCIL: if (!mOptions.stencil) { return Some(LOCAL_GL_NONE);
} break; default:
ErrorInvalidEnum( "With the default framebuffer, can only query COLOR, DEPTH," " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE"); return Nothing();
} return Some(LOCAL_GL_FRAMEBUFFER_DEFAULT);
////////////////
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: if (attachment == LOCAL_GL_BACK) return Some(8); return Some(0);
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: if (attachment == LOCAL_GL_BACK) { if (mOptions.alpha) { return Some(8);
}
ErrorInvalidOperation( "The default framebuffer doesn't contain an alpha buffer"); return Nothing();
} return Some(0);
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: if (attachment == LOCAL_GL_DEPTH) { if (mOptions.depth) { return Some(24);
}
ErrorInvalidOperation( "The default framebuffer doesn't contain an depth buffer"); return Nothing();
} return Some(0);
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: if (attachment == LOCAL_GL_STENCIL) { if (mOptions.stencil) { return Some(8);
}
ErrorInvalidOperation( "The default framebuffer doesn't contain an stencil buffer"); return Nothing();
} return Some(0);
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: if (attachment == LOCAL_GL_STENCIL) { if (mOptions.stencil) { return Some(LOCAL_GL_UNSIGNED_INT);
}
ErrorInvalidOperation( "The default framebuffer doesn't contain an stencil buffer");
} elseif (attachment == LOCAL_GL_DEPTH) { if (mOptions.depth) { return Some(LOCAL_GL_UNSIGNED_NORMALIZED);
}
ErrorInvalidOperation( "The default framebuffer doesn't contain an depth buffer");
} else { // LOCAL_GL_BACK return Some(LOCAL_GL_UNSIGNED_NORMALIZED);
} return Nothing();
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: if (attachment == LOCAL_GL_STENCIL) { if (!mOptions.stencil) {
ErrorInvalidOperation( "The default framebuffer doesn't contain an stencil buffer"); return Nothing();
}
} elseif (attachment == LOCAL_GL_DEPTH) { if (!mOptions.depth) {
ErrorInvalidOperation( "The default framebuffer doesn't contain an depth buffer"); return Nothing();
}
} return Some(LOCAL_GL_LINEAR);
}
switch (pname) { case LOCAL_GL_RENDERBUFFER_SAMPLES: if (!IsWebGL2()) break;
[[fallthrough]];
case LOCAL_GL_RENDERBUFFER_WIDTH: case LOCAL_GL_RENDERBUFFER_HEIGHT: case LOCAL_GL_RENDERBUFFER_RED_SIZE: case LOCAL_GL_RENDERBUFFER_GREEN_SIZE: case LOCAL_GL_RENDERBUFFER_BLUE_SIZE: case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: { // RB emulation means we have to ask the RB itself.
GLint i = rb.GetRenderbufferParameter(pname); return Some(i);
}
auto err = mWebGLError;
mWebGLError = 0; if (IsContextLost() || err) // Must check IsContextLost in all flow paths. return err;
// Either no WebGL-side error, or it's already been cleared. // UnderlyingGL-side errors, now.
err = gl->fGetError(); if (gl->IsContextLost()) {
CheckForContextLoss(); return GetError();
}
MOZ_ASSERT(err != LOCAL_GL_CONTEXT_LOST);
if (err) {
GenerateWarning("Driver error unexpected by WebGL: 0x%04x", err); // This might be: // - INVALID_OPERATION from ANGLE due to incomplete RBAB implementation for // DrawElements // with DYNAMIC_DRAW index buffer.
} return err;
}
constauto& info = prog.LinkInfo(); if (!info) return;
constauto locInfo = MaybeFind(info->locationMap, loc); if (!locInfo) return;
ret.type = locInfo->info.info.elemType; switch (ret.type) { case LOCAL_GL_FLOAT: case LOCAL_GL_FLOAT_VEC2: case LOCAL_GL_FLOAT_VEC3: case LOCAL_GL_FLOAT_VEC4: case LOCAL_GL_FLOAT_MAT2: case LOCAL_GL_FLOAT_MAT3: case LOCAL_GL_FLOAT_MAT4: case LOCAL_GL_FLOAT_MAT2x3: case LOCAL_GL_FLOAT_MAT2x4: case LOCAL_GL_FLOAT_MAT3x2: case LOCAL_GL_FLOAT_MAT3x4: case LOCAL_GL_FLOAT_MAT4x2: case LOCAL_GL_FLOAT_MAT4x3:
gl->fGetUniformfv(prog.mGLName, loc, reinterpret_cast<float*>(ret.data.data())); break;
case LOCAL_GL_INT: case LOCAL_GL_INT_VEC2: case LOCAL_GL_INT_VEC3: case LOCAL_GL_INT_VEC4: case LOCAL_GL_SAMPLER_2D: case LOCAL_GL_SAMPLER_3D: case LOCAL_GL_SAMPLER_CUBE: case LOCAL_GL_SAMPLER_2D_SHADOW: case LOCAL_GL_SAMPLER_2D_ARRAY: case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: case LOCAL_GL_SAMPLER_CUBE_SHADOW: case LOCAL_GL_INT_SAMPLER_2D: case LOCAL_GL_INT_SAMPLER_3D: case LOCAL_GL_INT_SAMPLER_CUBE: case LOCAL_GL_INT_SAMPLER_2D_ARRAY: case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: case LOCAL_GL_BOOL: case LOCAL_GL_BOOL_VEC2: case LOCAL_GL_BOOL_VEC3: case LOCAL_GL_BOOL_VEC4:
gl->fGetUniformiv(prog.mGLName, loc, reinterpret_cast<int32_t*>(ret.data.data())); break;
case LOCAL_GL_UNSIGNED_INT: case LOCAL_GL_UNSIGNED_INT_VEC2: case LOCAL_GL_UNSIGNED_INT_VEC3: case LOCAL_GL_UNSIGNED_INT_VEC4:
gl->fGetUniformuiv(prog.mGLName, loc, reinterpret_cast<uint32_t*>(ret.data.data())); break;
if (&prog == mCurrentProgram) { if (!prog.IsLinked()) { // We use to simply early-out here, and preserve the GL behavior that // failed relink doesn't invalidate the current active program link info. // The new behavior was changed for WebGL here: // https://github.com/KhronosGroup/WebGL/pull/3371
mActiveProgramLinkInfo = nullptr;
gl->fUseProgram(0); // Shouldn't be needed, but let's be safe. return;
}
mActiveProgramLinkInfo = prog.LinkInfo();
gl->fUseProgram(prog.mGLName); // Uncontionally re-use. // Previously, we needed this re-use on nvidia as a driver workaround, // but we might as well do it unconditionally.
}
}
case LOCAL_GL_UNPACK_SKIP_IMAGES:
pValueSlot = &unpacking->skipImages; break;
case LOCAL_GL_UNPACK_ROW_LENGTH:
pValueSlot = &unpacking->rowLength; break;
case LOCAL_GL_UNPACK_SKIP_ROWS:
pValueSlot = &unpacking->skipRows; break;
case LOCAL_GL_UNPACK_SKIP_PIXELS:
pValueSlot = &unpacking->skipPixels; break;
}
if (pValueSlot) {
*pValueSlot = static_cast<uint32_t>(param); return {};
}
}
switch (pname) { case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL:
unpacking->flipY = bool(param); return {};
case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL:
unpacking->premultiplyAlpha = bool(param); return {};
case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL: switch (param) { case LOCAL_GL_NONE: case dom::WebGLRenderingContext_Binding::BROWSER_DEFAULT_WEBGL: break;
bool WebGLContext::DoReadPixelsAndConvert( const webgl::FormatInfo* const srcFormat, const webgl::ReadPixelsDesc& desc, const uintptr_t dest, const uint64_t destSize, const uint32_t rowStride) { constauto& x = desc.srcOffset.x; constauto& y = desc.srcOffset.y; constauto size = *ivec2::From(desc.size); auto pi = desc.pi;
// Gecko normalizes WebGL 1's GL_HALF_FLOAT_OES to GL_HALF_FLOAT internally, // but GLES 2 glReadPixels() still requires GL_HALF_FLOAT_OES rather than // GL_HALF_FLOAT. if (!IsWebGL2() && gl->IsGLES() && gl->Version() < 300 &&
pi.type == LOCAL_GL_HALF_FLOAT) {
pi.type = LOCAL_GL_HALF_FLOAT_OES;
}
// On at least Win+NV, we'll get PBO errors if we don't have at least // `rowStride * height` bytes available to read into. constauto naiveBytesNeeded = CheckedInt<uint64_t>(rowStride) * size.y; constbool isDangerCloseToEdge =
(!naiveBytesNeeded.isValid() || naiveBytesNeeded.value() > destSize); constbool useParanoidHandling =
(gl->WorkAroundDriverBugs() && isDangerCloseToEdge &&
mBoundPixelPackBuffer); if (!useParanoidHandling) {
gl->fReadPixels(x, y, size.x, size.y, pi.format, pi.type, reinterpret_cast<void*>(dest)); returntrue;
}
// Read everything but the last row. constauto bodyHeight = size.y - 1; if (bodyHeight) {
gl->fReadPixels(x, y, size.x, bodyHeight, pi.format, pi.type, reinterpret_cast<void*>(dest));
}
// Now read the last row.
gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
if (offset % pii->bytesPerElement != 0) {
ErrorInvalidOperation( "`offset` must be divisible by the size of `type`" " in bytes."); return;
}
}
//////
auto bytesAvailable = buffer->ByteLength(); if (offset > bytesAvailable) {
ErrorInvalidOperation("`offset` too large for bound PIXEL_PACK_BUFFER."); return;
}
bytesAvailable -= offset;
static webgl::PackingInfo DefaultReadPixelPI( const webgl::FormatUsageInfo* usage) {
MOZ_ASSERT(usage->IsRenderable()); constauto& format = *usage->format; switch (format.componentType) { case webgl::ComponentType::NormUInt: if (format.r == 16) { return {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT};
} return {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE};
case webgl::ComponentType::Int: return {LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT};
case webgl::ComponentType::UInt: return {LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT};
case webgl::ComponentType::Float: return {LOCAL_GL_RGBA, LOCAL_GL_FLOAT};
case webgl::ComponentType::NormInt:
MOZ_RELEASE_ASSERT(false, "SNORM formats are never color-renderable!"); break;
}
MOZ_CRASH("bad webgl::ComponentType");
}
staticbool ArePossiblePackEnums(const webgl::PackingInfo& pi) { // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid // combination for glReadPixels()...
// Only valid when pulled from: // * GLES 2.0.25 p105: // "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA." // * GLES 3.0.4 p193: // "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL." switch (pi.format) { case LOCAL_GL_LUMINANCE: case LOCAL_GL_LUMINANCE_ALPHA: case LOCAL_GL_DEPTH_COMPONENT: case LOCAL_GL_DEPTH_STENCIL: returnfalse;
}
if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8) returnfalse;
constauto pii = webgl::PackingInfoInfo::For(pi); if (!pii) returnfalse;
staticbool ValidateReadPixelsFormatAndType( const webgl::FormatUsageInfo* srcUsage, const webgl::PackingInfo& pi,
WebGLContext* webgl) { if (!ArePossiblePackEnums(pi)) {
webgl->ErrorInvalidEnum("Unexpected format or type."); returnfalse;
}
constauto defaultPI = DefaultReadPixelPI(srcUsage); if (pi == defaultPI) returntrue;
////
// OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is // RGB10_A2, a third combination of format RGBA and type // UNSIGNED_INT_2_10_10_10_REV is accepted.
std::optional<webgl::PackingInfo> bonusValidPi; if (srcUsage->format->effectiveFormat == webgl::EffectiveFormat::RGB10_A2) {
bonusValidPi =
webgl::PackingInfo{LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV};
} if (bonusValidPi && pi == *bonusValidPi) returntrue;
// Map HALF_FLOAT to HALF_FLOAT_OES for error messages in webgl1.
webgl::PackingInfo clientImplPI = implPI; if (clientImplPI.type == LOCAL_GL_HALF_FLOAT && !webgl->IsWebGL2()) {
clientImplPI.type = LOCAL_GL_HALF_FLOAT_OES;
}
auto validPiStr = fmt::format("{} (spec-required baseline for format {}",
defaultPI, srcUsage->format->name); if (implPI != defaultPI) {
validPiStr += fmt::format( ", or {} (spec-optional implementation-chosen format-dependant" " IMPLEMENTATION_COLOR_READ_FORMAT/_TYPE)",
clientImplPI);
} if (bonusValidPi) {
validPiStr += fmt::format(", or {} (spec-required bonus for format {}",
*bonusValidPi, srcUsage->format->name);
}
webgl->ErrorInvalidOperation( "Format/type %s/%s incompatible with this %s framebuffer. Must use: %s.",
EnumString(pi.format).c_str(), EnumString(pi.type).c_str(),
srcUsage->format->name, validPiStr.c_str()); returnfalse;
}
// Read request contains out-of-bounds pixels. Unfortunately: // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer": // "If any of these pixels lies outside of the window allocated to the current // GL context, or outside of the image attached to the currently bound // framebuffer object, then the values obtained for those pixels are // undefined."
// This is a slow-path, so warn people away!
GenerateWarning( "Out-of-bounds reads with readPixels are deprecated, and" " may be slow.");
//////////////////////////////////// // Read only the in-bounds pixels.
if (IsWebGL2()) { if (!packing.rowLength) {
gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, packing.skipPixels + size.x);
}
gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, packing.skipPixels + writeX);
gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, packing.skipRows + writeY);
constauto lengthInType = data.size(); constauto elemCount = lengthInType / channels; if (elemCount > 1 && !validationInfo.isArray) {
GenerateError(
LOCAL_GL_INVALID_OPERATION, "(uniform %s) `values` length (%u) must exactly match size of %s.",
activeInfo.name.c_str(), (uint32_t)lengthInType,
EnumString(activeInfo.elemType).c_str()); return;
}
// -
constauto& samplerInfo = locInfo->samplerInfo; if (samplerInfo) { constauto idata = ReinterpretToSpan<const uint32_t>::From(data); constauto maxTexUnits = GLMaxTextureUnits(); for (constauto& val : idata) { if (val >= maxTexUnits) {
ErrorInvalidValue( "This uniform location is a sampler, but %d" " is not a valid texture unit.",
val); return;
}
}
}
// -
// This is a little galaxy-brain, sorry! constauto ptr = static_cast<constvoid*>(data.data());
(*pfn)(*gl, static_cast<GLint>(loc), elemCount, transpose, ptr);
// -
if (samplerInfo) { auto& texUnits = samplerInfo->texUnits;
constauto srcBegin = reinterpret_cast<const uint32_t*>(data.data()); auto destIndex = locInfo->indexIntoUniform; if (destIndex < texUnits.length()) { // Only sample as many indexes as available tex units allow. constauto destCount = std::min(elemCount, texUnits.length() - destIndex); for (constauto& val : Span<const uint32_t>(srcBegin, destCount)) {
texUnits[destIndex] = AssertedCast<uint8_t>(val);
destIndex += 1;
}
}
}
}
// Doing it this way instead of `if (width <= 0.0)` handles NaNs. constbool isValid = width > 0.0; if (!isValid) {
ErrorInvalidValue("`width` must be positive and non-zero."); return;
}
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.