/* -*- 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/. */
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);
}
/* WebGL 1.0: Section 5.14.3: Setting and getting state: * If the context's webgl context lost flag is set, returns * CONTEXT_LOST_WEBGL the first time this method is called. * Afterward, returns NO_ERROR until the context has been * restored. * * WEBGL_lose_context: * [When this extension is enabled: ] loseContext and * restoreContext are allowed to generate INVALID_OPERATION errors * even when the context is lost.
*/
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)); 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)); 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)); 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;
auto pi = desc.pi; if (mRemapImplReadType_HalfFloatOes) { if (pi.type == LOCAL_GL_HALF_FLOAT_OES) {
pi.type = LOCAL_GL_HALF_FLOAT;
}
}
// 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};
default:
MOZ_CRASH();
}
}
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;
// ES2_compatibility always returns RGBA/UNSIGNED_BYTE, so branch on actual // IsGLES(). Also OSX+NV generates an error here. if (!gl->IsGLES()) return defaultPI;
if (!IsWebGL2()) { if (implPI.type == LOCAL_GL_HALF_FLOAT) {
mRemapImplReadType_HalfFloatOes = true;
implPI.type = LOCAL_GL_HALF_FLOAT_OES;
}
}
if (!ArePossiblePackEnums(implPI)) return defaultPI;
return implPI;
}
staticbool ValidateReadPixelsFormatAndType( const webgl::FormatUsageInfo* srcUsage, const webgl::PackingInfo& pi,
gl::GLContext* gl, 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.
constauto implPI = webgl->ValidImplementationColorReadPI(srcUsage); if (pi == implPI) returntrue;
////
// clang-format off
webgl->ErrorInvalidOperation( "Format and type %s/%s incompatible with this %s attachment." " This framebuffer requires either %s/%s or" " getParameter(IMPLEMENTATION_COLOR_READ_FORMAT/_TYPE) %s/%s.",
EnumString(pi.format).c_str(), EnumString(pi.type).c_str(),
srcUsage->format->name,
EnumString(defaultPI.format).c_str(), EnumString(defaultPI.type).c_str(),
EnumString(implPI.format).c_str(), EnumString(implPI.type).c_str()); // clang-format on
// 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;
}
}
}
}
switch (shadertype) { case LOCAL_GL_FRAGMENT_SHADER: case LOCAL_GL_VERTEX_SHADER: break; default:
ErrorInvalidEnumInfo("shadertype", shadertype); return Nothing();
}
switch (precisiontype) { case LOCAL_GL_LOW_FLOAT: case LOCAL_GL_MEDIUM_FLOAT: case LOCAL_GL_HIGH_FLOAT: case LOCAL_GL_LOW_INT: case LOCAL_GL_MEDIUM_INT: case LOCAL_GL_HIGH_INT: break; default:
ErrorInvalidEnumInfo("precisiontype", precisiontype); return Nothing();
}
// 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.