1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231 |
- /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* 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/. */
- #include "WebGLTexture.h"
- #include <algorithm>
- #include "GLContext.h"
- #include "mozilla/dom/WebGLRenderingContextBinding.h"
- #include "mozilla/gfx/Logging.h"
- #include "mozilla/MathAlgorithms.h"
- #include "mozilla/Scoped.h"
- #include "mozilla/Unused.h"
- #include "ScopedGLHelpers.h"
- #include "WebGLContext.h"
- #include "WebGLContextUtils.h"
- #include "WebGLFramebuffer.h"
- #include "WebGLSampler.h"
- #include "WebGLTexelConversions.h"
- namespace mozilla {
- /*static*/ const WebGLTexture::ImageInfo WebGLTexture::ImageInfo::kUndefined;
- ////////////////////////////////////////
- template <typename T>
- static inline T&
- Mutable(const T& x)
- {
- return const_cast<T&>(x);
- }
- void
- WebGLTexture::ImageInfo::Clear()
- {
- if (!IsDefined())
- return;
- OnRespecify();
- Mutable(mFormat) = LOCAL_GL_NONE;
- Mutable(mWidth) = 0;
- Mutable(mHeight) = 0;
- Mutable(mDepth) = 0;
- MOZ_ASSERT(!IsDefined());
- }
- WebGLTexture::ImageInfo&
- WebGLTexture::ImageInfo::operator =(const ImageInfo& a)
- {
- Mutable(mFormat) = a.mFormat;
- Mutable(mWidth) = a.mWidth;
- Mutable(mHeight) = a.mHeight;
- Mutable(mDepth) = a.mDepth;
- mIsDataInitialized = a.mIsDataInitialized;
- // But *don't* transfer mAttachPoints!
- MOZ_ASSERT(a.mAttachPoints.empty());
- OnRespecify();
- return *this;
- }
- bool
- WebGLTexture::ImageInfo::IsPowerOfTwo() const
- {
- return mozilla::IsPowerOfTwo(mWidth) &&
- mozilla::IsPowerOfTwo(mHeight) &&
- mozilla::IsPowerOfTwo(mDepth);
- }
- void
- WebGLTexture::ImageInfo::AddAttachPoint(WebGLFBAttachPoint* attachPoint)
- {
- const auto pair = mAttachPoints.insert(attachPoint);
- DebugOnly<bool> didInsert = pair.second;
- MOZ_ASSERT(didInsert);
- }
- void
- WebGLTexture::ImageInfo::RemoveAttachPoint(WebGLFBAttachPoint* attachPoint)
- {
- DebugOnly<size_t> numElemsErased = mAttachPoints.erase(attachPoint);
- MOZ_ASSERT_IF(IsDefined(), numElemsErased == 1);
- }
- void
- WebGLTexture::ImageInfo::OnRespecify() const
- {
- for (auto cur : mAttachPoints) {
- cur->OnBackingStoreRespecified();
- }
- }
- size_t
- WebGLTexture::ImageInfo::MemoryUsage() const
- {
- if (!IsDefined())
- return 0;
- const auto bytesPerTexel = mFormat->format->estimatedBytesPerPixel;
- return size_t(mWidth) * size_t(mHeight) * size_t(mDepth) * bytesPerTexel;
- }
- void
- WebGLTexture::ImageInfo::SetIsDataInitialized(bool isDataInitialized, WebGLTexture* tex)
- {
- MOZ_ASSERT(tex);
- MOZ_ASSERT(this >= &tex->mImageInfoArr[0]);
- MOZ_ASSERT(this < &tex->mImageInfoArr[kMaxLevelCount * kMaxFaceCount]);
- mIsDataInitialized = isDataInitialized;
- tex->InvalidateResolveCache();
- }
- ////////////////////////////////////////
- JSObject*
- WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {
- return dom::WebGLTextureBinding::Wrap(cx, this, givenProto);
- }
- WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex)
- : WebGLRefCountedObject(webgl)
- , mGLName(tex)
- , mTarget(LOCAL_GL_NONE)
- , mFaceCount(0)
- , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
- , mMagFilter(LOCAL_GL_LINEAR)
- , mWrapS(LOCAL_GL_REPEAT)
- , mWrapT(LOCAL_GL_REPEAT)
- , mImmutable(false)
- , mImmutableLevelCount(0)
- , mBaseMipmapLevel(0)
- , mMaxMipmapLevel(1000)
- , mTexCompareMode(LOCAL_GL_NONE)
- , mIsResolved(false)
- , mResolved_Swizzle(nullptr)
- {
- mContext->mTextures.insertBack(this);
- }
- void
- WebGLTexture::Delete()
- {
- for (auto& cur : mImageInfoArr) {
- cur.Clear();
- }
- mContext->MakeContextCurrent();
- mContext->gl->fDeleteTextures(1, &mGLName);
- LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
- }
- size_t
- WebGLTexture::MemoryUsage() const
- {
- if (IsDeleted())
- return 0;
- size_t accum = 0;
- for (const auto& cur : mImageInfoArr) {
- accum += cur.MemoryUsage();
- }
- return accum;
- }
- void
- WebGLTexture::SetImageInfo(ImageInfo* target, const ImageInfo& newInfo)
- {
- *target = newInfo;
- InvalidateResolveCache();
- }
- void
- WebGLTexture::SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo)
- {
- for (uint8_t i = 0; i < mFaceCount; i++) {
- ImageInfoAtFace(i, level) = newInfo;
- }
- InvalidateResolveCache();
- }
- bool
- WebGLTexture::IsMipmapComplete(const char* funcName, uint32_t texUnit,
- bool* const out_initFailed)
- {
- *out_initFailed = false;
- MOZ_ASSERT(DoesMinFilterRequireMipmap());
- // GLES 3.0.4, p161
- uint32_t maxLevel;
- if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel))
- return false;
- // "* `level_base <= level_max`"
- if (mBaseMipmapLevel > maxLevel)
- return false;
- // Make a copy so we can modify it.
- const ImageInfo& baseImageInfo = BaseImageInfo();
- // Reference dimensions based on the current level.
- uint32_t refWidth = baseImageInfo.mWidth;
- uint32_t refHeight = baseImageInfo.mHeight;
- uint32_t refDepth = baseImageInfo.mDepth;
- MOZ_ASSERT(refWidth && refHeight && refDepth);
- for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
- if (!EnsureLevelInitialized(funcName, level)) {
- *out_initFailed = true;
- return false;
- }
- // "A cube map texture is mipmap complete if each of the six texture images,
- // considered individually, is mipmap complete."
- for (uint8_t face = 0; face < mFaceCount; face++) {
- const ImageInfo& cur = ImageInfoAtFace(face, level);
- // "* The set of mipmap arrays `level_base` through `q` (where `q` is defined
- // the "Mipmapping" discussion of section 3.8.10) were each specified with
- // the same effective internal format."
- // "* The dimensions of the arrays follow the sequence described in the
- // "Mipmapping" discussion of section 3.8.10."
- if (cur.mWidth != refWidth ||
- cur.mHeight != refHeight ||
- cur.mDepth != refDepth ||
- cur.mFormat != baseImageInfo.mFormat)
- {
- return false;
- }
- }
- // GLES 3.0.4, p158:
- // "[...] until the last array is reached with dimension 1 x 1 x 1."
- if (mTarget == LOCAL_GL_TEXTURE_3D) {
- if (refWidth == 1 &&
- refHeight == 1 &&
- refDepth == 1)
- {
- break;
- }
- refDepth = std::max(uint32_t(1), refDepth / 2);
- } else {
- // TEXTURE_2D_ARRAY may have depth != 1, but that's normal.
- if (refWidth == 1 &&
- refHeight == 1)
- {
- break;
- }
- }
- refWidth = std::max(uint32_t(1), refWidth / 2);
- refHeight = std::max(uint32_t(1), refHeight / 2);
- }
- return true;
- }
- bool
- WebGLTexture::IsCubeComplete() const
- {
- // GLES 3.0.4, p161
- // "[...] a cube map texture is cube complete if the following conditions all hold
- // true:
- // * The `level_base` arrays of each of the six texture images making up the cube map
- // have identical, positive, and square dimensions.
- // * The `level_base` arrays were each specified with the same effective internal
- // format."
- // Note that "cube complete" does not imply "mipmap complete".
- const ImageInfo& reference = BaseImageInfo();
- if (!reference.IsDefined())
- return false;
- auto refWidth = reference.mWidth;
- auto refFormat = reference.mFormat;
- for (uint8_t face = 0; face < mFaceCount; face++) {
- const ImageInfo& cur = ImageInfoAtFace(face, mBaseMipmapLevel);
- if (!cur.IsDefined())
- return false;
- MOZ_ASSERT(cur.mDepth == 1);
- if (cur.mFormat != refFormat || // Check effective formats.
- cur.mWidth != refWidth || // Check both width and height against refWidth to
- cur.mHeight != refWidth) // to enforce positive and square dimensions.
- {
- return false;
- }
- }
- return true;
- }
- bool
- WebGLTexture::IsComplete(const char* funcName, uint32_t texUnit,
- const char** const out_reason, bool* const out_initFailed)
- {
- *out_initFailed = false;
- const auto maxLevel = kMaxLevelCount - 1;
- if (mBaseMipmapLevel > maxLevel) {
- *out_reason = "`level_base` too high.";
- return false;
- }
- if (!EnsureLevelInitialized(funcName, mBaseMipmapLevel)) {
- *out_initFailed = true;
- return false;
- }
- // Texture completeness is established at GLES 3.0.4, p160-161.
- // "[A] texture is complete unless any of the following conditions hold true:"
- // "* Any dimension of the `level_base` array is not positive."
- const ImageInfo& baseImageInfo = BaseImageInfo();
- if (!baseImageInfo.IsDefined()) {
- // In case of undefined texture image, we don't print any message because this is
- // a very common and often legitimate case (asynchronous texture loading).
- *out_reason = nullptr;
- return false;
- }
- if (!baseImageInfo.mWidth || !baseImageInfo.mHeight || !baseImageInfo.mDepth) {
- *out_reason = "The dimensions of `level_base` are not all positive.";
- return false;
- }
- // "* The texture is a cube map texture, and is not cube complete."
- if (IsCubeMap() && !IsCubeComplete()) {
- *out_reason = "Cubemaps must be \"cube complete\".";
- return false;
- }
- WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
- TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
- TexMagFilter magFilter = sampler ? sampler->mMagFilter : mMagFilter;
- // "* The minification filter requires a mipmap (is neither NEAREST nor LINEAR) and
- // the texture is not mipmap complete."
- const bool requiresMipmap = (minFilter != LOCAL_GL_NEAREST &&
- minFilter != LOCAL_GL_LINEAR);
- if (requiresMipmap && !IsMipmapComplete(funcName, texUnit, out_initFailed)) {
- if (*out_initFailed)
- return false;
- *out_reason = "Because the minification filter requires mipmapping, the texture"
- " must be \"mipmap complete\".";
- return false;
- }
- const bool isMinFilteringNearest = (minFilter == LOCAL_GL_NEAREST ||
- minFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
- const bool isMagFilteringNearest = (magFilter == LOCAL_GL_NEAREST);
- const bool isFilteringNearestOnly = (isMinFilteringNearest && isMagFilteringNearest);
- if (!isFilteringNearestOnly) {
- auto formatUsage = baseImageInfo.mFormat;
- auto format = formatUsage->format;
- bool isFilterable = formatUsage->isFilterable;
- // "* The effective internal format specified for the texture arrays is a sized
- // internal depth or depth and stencil format, the value of
- // TEXTURE_COMPARE_MODE is NONE[1], and either the magnification filter is not
- // NEAREST, or the minification filter is neither NEAREST nor
- // NEAREST_MIPMAP_NEAREST."
- // [1]: This sounds suspect, but is explicitly noted in the change log for GLES
- // 3.0.1:
- // "* Clarify that a texture is incomplete if it has a depth component, no
- // shadow comparison, and linear filtering (also Bug 9481)."
- if (format->d && mTexCompareMode != LOCAL_GL_NONE) {
- isFilterable = true;
- }
- // "* The effective internal format specified for the texture arrays is a sized
- // internal color format that is not texture-filterable, and either the
- // magnification filter is not NEAREST or the minification filter is neither
- // NEAREST nor NEAREST_MIPMAP_NEAREST."
- // Since all (GLES3) unsized color formats are filterable just like their sized
- // equivalents, we don't have to care whether its sized or not.
- if (!isFilterable) {
- *out_reason = "Because minification or magnification filtering is not NEAREST"
- " or NEAREST_MIPMAP_NEAREST, and the texture's format must be"
- " \"texture-filterable\".";
- return false;
- }
- }
- // Texture completeness is effectively (though not explicitly) amended for GLES2 by
- // the "Texture Access" section under $3.8 "Fragment Shaders". This also applies to
- // vertex shaders, as noted on GLES 2.0.25, p41.
- if (!mContext->IsWebGL2()) {
- // GLES 2.0.25, p87-88:
- // "Calling a sampler from a fragment shader will return (R,G,B,A)=(0,0,0,1) if
- // any of the following conditions are true:"
- // "* A two-dimensional sampler is called, the minification filter is one that
- // requires a mipmap[...], and the sampler's associated texture object is not
- // complete[.]"
- // (already covered)
- // "* A two-dimensional sampler is called, the minification filter is not one that
- // requires a mipmap (either NEAREST nor[sic] LINEAR), and either dimension of
- // the level zero array of the associated texture object is not positive."
- // (already covered)
- // "* A two-dimensional sampler is called, the corresponding texture image is a
- // non-power-of-two image[...], and either the texture wrap mode is not
- // CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
- // "* A cube map sampler is called, any of the corresponding texture images are
- // non-power-of-two images, and either the texture wrap mode is not
- // CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
- if (!baseImageInfo.IsPowerOfTwo()) {
- TexWrap wrapS = sampler ? sampler->mWrapS : mWrapS;
- TexWrap wrapT = sampler ? sampler->mWrapT : mWrapT;
- // "either the texture wrap mode is not CLAMP_TO_EDGE"
- if (wrapS != LOCAL_GL_CLAMP_TO_EDGE ||
- wrapT != LOCAL_GL_CLAMP_TO_EDGE)
- {
- *out_reason = "Non-power-of-two textures must have a wrap mode of"
- " CLAMP_TO_EDGE.";
- return false;
- }
- // "or the minification filter is neither NEAREST nor LINEAR"
- if (requiresMipmap) {
- *out_reason = "Mipmapping requires power-of-two textures.";
- return false;
- }
- }
- // "* A cube map sampler is called, and either the corresponding cube map texture
- // image is not cube complete, or TEXTURE_MIN_FILTER is one that requires a
- // mipmap and the texture is not mipmap cube complete."
- // (already covered)
- }
- return true;
- }
- bool
- WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const
- {
- WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
- TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
- if (minFilter == LOCAL_GL_NEAREST ||
- minFilter == LOCAL_GL_LINEAR)
- {
- // No extra mips used.
- *out = mBaseMipmapLevel;
- return true;
- }
- const auto& imageInfo = BaseImageInfo();
- if (!imageInfo.IsDefined())
- return false;
- uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.PossibleMipmapLevels() - 1;
- *out = std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
- return true;
- }
- bool
- WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
- FakeBlackType* const out_fakeBlack)
- {
- const char* incompleteReason;
- bool initFailed = false;
- if (!IsComplete(funcName, texUnit, &incompleteReason, &initFailed)) {
- if (initFailed) {
- mContext->ErrorOutOfMemory("%s: Failed to initialize texture data.",
- funcName);
- return false; // The world just exploded.
- }
- if (incompleteReason) {
- mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
- " 'incomplete', and will be rendered as"
- " RGBA(0,0,0,1), as per the GLES 2.0.24 $3.8.2: %s",
- funcName, texUnit, mTarget.get(),
- incompleteReason);
- }
- *out_fakeBlack = FakeBlackType::RGBA0001;
- return true;
- }
- *out_fakeBlack = FakeBlackType::None;
- return true;
- }
- static void
- SetSwizzle(gl::GLContext* gl, TexTarget target, const GLint* swizzle)
- {
- static const GLint kNoSwizzle[4] = { LOCAL_GL_RED, LOCAL_GL_GREEN, LOCAL_GL_BLUE,
- LOCAL_GL_ALPHA };
- if (!swizzle) {
- swizzle = kNoSwizzle;
- } else if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) {
- MOZ_CRASH("GFX: Needs swizzle feature to swizzle!");
- }
- gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, swizzle[0]);
- gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, swizzle[1]);
- gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, swizzle[2]);
- gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, swizzle[3]);
- }
- bool
- WebGLTexture::ResolveForDraw(const char* funcName, uint32_t texUnit,
- FakeBlackType* const out_fakeBlack)
- {
- if (!mIsResolved) {
- if (!GetFakeBlackType(funcName, texUnit, &mResolved_FakeBlack))
- return false;
- // Check which swizzle we should use. Since the texture must be complete at this
- // point, just grab the format off any valid image.
- const GLint* newSwizzle = nullptr;
- if (mResolved_FakeBlack == FakeBlackType::None) {
- const auto& cur = ImageInfoAtFace(0, mBaseMipmapLevel);
- newSwizzle = cur.mFormat->textureSwizzleRGBA;
- }
- // Only set the swizzle if it changed since last time we did it.
- if (newSwizzle != mResolved_Swizzle) {
- mResolved_Swizzle = newSwizzle;
- // Set the new swizzle!
- mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
- SetSwizzle(mContext->gl, mTarget, mResolved_Swizzle);
- mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mContext->mActiveTexture);
- }
- mIsResolved = true;
- }
- *out_fakeBlack = mResolved_FakeBlack;
- return true;
- }
- bool
- WebGLTexture::EnsureImageDataInitialized(const char* funcName, TexImageTarget target,
- uint32_t level)
- {
- auto& imageInfo = ImageInfoAt(target, level);
- if (!imageInfo.IsDefined())
- return true;
- if (imageInfo.IsDataInitialized())
- return true;
- return InitializeImageData(funcName, target, level);
- }
- bool
- WebGLTexture::EnsureLevelInitialized(const char* funcName, uint32_t level)
- {
- if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
- return EnsureImageDataInitialized(funcName, mTarget.get(), level);
- for (GLenum texImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
- texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
- ++texImageTarget)
- {
- if (!EnsureImageDataInitialized(funcName, texImageTarget, level))
- return false;
- }
- return true;
- }
- static void
- ZeroANGLEDepthTexture(WebGLContext* webgl, GLuint tex,
- const webgl::FormatUsageInfo* usage, uint32_t width,
- uint32_t height)
- {
- const auto& format = usage->format;
- GLenum attachPoint = 0;
- GLbitfield clearBits = 0;
- if (format->d) {
- attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
- clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
- }
- if (format->s) {
- attachPoint = (format->d ? LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
- : LOCAL_GL_STENCIL_ATTACHMENT);
- clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
- }
- MOZ_RELEASE_ASSERT(attachPoint && clearBits, "GFX: No bits cleared.");
- ////
- const auto& gl = webgl->gl;
- MOZ_ASSERT(gl->IsCurrent());
- gl::ScopedFramebuffer scopedFB(gl);
- const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
- gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint, LOCAL_GL_TEXTURE_2D,
- tex, 0);
- const auto& status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
- MOZ_RELEASE_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
- ////
- const bool fakeNoAlpha = false;
- webgl->ForceClearFramebufferWithDefaultValues(clearBits, fakeNoAlpha);
- }
- static bool
- ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex,
- TexImageTarget target, uint32_t level,
- const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height,
- uint32_t depth)
- {
- // This has two usecases:
- // 1. Lazy zeroing of uninitialized textures:
- // a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*)
- // b. Before partial upload. (TexStorage + TexSubImage)
- // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)
- // We have no sympathy for any of these cases.
- // "Doctor, it hurts when I do this!" "Well don't do that!"
- webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is"
- " slow.",
- funcName);
- gl::GLContext* gl = webgl->GL();
- gl->MakeCurrent();
- GLenum scopeBindTarget;
- switch (target.get()) {
- case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
- case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
- case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
- case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
- case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
- case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
- scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP;
- break;
- default:
- scopeBindTarget = target.get();
- break;
- }
- const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget);
- auto compression = usage->format->compression;
- if (compression) {
- auto sizedFormat = usage->format->sizedFormat;
- MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set");
- const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) {
- return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock;
- };
- const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
- const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);
- CheckedUint32 checkedByteCount = compression->bytesPerBlock;
- checkedByteCount *= widthBlocks;
- checkedByteCount *= heightBlocks;
- checkedByteCount *= depth;
- if (!checkedByteCount.isValid())
- return false;
- const size_t byteCount = checkedByteCount.value();
- UniqueBuffer zeros = calloc(1, byteCount);
- if (!zeros)
- return false;
- ScopedUnpackReset scopedReset(webgl);
- gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it
- // well.
- const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0,
- width, height, depth, sizedFormat,
- byteCount, zeros.get());
- return !error;
- }
- const auto driverUnpackInfo = usage->idealUnpack;
- MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set.");
- if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
- gl->IsANGLE() &&
- usage->format->d)
- {
- // ANGLE_depth_texture does not allow uploads, so we have to clear.
- // (Restriction because of D3D9)
- MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D);
- MOZ_ASSERT(level == 0);
- ZeroANGLEDepthTexture(webgl, tex, usage, width, height);
- return true;
- }
- const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();
- const auto bytesPerPixel = webgl::BytesPerPixel(packing);
- CheckedUint32 checkedByteCount = bytesPerPixel;
- checkedByteCount *= width;
- checkedByteCount *= height;
- checkedByteCount *= depth;
- if (!checkedByteCount.isValid())
- return false;
- const size_t byteCount = checkedByteCount.value();
- UniqueBuffer zeros = calloc(1, byteCount);
- if (!zeros)
- return false;
- ScopedUnpackReset scopedReset(webgl);
- gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
- const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth,
- packing, zeros.get());
- return !error;
- }
- bool
- WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target,
- uint32_t level)
- {
- auto& imageInfo = ImageInfoAt(target, level);
- MOZ_ASSERT(imageInfo.IsDefined());
- MOZ_ASSERT(!imageInfo.IsDataInitialized());
- const auto& usage = imageInfo.mFormat;
- const auto& width = imageInfo.mWidth;
- const auto& height = imageInfo.mHeight;
- const auto& depth = imageInfo.mDepth;
- if (!ZeroTextureData(mContext, funcName, mGLName, target, level, usage, width, height,
- depth))
- {
- return false;
- }
- imageInfo.SetIsDataInitialized(true, this);
- return true;
- }
- void
- WebGLTexture::ClampLevelBaseAndMax()
- {
- if (!mImmutable)
- return;
- // GLES 3.0.4, p158:
- // "For immutable-format textures, `level_base` is clamped to the range
- // `[0, levels-1]`, `level_max` is then clamped to the range `
- // `[level_base, levels-1]`, where `levels` is the parameter passed to
- // TexStorage* for the texture object."
- mBaseMipmapLevel = Clamp<uint32_t>(mBaseMipmapLevel, 0, mImmutableLevelCount - 1);
- mMaxMipmapLevel = Clamp<uint32_t>(mMaxMipmapLevel, mBaseMipmapLevel,
- mImmutableLevelCount - 1);
- }
- void
- WebGLTexture::PopulateMipChain(uint32_t firstLevel, uint32_t lastLevel)
- {
- const ImageInfo& baseImageInfo = ImageInfoAtFace(0, firstLevel);
- MOZ_ASSERT(baseImageInfo.IsDefined());
- uint32_t refWidth = baseImageInfo.mWidth;
- uint32_t refHeight = baseImageInfo.mHeight;
- uint32_t refDepth = baseImageInfo.mDepth;
- if (!refWidth || !refHeight || !refDepth)
- return;
- for (uint32_t level = firstLevel + 1; level <= lastLevel; level++) {
- bool isMinimal = (refWidth == 1 &&
- refHeight == 1);
- if (mTarget == LOCAL_GL_TEXTURE_3D) {
- isMinimal &= (refDepth == 1);
- }
- // Higher levels are unaffected.
- if (isMinimal)
- break;
- refWidth = std::max(uint32_t(1), refWidth / 2);
- refHeight = std::max(uint32_t(1), refHeight / 2);
- if (mTarget == LOCAL_GL_TEXTURE_3D) { // But not TEXTURE_2D_ARRAY!
- refDepth = std::max(uint32_t(1), refDepth / 2);
- }
- const ImageInfo cur(baseImageInfo.mFormat, refWidth, refHeight, refDepth,
- baseImageInfo.IsDataInitialized());
- SetImageInfosAtLevel(level, cur);
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // GL calls
- bool
- WebGLTexture::BindTexture(TexTarget texTarget)
- {
- if (IsDeleted()) {
- mContext->ErrorInvalidOperation("bindTexture: Cannot bind a deleted object.");
- return false;
- }
- const bool isFirstBinding = !HasEverBeenBound();
- if (!isFirstBinding && mTarget != texTarget) {
- mContext->ErrorInvalidOperation("bindTexture: This texture has already been bound"
- " to a different target.");
- return false;
- }
- mTarget = texTarget;
- mContext->gl->fBindTexture(mTarget.get(), mGLName);
- if (isFirstBinding) {
- mFaceCount = IsCubeMap() ? 6 : 1;
- gl::GLContext* gl = mContext->gl;
- // Thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R
- // is not present in GLES 2, but is present in GL and it seems as if for
- // cube maps we need to set it to GL_CLAMP_TO_EDGE to get the expected
- // GLES behavior.
- // If we are WebGL 2 though, we'll want to leave it as REPEAT.
- const bool hasWrapR = gl->IsSupported(gl::GLFeature::texture_3D);
- if (IsCubeMap() && hasWrapR && !mContext->IsWebGL2()) {
- gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_WRAP_R,
- LOCAL_GL_CLAMP_TO_EDGE);
- }
- }
- return true;
- }
- void
- WebGLTexture::GenerateMipmap(TexTarget texTarget)
- {
- // GLES 3.0.4 p160:
- // "Mipmap generation replaces texel array levels level base + 1 through q with arrays
- // derived from the level base array, regardless of their previous contents. All
- // other mipmap arrays, including the level base array, are left unchanged by this
- // computation."
- const ImageInfo& baseImageInfo = BaseImageInfo();
- if (!baseImageInfo.IsDefined()) {
- mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture is"
- " not defined.");
- return;
- }
- if (IsCubeMap() && !IsCubeComplete()) {
- mContext->ErrorInvalidOperation("generateMipmap: Cube maps must be \"cube"
- " complete\".");
- return;
- }
- if (!mContext->IsWebGL2() && !baseImageInfo.IsPowerOfTwo()) {
- mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture"
- " does not have power-of-two dimensions.");
- return;
- }
- auto format = baseImageInfo.mFormat->format;
- if (format->compression) {
- mContext->ErrorInvalidOperation("generateMipmap: Texture data at base level is"
- " compressed.");
- return;
- }
- if (format->d) {
- mContext->ErrorInvalidOperation("generateMipmap: Depth textures are not"
- " supported.");
- return;
- }
- // OpenGL ES 3.0.4 p160:
- // If the level base array was not specified with an unsized internal format from
- // table 3.3 or a sized internal format that is both color-renderable and
- // texture-filterable according to table 3.13, an INVALID_OPERATION error
- // is generated.
- const auto usage = baseImageInfo.mFormat;
- bool canGenerateMipmap = (usage->IsRenderable() && usage->isFilterable);
- switch (usage->format->effectiveFormat) {
- case webgl::EffectiveFormat::Luminance8:
- case webgl::EffectiveFormat::Alpha8:
- case webgl::EffectiveFormat::Luminance8Alpha8:
- // Non-color-renderable formats from Table 3.3.
- canGenerateMipmap = true;
- break;
- default:
- break;
- }
- if (!canGenerateMipmap) {
- mContext->ErrorInvalidOperation("generateMipmap: Texture at base level is not unsized"
- " internal format or is not"
- " color-renderable or texture-filterable.");
- return;
- }
- // Done with validation. Do the operation.
- mContext->MakeContextCurrent();
- gl::GLContext* gl = mContext->gl;
- if (gl->WorkAroundDriverBugs()) {
- // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we
- // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance
- // overhead so we do it unconditionally.
- //
- // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105.
- gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER,
- LOCAL_GL_NEAREST_MIPMAP_NEAREST);
- gl->fGenerateMipmap(texTarget.get());
- gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER,
- mMinFilter.get());
- } else {
- gl->fGenerateMipmap(texTarget.get());
- }
- // Record the results.
- // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns
- // mBaseMipmapLevel if the min filter doesn't require mipmaps.
- const uint32_t maxLevel = mBaseMipmapLevel + baseImageInfo.PossibleMipmapLevels() - 1;
- PopulateMipChain(mBaseMipmapLevel, maxLevel);
- }
- JS::Value
- WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname)
- {
- mContext->MakeContextCurrent();
- GLint i = 0;
- GLfloat f = 0.0f;
- switch (pname) {
- case LOCAL_GL_TEXTURE_MIN_FILTER:
- return JS::NumberValue(uint32_t(mMinFilter.get()));
- case LOCAL_GL_TEXTURE_MAG_FILTER:
- return JS::NumberValue(uint32_t(mMagFilter.get()));
- case LOCAL_GL_TEXTURE_WRAP_S:
- return JS::NumberValue(uint32_t(mWrapS.get()));
- case LOCAL_GL_TEXTURE_WRAP_T:
- return JS::NumberValue(uint32_t(mWrapT.get()));
- case LOCAL_GL_TEXTURE_BASE_LEVEL:
- return JS::NumberValue(mBaseMipmapLevel);
- case LOCAL_GL_TEXTURE_COMPARE_MODE:
- return JS::NumberValue(uint32_t(mTexCompareMode));
- case LOCAL_GL_TEXTURE_MAX_LEVEL:
- return JS::NumberValue(mMaxMipmapLevel);
- case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
- return JS::BooleanValue(mImmutable);
- case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS:
- return JS::NumberValue(uint32_t(mImmutableLevelCount));
- case LOCAL_GL_TEXTURE_COMPARE_FUNC:
- case LOCAL_GL_TEXTURE_WRAP_R:
- mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i);
- return JS::NumberValue(uint32_t(i));
- case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
- case LOCAL_GL_TEXTURE_MAX_LOD:
- case LOCAL_GL_TEXTURE_MIN_LOD:
- mContext->gl->fGetTexParameterfv(texTarget.get(), pname, &f);
- return JS::NumberValue(float(f));
- default:
- MOZ_CRASH("GFX: Unhandled pname.");
- }
- }
- bool
- WebGLTexture::IsTexture() const
- {
- return HasEverBeenBound() && !IsDeleted();
- }
- // Here we have to support all pnames with both int and float params.
- // See this discussion:
- // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
- void
- WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, const FloatOrInt& param)
- {
- bool isPNameValid = false;
- switch (pname) {
- // GLES 2.0.25 p76:
- case LOCAL_GL_TEXTURE_WRAP_S:
- case LOCAL_GL_TEXTURE_WRAP_T:
- case LOCAL_GL_TEXTURE_MIN_FILTER:
- case LOCAL_GL_TEXTURE_MAG_FILTER:
- isPNameValid = true;
- break;
- // GLES 3.0.4 p149-150:
- case LOCAL_GL_TEXTURE_BASE_LEVEL:
- case LOCAL_GL_TEXTURE_COMPARE_MODE:
- case LOCAL_GL_TEXTURE_COMPARE_FUNC:
- case LOCAL_GL_TEXTURE_MAX_LEVEL:
- case LOCAL_GL_TEXTURE_MAX_LOD:
- case LOCAL_GL_TEXTURE_MIN_LOD:
- case LOCAL_GL_TEXTURE_WRAP_R:
- if (mContext->IsWebGL2())
- isPNameValid = true;
- break;
- case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
- if (mContext->IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic))
- isPNameValid = true;
- break;
- }
- if (!isPNameValid) {
- mContext->ErrorInvalidEnumInfo("texParameter: pname", pname);
- return;
- }
- ////////////////
- // Validate params and invalidate if needed.
- bool paramBadEnum = false;
- bool paramBadValue = false;
- switch (pname) {
- case LOCAL_GL_TEXTURE_BASE_LEVEL:
- case LOCAL_GL_TEXTURE_MAX_LEVEL:
- paramBadValue = (param.i < 0);
- break;
- case LOCAL_GL_TEXTURE_COMPARE_MODE:
- paramBadValue = (param.i != LOCAL_GL_NONE &&
- param.i != LOCAL_GL_COMPARE_REF_TO_TEXTURE);
- break;
- case LOCAL_GL_TEXTURE_COMPARE_FUNC:
- switch (param.i) {
- case LOCAL_GL_LEQUAL:
- case LOCAL_GL_GEQUAL:
- case LOCAL_GL_LESS:
- case LOCAL_GL_GREATER:
- case LOCAL_GL_EQUAL:
- case LOCAL_GL_NOTEQUAL:
- case LOCAL_GL_ALWAYS:
- case LOCAL_GL_NEVER:
- break;
- default:
- paramBadValue = true;
- break;
- }
- break;
- case LOCAL_GL_TEXTURE_MIN_FILTER:
- switch (param.i) {
- case LOCAL_GL_NEAREST:
- case LOCAL_GL_LINEAR:
- case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
- case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
- case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
- case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
- break;
- default:
- paramBadEnum = true;
- break;
- }
- break;
- case LOCAL_GL_TEXTURE_MAG_FILTER:
- switch (param.i) {
- case LOCAL_GL_NEAREST:
- case LOCAL_GL_LINEAR:
- break;
- default:
- paramBadEnum = true;
- break;
- }
- break;
- case LOCAL_GL_TEXTURE_WRAP_S:
- case LOCAL_GL_TEXTURE_WRAP_T:
- case LOCAL_GL_TEXTURE_WRAP_R:
- switch (param.i) {
- case LOCAL_GL_CLAMP_TO_EDGE:
- case LOCAL_GL_MIRRORED_REPEAT:
- case LOCAL_GL_REPEAT:
- break;
- default:
- paramBadEnum = true;
- break;
- }
- break;
- case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
- if (param.f < 1.0f)
- paramBadValue = true;
- break;
- }
- if (paramBadEnum) {
- if (!param.isFloat) {
- mContext->ErrorInvalidEnum("texParameteri: pname 0x%04x: Invalid param"
- " 0x%04x.",
- pname, param.i);
- } else {
- mContext->ErrorInvalidEnum("texParameterf: pname 0x%04x: Invalid param %g.",
- pname, param.f);
- }
- return;
- }
- if (paramBadValue) {
- if (!param.isFloat) {
- mContext->ErrorInvalidValue("texParameteri: pname 0x%04x: Invalid param %i"
- " (0x%x).",
- pname, param.i, param.i);
- } else {
- mContext->ErrorInvalidValue("texParameterf: pname 0x%04x: Invalid param %g.",
- pname, param.f);
- }
- return;
- }
- ////////////////
- // Store any needed values
- FloatOrInt clamped = param;
- switch (pname) {
- case LOCAL_GL_TEXTURE_BASE_LEVEL:
- mBaseMipmapLevel = clamped.i;
- ClampLevelBaseAndMax();
- clamped = FloatOrInt(GLint(mBaseMipmapLevel));
- break;
- case LOCAL_GL_TEXTURE_MAX_LEVEL:
- mMaxMipmapLevel = clamped.i;
- ClampLevelBaseAndMax();
- clamped = FloatOrInt(GLint(mMaxMipmapLevel));
- break;
- case LOCAL_GL_TEXTURE_MIN_FILTER:
- mMinFilter = clamped.i;
- break;
- case LOCAL_GL_TEXTURE_MAG_FILTER:
- mMagFilter = clamped.i;
- break;
- case LOCAL_GL_TEXTURE_WRAP_S:
- mWrapS = clamped.i;
- break;
- case LOCAL_GL_TEXTURE_WRAP_T:
- mWrapT = clamped.i;
- break;
- case LOCAL_GL_TEXTURE_COMPARE_MODE:
- mTexCompareMode = clamped.i;
- break;
- // We don't actually need to store the WRAP_R, since it doesn't change texture
- // completeness rules.
- }
- // Only a couple of pnames don't need to invalidate our resolve status cache.
- switch (pname) {
- case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
- case LOCAL_GL_TEXTURE_WRAP_R:
- break;
- default:
- InvalidateResolveCache();
- break;
- }
- ////////////////
- mContext->MakeContextCurrent();
- if (!clamped.isFloat)
- mContext->gl->fTexParameteri(texTarget.get(), pname, clamped.i);
- else
- mContext->gl->fTexParameterf(texTarget.get(), pname, clamped.f);
- }
- void WebGLTexture::Truncate() {
- for (auto& cur : mImageInfoArr) {
- SetImageInfo(&cur, ImageInfo());
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
- NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
- NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)
- } // namespace mozilla
|