void WebGLContext::GenerateMipmap(GLenum rawTexTarget) { TexTarget texTarget; WebGLTexture* tex; if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex)) return; tex->GenerateMipmap(texTarget); }
void WebGLContext::AssertCachedBindings() { #ifdef DEBUG MakeContextCurrent(); GetAndFlushUnderlyingGLErrors(); if (IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound); } // Bound object state if (IsWebGL2()) { GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, bound); bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_READ_FRAMEBUFFER_BINDING, bound); } else { MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer); GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound); } GLuint bound = mCurrentProgram ? mCurrentProgram->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound); // Textures GLenum activeTexture = mActiveTexture + LOCAL_GL_TEXTURE0; AssertUintParamCorrect(gl, LOCAL_GL_ACTIVE_TEXTURE, activeTexture); WebGLTexture* curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_2D); bound = curTex ? curTex->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_2D, bound); curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_CUBE_MAP); bound = curTex ? curTex->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_CUBE_MAP, bound); // Buffers bound = mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_ARRAY_BUFFER_BINDING, bound); MOZ_ASSERT(mBoundVertexArray); WebGLBuffer* curBuff = mBoundVertexArray->mElementArrayBuffer; bound = curBuff ? curBuff->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING, bound); MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors()); #endif }
void WebGLContext::GenerateMipmap(GLenum rawTexTarget) { const FuncScope funcScope(*this, "generateMipmap"); const uint8_t funcDims = 0; TexTarget texTarget; WebGLTexture* tex; if (!ValidateTexTarget(this, funcDims, rawTexTarget, &texTarget, &tex)) return; tex->GenerateMipmap(texTarget); }
void WebGLContext::CopyTexSubImage(uint8_t funcDims, GLenum rawTarget, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width, GLsizei height) { TexImageTarget target; WebGLTexture* tex; if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex)) return; tex->CopyTexSubImage(target, level, xOffset, yOffset, zOffset, x, y, width, height); }
void WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeIntParam, GLfloat* maybeFloatParam) { MOZ_ASSERT(maybeIntParam || maybeFloatParam); TexTarget texTarget; WebGLTexture* tex; if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex)) return; tex->TexParameter(texTarget, pname, maybeIntParam, maybeFloatParam); }
void WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, const FloatOrInt& param) { const FuncScope funcScope(*this, "texParameter"); const uint8_t funcDims = 0; TexTarget texTarget; WebGLTexture* tex; if (!ValidateTexTarget(this, funcDims, rawTexTarget, &texTarget, &tex)) return; tex->TexParameter(texTarget, pname, param); }
void WebGLContext::CompressedTexImage(uint8_t funcDims, GLenum rawTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, const TexImageSource& src, const Maybe<GLsizei>& expectedImageSize) { TexImageTarget target; WebGLTexture* tex; if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex)) return; tex->CompressedTexImage(target, level, internalFormat, width, height, depth, border, src, expectedImageSize); }
JS::Value WebGLContext::GetTexParameter(GLenum rawTexTarget, GLenum pname) { TexTarget texTarget; WebGLTexture* tex; if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex)) return JS::NullValue(); if (!IsTexParamValid(pname)) { ErrorInvalidEnumInfo("getTexParameter: pname", pname); return JS::NullValue(); } return tex->GetTexParameter(texTarget, pname); }
void WebGLContext::CopyTexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset, GLint x, GLint y, GLsizei width, GLsizei height) { TexImageTarget texImageTarget; WebGLTexture* tex; if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexSubImage2D", &texImageTarget, &tex)) { return; } tex->CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width, height); }
void WebGLContext::TexImage(uint8_t funcDims, GLenum rawTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat, GLenum unpackType, const TexImageSource& src) { TexImageTarget target; WebGLTexture* tex; if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex)) return; const webgl::PackingInfo pi = {unpackFormat, unpackType}; tex->TexImage(target, level, internalFormat, width, height, depth, border, pi, src); }
void WebGLContext::CopyTexImage2D(GLenum rawTarget, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { const FuncScope funcScope(*this, "copyTexImage2D"); const uint8_t funcDims = 2; TexImageTarget target; WebGLTexture* tex; if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex)) return; tex->CopyTexImage2D(target, level, internalFormat, x, y, width, height, border); }
void WebGLContext::CompressedTexSubImage(uint8_t funcDims, GLenum rawTarget, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, GLenum unpackFormat, const TexImageSource& src, const Maybe<GLsizei>& expectedImageSize) { TexImageTarget target; WebGLTexture* tex; if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex)) return; tex->CompressedTexSubImage(target, level, xOffset, yOffset, zOffset, width, height, depth, unpackFormat, src, expectedImageSize); }
void WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, GLenum unpackFormat, GLenum unpackType, dom::Element* elem, ErrorResult* const out_rv) { TexImageTarget texImageTarget; WebGLTexture* tex; if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget, &tex)) { return; } tex->TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, elem, out_rv); }
void WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset, GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData, ErrorResult& out_rv) { TexImageTarget texImageTarget; WebGLTexture* tex; if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget, &tex)) { return; } tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType, imageData, &out_rv); }
void WebGLContext::CompressedTexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, const dom::ArrayBufferView& view) { TexImageTarget texImageTarget; WebGLTexture* tex; if (!ValidateTexImageTarget(this, rawTexImageTarget, "compressedTexImage2D", &texImageTarget, &tex)) { return; } tex->CompressedTexImage2D(texImageTarget, level, internalFormat, width, height, border, view); }
void WebGLContext::CopyTexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { TexImageTarget texImageTarget; WebGLTexture* tex; if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexImage2D", &texImageTarget, &tex)) { return; } tex->CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height, border); }
void WebGLContext::CompressedTexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset, GLsizei width, GLsizei height, GLenum unpackFormat, const dom::ArrayBufferView& view) { TexImageTarget texImageTarget; WebGLTexture* tex; if (!ValidateTexImageTarget(this, rawTexImageTarget, "compressedTexSubImage2D", &texImageTarget, &tex)) { return; } tex->CompressedTexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height, unpackFormat, view); }
JS::Value WebGLContext::GetTexParameter(GLenum rawTexTarget, GLenum pname) { const FuncScope funcScope(*this, "getTexParameter"); const uint8_t funcDims = 0; TexTarget texTarget; WebGLTexture* tex; if (!ValidateTexTarget(this, funcDims, rawTexTarget, &texTarget, &tex)) return JS::NullValue(); if (!IsTexParamValid(pname)) { ErrorInvalidEnumInfo("pname", pname); return JS::NullValue(); } return tex->GetTexParameter(texTarget, pname); }
void WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset, GLsizei width, GLsizei height, GLenum unpackFormat, GLenum unpackType, const dom::Nullable<dom::ArrayBufferView>& maybeView, ErrorResult& out_rv) { TexImageTarget texImageTarget; WebGLTexture* tex; if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget, &tex)) { return; } tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height, unpackFormat, unpackType, maybeView, &out_rv); }
ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName, bool* const out_error) : mWebGL(webgl) { MOZ_ASSERT(webgl->gl->IsCurrent()); typedef decltype(WebGLContext::mBound2DTextures) TexturesT; const auto fnResolveAll = [this, funcName](const TexturesT& textures) { const auto len = textures.Length(); for (uint32_t texUnit = 0; texUnit < len; ++texUnit) { WebGLTexture* tex = textures[texUnit]; if (!tex) continue; FakeBlackType fakeBlack; if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack)) return false; if (fakeBlack == FakeBlackType::None) continue; mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack); mRebindRequests.push_back({texUnit, tex}); } return true; }; bool ok = true; ok &= fnResolveAll(mWebGL->mBound2DTextures); ok &= fnResolveAll(mWebGL->mBoundCubeMapTextures); ok &= fnResolveAll(mWebGL->mBound3DTextures); ok &= fnResolveAll(mWebGL->mBound2DArrayTextures); if (!ok) { mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.", funcName); } *out_error = !ok; }
/** Validates parameters to texStorage{2D,3D} */ bool WebGL2Context::ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, const char* info) { // GL_INVALID_OPERATION is generated if the default texture object is curently bound to target. WebGLTexture* tex = activeBoundTextureForTarget(target); if (!tex) { ErrorInvalidOperation("%s: no texture is bound to target %s", info, EnumName(target)); return false; } // GL_INVALID_OPERATION is generated if the texture object currently bound to target already has // GL_TEXTURE_IMMUTABLE_FORMAT set to GL_TRUE. if (tex->IsImmutable()) { ErrorInvalidOperation("%s: texture bound to target %s is already immutable", info, EnumName(target)); return false; } // GL_INVALID_ENUM is generated if internalformat is not a valid sized internal format. if (!ValidateSizedInternalFormat(internalformat, info)) return false; // GL_INVALID_VALUE is generated if width, height or levels are less than 1. if (width < 1) { ErrorInvalidValue("%s: width is < 1", info); return false; } if (height < 1) { ErrorInvalidValue("%s: height is < 1", info); return false; } if (depth < 1) { ErrorInvalidValue("%s: depth is < 1", info); return false; } if (levels < 1) { ErrorInvalidValue("%s: levels is < 1", info); return false; } // GL_INVALID_OPERATION is generated if levels is greater than floor(log2(max(width, height, depth)))+1. if (FloorLog2(std::max(std::max(width, height), depth)) + 1 < levels) { ErrorInvalidOperation("%s: too many levels for given texture dimensions", info); return false; } return true; }
void WebGL2Context::TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { if (IsContextLost()) return; // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants. if (target != LOCAL_GL_TEXTURE_3D) return ErrorInvalidEnum("texStorage3D: target is not TEXTURE_3D"); if (!ValidateTexStorage(target, levels, internalformat, width, height, depth, "texStorage3D")) return; GetAndFlushUnderlyingGLErrors(); gl->fTexStorage3D(target, levels, internalformat, width, height, depth); GLenum error = GetAndFlushUnderlyingGLErrors(); if (error) { return GenerateWarning("texStorage3D generated error %s", ErrorName(error)); } WebGLTexture* tex = activeBoundTextureForTarget(target); tex->SetImmutable(); GLsizei w = width; GLsizei h = height; GLsizei d = depth; for (size_t l = 0; l < size_t(levels); l++) { tex->SetImageInfo(TexImageTargetForTargetAndFace(target, 0), l, w, h, d, internalformat, WebGLImageDataStatus::UninitializedImageData); w = std::max(1, w >> 1); h = std::max(1, h >> 1); d = std::max(1, d >> 1); } }
void WebGL2Context::TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { if (IsContextLost()) return; // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants. if (target != LOCAL_GL_TEXTURE_2D && target != LOCAL_GL_TEXTURE_CUBE_MAP) return ErrorInvalidEnum("texStorage2D: target is not TEXTURE_2D or TEXTURE_CUBE_MAP"); if (!ValidateTexStorage(target, levels, internalformat, width, height, 1, "texStorage2D")) return; GetAndFlushUnderlyingGLErrors(); gl->fTexStorage2D(target, levels, internalformat, width, height); GLenum error = GetAndFlushUnderlyingGLErrors(); if (error) { return GenerateWarning("texStorage2D generated error %s", ErrorName(error)); } WebGLTexture* tex = activeBoundTextureForTarget(target); tex->SetImmutable(); const size_t facesCount = (target == LOCAL_GL_TEXTURE_2D) ? 1 : 6; GLsizei w = width; GLsizei h = height; for (size_t l = 0; l < size_t(levels); l++) { for (size_t f = 0; f < facesCount; f++) { tex->SetImageInfo(TexImageTargetForTargetAndFace(target, f), l, w, h, 1, internalformat, WebGLImageDataStatus::UninitializedImageData); } w = std::max(1, w / 2); h = std::max(1, h / 2); } }
void WebGL2Context::TexSubImage3D(GLenum rawTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const Nullable<dom::ArrayBufferView>& pixels, ErrorResult& rv) { if (IsContextLost()) return; if (pixels.IsNull()) return ErrorInvalidValue("texSubImage3D: pixels must not be null!"); const ArrayBufferView& view = pixels.Value(); view.ComputeLengthAndData(); const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage; const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D; if (!ValidateTexImageTarget(rawTarget, func, dims)) return; TexImageTarget texImageTarget(rawTarget); WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget); if (!tex) { return ErrorInvalidOperation("texSubImage3D: no texture bound on active texture unit"); } if (!tex->HasImageInfoAt(texImageTarget, level)) { return ErrorInvalidOperation("texSubImage3D: no previously defined texture image"); } const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level); const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat(); TexInternalFormat existingUnsizedInternalFormat = LOCAL_GL_NONE; TexType existingType = LOCAL_GL_NONE; UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(existingEffectiveInternalFormat, &existingUnsizedInternalFormat, &existingType); if (!ValidateTexImage(texImageTarget, level, existingEffectiveInternalFormat.get(), xoffset, yoffset, zoffset, width, height, depth, 0, format, type, func, dims)) { return; } if (type != existingType) { return ErrorInvalidOperation("texSubImage3D: type differs from that of the existing image"); } js::Scalar::Type jsArrayType = JS_GetArrayBufferViewType(view.Obj()); void* data = view.Data(); size_t dataLength = view.Length(); if (!ValidateTexInputData(type, jsArrayType, func, dims)) return; const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat); MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here. size_t srcTexelSize = bitsPerTexel / 8; if (width == 0 || height == 0 || depth == 0) return; // no effect, we better return right now CheckedUint32 checked_neededByteLength = GetImageSize(height, width, depth, srcTexelSize, mPixelStoreUnpackAlignment); if (!checked_neededByteLength.isValid()) return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size"); uint32_t bytesNeeded = checked_neededByteLength.value(); if (dataLength < bytesNeeded) return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, dataLength); if (imageInfo.HasUninitializedImageData()) { bool coversWholeImage = xoffset == 0 && yoffset == 0 && zoffset == 0 && width == imageInfo.Width() && height == imageInfo.Height() && depth == imageInfo.Depth(); if (coversWholeImage) { tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData); } else { tex->EnsureNoUninitializedImageData(texImageTarget, level); } } GLenum driverType = LOCAL_GL_NONE; GLenum driverInternalFormat = LOCAL_GL_NONE; GLenum driverFormat = LOCAL_GL_NONE; DriverFormatsFromEffectiveInternalFormat(gl, existingEffectiveInternalFormat, &driverInternalFormat, &driverFormat, &driverType); MakeContextCurrent(); gl->fTexSubImage3D(texImageTarget.get(), level, xoffset, yoffset, zoffset, width, height, depth, driverFormat, driverType, data); }
void WebGL2Context::TexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const Nullable<dom::ArrayBufferView> &pixels, ErrorResult& rv) { if (IsContextLost()) return; void* data; size_t dataLength; js::Scalar::Type jsArrayType; if (pixels.IsNull()) { data = nullptr; dataLength = 0; jsArrayType = js::Scalar::TypeMax; } else { const ArrayBufferView& view = pixels.Value(); view.ComputeLengthAndData(); data = view.Data(); dataLength = view.Length(); jsArrayType = JS_GetArrayBufferViewType(view.Obj()); } const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage; const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D; if (!ValidateTexImageTarget(target, func, dims)) return; TexImageTarget texImageTarget = target; if (!ValidateTexImage(texImageTarget, level, internalformat, 0, 0, 0, width, height, depth, border, format, type, func, dims)) { return; } if (!ValidateTexInputData(type, jsArrayType, func, dims)) return; TexInternalFormat effectiveInternalFormat = EffectiveInternalFormatFromInternalFormatAndType(internalformat, type); if (effectiveInternalFormat == LOCAL_GL_NONE) { return ErrorInvalidOperation("texImage3D: bad combination of internalformat and type"); } // we need to find the exact sized format of the source data. Slightly abusing // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format // is the same thing as an unsized internalformat. TexInternalFormat effectiveSourceFormat = EffectiveInternalFormatFromInternalFormatAndType(format, type); MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat); MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here. size_t srcTexelSize = srcbitsPerTexel / 8; CheckedUint32 checked_neededByteLength = GetImageSize(height, width, depth, srcTexelSize, mPixelStoreUnpackAlignment); if (!checked_neededByteLength.isValid()) return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size"); uint32_t bytesNeeded = checked_neededByteLength.value(); if (dataLength && dataLength < bytesNeeded) return ErrorInvalidOperation("texImage3D: not enough data for operation (need %d, have %d)", bytesNeeded, dataLength); WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget); if (!tex) return ErrorInvalidOperation("texImage3D: no texture is bound to this target"); if (tex->IsImmutable()) { return ErrorInvalidOperation( "texImage3D: disallowed because the texture " "bound to this target has already been made immutable by texStorage3D"); } GLenum driverType = LOCAL_GL_NONE; GLenum driverInternalFormat = LOCAL_GL_NONE; GLenum driverFormat = LOCAL_GL_NONE; DriverFormatsFromEffectiveInternalFormat(gl, effectiveInternalFormat, &driverInternalFormat, &driverFormat, &driverType); MakeContextCurrent(); GetAndFlushUnderlyingGLErrors(); gl->fTexImage3D(texImageTarget.get(), level, driverInternalFormat, width, height, depth, 0, driverFormat, driverType, data); GLenum error = GetAndFlushUnderlyingGLErrors(); if (error) { return GenerateWarning("texImage3D generated error %s", ErrorName(error)); } tex->SetImageInfo(texImageTarget, level, width, height, depth, effectiveInternalFormat, data ? WebGLImageDataStatus::InitializedImageData : WebGLImageDataStatus::UninitializedImageData); }