void UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(TexInternalFormat effectiveinternalformat, TexInternalFormat* out_internalformat, TexType* out_type) { MOZ_ASSERT(TypeFromInternalFormat(effectiveinternalformat) != LOCAL_GL_NONE); MOZ_ASSERT(out_internalformat); MOZ_ASSERT(out_type); GLenum internalformat = LOCAL_GL_NONE; GLenum type = LOCAL_GL_NONE; switch (effectiveinternalformat.get()) { #define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \ case table_effectiveinternalformat: \ internalformat = table_internalformat; \ type = table_type; \ break; #include "WebGLInternalFormatsTable.h" default: MOZ_CRASH(); // impossible to get here } *out_internalformat = internalformat; *out_type = type; }
GLComponents::GLComponents(TexInternalFormat internalformat) { TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat); mComponents = 0; switch (unsizedformat.get()) { case LOCAL_GL_RGBA: case LOCAL_GL_RGBA4: case LOCAL_GL_RGBA8: case LOCAL_GL_RGB5_A1: // Luminance + Alpha can be converted // to and from RGBA case LOCAL_GL_LUMINANCE_ALPHA: mComponents |= Components::Alpha; // Drops through case LOCAL_GL_RGB: case LOCAL_GL_RGB565: // Luminance can be converted to and from RGB case LOCAL_GL_LUMINANCE: mComponents |= Components::Red | Components::Green | Components::Blue; break; case LOCAL_GL_ALPHA: mComponents |= Components::Alpha; break; case LOCAL_GL_DEPTH_COMPONENT: mComponents |= Components::Depth; break; case LOCAL_GL_DEPTH_STENCIL: mComponents |= Components::Stencil; break; default: MOZ_ASSERT(false, "Unhandled case - GLComponents"); break; } }
bool WebGLContext::IsTextureFormatCompressed(TexInternalFormat format) { switch (format.get()) { case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case LOCAL_GL_ATC_RGB: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: case LOCAL_GL_ETC1_RGB8_OES: return true; default: return false; } }
bool WebGLContext::IsTextureFormatCompressed(TexInternalFormat format) { return IsCompressedTextureFormat(format.get()); }
/** * Return the bits per texel for format & type combination. * Assumes that format & type are a valid combination as checked with * ValidateTexImageFormatAndType(). */ size_t GetBitsPerTexel(TexInternalFormat effectiveinternalformat) { switch (effectiveinternalformat.get()) { case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: return 2; case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case LOCAL_GL_ATC_RGB: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: case LOCAL_GL_ETC1_RGB8_OES: return 4; case LOCAL_GL_ALPHA8: case LOCAL_GL_LUMINANCE8: case LOCAL_GL_R8: case LOCAL_GL_R8I: case LOCAL_GL_R8UI: case LOCAL_GL_R8_SNORM: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: return 8; case LOCAL_GL_LUMINANCE8_ALPHA8: case LOCAL_GL_RGBA4: case LOCAL_GL_RGB5_A1: case LOCAL_GL_DEPTH_COMPONENT16: case LOCAL_GL_RG8: case LOCAL_GL_R16I: case LOCAL_GL_R16UI: case LOCAL_GL_RGB565: case LOCAL_GL_R16F: case LOCAL_GL_RG8I: case LOCAL_GL_RG8UI: case LOCAL_GL_RG8_SNORM: case LOCAL_GL_ALPHA16F_EXT: case LOCAL_GL_LUMINANCE16F_EXT: return 16; case LOCAL_GL_RGB8: case LOCAL_GL_DEPTH_COMPONENT24: case LOCAL_GL_SRGB8: case LOCAL_GL_RGB8UI: case LOCAL_GL_RGB8I: case LOCAL_GL_RGB8_SNORM: return 24; case LOCAL_GL_RGBA8: case LOCAL_GL_RGB10_A2: case LOCAL_GL_R32F: case LOCAL_GL_RG16F: case LOCAL_GL_R32I: case LOCAL_GL_R32UI: case LOCAL_GL_RG16I: case LOCAL_GL_RG16UI: case LOCAL_GL_DEPTH24_STENCIL8: case LOCAL_GL_R11F_G11F_B10F: case LOCAL_GL_RGB9_E5: case LOCAL_GL_SRGB8_ALPHA8: case LOCAL_GL_DEPTH_COMPONENT32F: case LOCAL_GL_RGBA8UI: case LOCAL_GL_RGBA8I: case LOCAL_GL_RGBA8_SNORM: case LOCAL_GL_RGB10_A2UI: case LOCAL_GL_LUMINANCE_ALPHA16F_EXT: case LOCAL_GL_ALPHA32F_EXT: case LOCAL_GL_LUMINANCE32F_EXT: return 32; case LOCAL_GL_DEPTH32F_STENCIL8: return 40; case LOCAL_GL_RGB16F: case LOCAL_GL_RGB16UI: case LOCAL_GL_RGB16I: return 48; case LOCAL_GL_RG32F: case LOCAL_GL_RG32I: case LOCAL_GL_RG32UI: case LOCAL_GL_RGBA16F: case LOCAL_GL_RGBA16UI: case LOCAL_GL_RGBA16I: case LOCAL_GL_LUMINANCE_ALPHA32F_EXT: return 64; case LOCAL_GL_RGB32F: case LOCAL_GL_RGB32UI: case LOCAL_GL_RGB32I: return 96; case LOCAL_GL_RGBA32F: case LOCAL_GL_RGBA32UI: case LOCAL_GL_RGBA32I: return 128; default: MOZ_ASSERT(false, "Unhandled format"); return 0; } }
/** * Convert effective internalformat into GL function parameters * valid for underlying driver. */ void DriverFormatsFromEffectiveInternalFormat(gl::GLContext* gl, TexInternalFormat effectiveinternalformat, GLenum* out_driverInternalFormat, GLenum* out_driverFormat, GLenum* out_driverType) { MOZ_ASSERT(out_driverInternalFormat); MOZ_ASSERT(out_driverFormat); MOZ_ASSERT(out_driverType); TexInternalFormat unsizedinternalformat = LOCAL_GL_NONE; TexType type = LOCAL_GL_NONE; UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(effectiveinternalformat, &unsizedinternalformat, &type); // driverType: almost always the generic type that we just got, except on ES // we must replace HALF_FLOAT by HALF_FLOAT_OES GLenum driverType = type.get(); if (gl->IsGLES() && type == LOCAL_GL_HALF_FLOAT) { driverType = LOCAL_GL_HALF_FLOAT_OES; } // driverFormat: always just the unsized internalformat that we just got GLenum driverFormat = unsizedinternalformat.get(); // driverInternalFormat: almost always the same as driverFormat, but on desktop GL, // in some cases we must pass a different value. On ES, they are equal by definition // as it is an error to pass internalformat!=format. GLenum driverInternalFormat = driverFormat; if (!gl->IsGLES()) { // Cases where desktop OpenGL requires a tweak to 'format' if (driverFormat == LOCAL_GL_SRGB) { driverFormat = LOCAL_GL_RGB; } else if (driverFormat == LOCAL_GL_SRGB_ALPHA) { driverFormat = LOCAL_GL_RGBA; } // WebGL2's new formats are not legal values for internalformat, // as using unsized internalformat is deprecated. if (driverFormat == LOCAL_GL_RED || driverFormat == LOCAL_GL_RG || driverFormat == LOCAL_GL_RED_INTEGER || driverFormat == LOCAL_GL_RG_INTEGER || driverFormat == LOCAL_GL_RGB_INTEGER || driverFormat == LOCAL_GL_RGBA_INTEGER) { driverInternalFormat = effectiveinternalformat.get(); } // Cases where desktop OpenGL requires a sized internalformat, // as opposed to the unsized internalformat that had the same // GLenum value as 'format', in order to get the precise // semantics that we want. For example, for floating-point formats, // we seem to need a sized internalformat to get non-clamped floating // point texture sampling. Can't find the spec reference for that, // but that's at least the case on my NVIDIA driver version 331. if (unsizedinternalformat == LOCAL_GL_DEPTH_COMPONENT || unsizedinternalformat == LOCAL_GL_DEPTH_STENCIL || type == LOCAL_GL_FLOAT || type == LOCAL_GL_HALF_FLOAT) { driverInternalFormat = effectiveinternalformat.get(); } } *out_driverInternalFormat = driverInternalFormat; *out_driverFormat = driverFormat; *out_driverType = driverType; }
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); }
// `mask` from glClear. static bool ClearWithTempFB(WebGLContext* webgl, GLuint tex, TexImageTarget texImageTarget, GLint level, TexInternalFormat baseInternalFormat, GLsizei width, GLsizei height) { MOZ_ASSERT(texImageTarget == LOCAL_GL_TEXTURE_2D); gl::GLContext* gl = webgl->GL(); MOZ_ASSERT(gl->IsCurrent()); gl::ScopedFramebuffer fb(gl); gl::ScopedBindFramebuffer autoFB(gl, fb.FB()); GLbitfield mask = 0; switch (baseInternalFormat.get()) { case LOCAL_GL_LUMINANCE: case LOCAL_GL_LUMINANCE_ALPHA: case LOCAL_GL_ALPHA: case LOCAL_GL_RGB: case LOCAL_GL_RGBA: case LOCAL_GL_BGR: case LOCAL_GL_BGRA: mask = LOCAL_GL_COLOR_BUFFER_BIT; gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, texImageTarget.get(), tex, level); break; case LOCAL_GL_DEPTH_COMPONENT32_OES: case LOCAL_GL_DEPTH_COMPONENT24_OES: case LOCAL_GL_DEPTH_COMPONENT16: case LOCAL_GL_DEPTH_COMPONENT: mask = LOCAL_GL_DEPTH_BUFFER_BIT; gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, texImageTarget.get(), tex, level); break; case LOCAL_GL_DEPTH24_STENCIL8: case LOCAL_GL_DEPTH_STENCIL: mask = LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT; gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, texImageTarget.get(), tex, level); gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, texImageTarget.get(), tex, level); break; default: return false; } MOZ_ASSERT(mask); if (ClearByMask(webgl, mask)) return true; // Failed to simply build an FB from the tex, but maybe it needs a // color buffer to be complete. if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { // Nope, it already had one. return false; } gl::ScopedRenderbuffer rb(gl); { // Only GLES guarantees RGBA4. GLenum format = gl->IsGLES() ? LOCAL_GL_RGBA4 : LOCAL_GL_RGBA8; gl::ScopedBindRenderbuffer rbBinding(gl, rb.RB()); gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, format, width, height); } gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, rb.RB()); mask |= LOCAL_GL_COLOR_BUFFER_BIT; // Last chance! return ClearByMask(webgl, mask); }
/** * Convert WebGL/ES format and type into GL internal * format valid for underlying driver. */ void DriverFormatsFromFormatAndType(GLContext* gl, TexInternalFormat webGLInternalFormat, TexType webGLType, GLenum* out_driverInternalFormat, GLenum* out_driverFormat) { MOZ_ASSERT(out_driverInternalFormat); MOZ_ASSERT(out_driverFormat); // ES2 requires that format == internalformat; floating-point is // indicated purely by the type that's loaded. For desktop GL, we // have to specify a floating point internal format. if (gl->IsGLES()) { *out_driverFormat = *out_driverInternalFormat = webGLInternalFormat.get(); return; } GLenum internalFormat = LOCAL_GL_NONE; GLenum format = LOCAL_GL_NONE; if (webGLInternalFormat == LOCAL_GL_DEPTH_COMPONENT) { format = LOCAL_GL_DEPTH_COMPONENT; if (webGLType == LOCAL_GL_UNSIGNED_SHORT) internalFormat = LOCAL_GL_DEPTH_COMPONENT16; else if (webGLType == LOCAL_GL_UNSIGNED_INT) internalFormat = LOCAL_GL_DEPTH_COMPONENT32; } else if (webGLInternalFormat == LOCAL_GL_DEPTH_STENCIL) { format = LOCAL_GL_DEPTH_STENCIL; if (webGLType == LOCAL_GL_UNSIGNED_INT_24_8_EXT) internalFormat = LOCAL_GL_DEPTH24_STENCIL8; } else { switch (webGLType.get()) { case LOCAL_GL_UNSIGNED_BYTE: case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: case LOCAL_GL_UNSIGNED_SHORT_5_6_5: format = internalFormat = webGLInternalFormat.get(); break; case LOCAL_GL_FLOAT: switch (webGLInternalFormat.get()) { case LOCAL_GL_RGBA: format = LOCAL_GL_RGBA; internalFormat = LOCAL_GL_RGBA32F; break; case LOCAL_GL_RGB: format = LOCAL_GL_RGB; internalFormat = LOCAL_GL_RGB32F; break; case LOCAL_GL_ALPHA: format = LOCAL_GL_ALPHA; internalFormat = LOCAL_GL_ALPHA32F_ARB; break; case LOCAL_GL_LUMINANCE: format = LOCAL_GL_LUMINANCE; internalFormat = LOCAL_GL_LUMINANCE32F_ARB; break; case LOCAL_GL_LUMINANCE_ALPHA: format = LOCAL_GL_LUMINANCE_ALPHA; internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB; break; } break; case LOCAL_GL_HALF_FLOAT_OES: switch (webGLInternalFormat.get()) { case LOCAL_GL_RGBA: format = LOCAL_GL_RGBA; internalFormat = LOCAL_GL_RGBA16F; break; case LOCAL_GL_RGB: format = LOCAL_GL_RGB; internalFormat = LOCAL_GL_RGB16F; break; case LOCAL_GL_ALPHA: format = LOCAL_GL_ALPHA; internalFormat = LOCAL_GL_ALPHA16F_ARB; break; case LOCAL_GL_LUMINANCE: format = LOCAL_GL_LUMINANCE; internalFormat = LOCAL_GL_LUMINANCE16F_ARB; break; case LOCAL_GL_LUMINANCE_ALPHA: format = LOCAL_GL_LUMINANCE_ALPHA; internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB; break; } break; default: break; } // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES // requires that format == internalformat, but GL will fail in this case. // GL requires: // format -> internalformat // GL_RGB GL_SRGB_EXT // GL_RGBA GL_SRGB_ALPHA_EXT switch (webGLInternalFormat.get()) { case LOCAL_GL_SRGB: format = LOCAL_GL_RGB; internalFormat = LOCAL_GL_SRGB; break; case LOCAL_GL_SRGB_ALPHA: format = LOCAL_GL_RGBA; internalFormat = LOCAL_GL_SRGB_ALPHA; break; } } MOZ_ASSERT(webGLInternalFormat != LOCAL_GL_NONE && internalFormat != LOCAL_GL_NONE, "Coding mistake -- bad format/type passed?"); *out_driverInternalFormat = internalFormat; *out_driverFormat = format; }