// // Copyright 2002 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //
// Texture.cpp: Implements the gl::Texture class. [OpenGL ES 2.0.24] section 3.7 page 63.
GLuint TextureState::getEffectiveBaseLevel() const
{ if (mImmutableFormat)
{ // GLES 3.0.4 section 3.8.10 return std::min(mBaseLevel, mImmutableLevels - 1);
} // Some classes use the effective base level to index arrays with level data. By clamping the // effective base level to max levels these arrays need just one extra item to store properties // that should be returned for all out-of-range base level values, instead of needing special // handling for out-of-range base levels. return std::min(mBaseLevel, static_cast<GLuint>(IMPLEMENTATION_MAX_TEXTURE_LEVELS));
}
// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. // According to [OpenGL ES 3.0.5] section 3.8.13 Texture Completeness page 160 any // per-level checks begin at the base-level. // For OpenGL ES2 the base level is always zero. bool TextureState::isCubeComplete() const
{
ASSERT(mType == TextureType::CubeMap);
// According to es 3.1 spec, texture is justified as incomplete if sized internalformat is // unfilterable(table 20.11) and filter is not GL_NEAREST(8.16). The default value of minFilter // is NEAREST_MIPMAP_LINEAR and magFilter is LINEAR(table 20.11,). For multismaple texture, // filter state of multisample texture is ignored(11.1.3.3). So it shouldn't be judged as // incomplete texture. So, we ignore filtering for multisample texture completeness here. if (!IsMultisampled(mType) &&
!baseImageDesc.format.info->filterSupport(state.getClientVersion(),
state.getExtensions()) &&
!IsPointSampled(samplerState))
{ returnfalse;
}
// OpenGLES 3.0.2 spec section 3.8.13 states that a texture is not mipmap complete if: // The internalformat specified for the texture arrays is a sized internal depth or // depth and stencil format (see table 3.13), the value of TEXTURE_COMPARE_- // MODE is NONE, and either the magnification filter is not NEAREST or the mini- // fication filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST. if (!IsMultisampled(mType) && baseImageDesc.format.info->depthBits > 0 &&
state.getClientMajorVersion() >= 3)
{ // Note: we restrict this validation to sized types. For the OES_depth_textures // extension, due to some underspecification problems, we must allow linear filtering // for legacy compatibility with WebGL 1. // See http://crbug.com/649200 if (samplerState.getCompareMode() == GL_NONE && baseImageDesc.format.info->sized)
{ if ((samplerState.getMinFilter() != GL_NEAREST &&
samplerState.getMinFilter() != GL_NEAREST_MIPMAP_NEAREST) ||
samplerState.getMagFilter() != GL_NEAREST)
{ returnfalse;
}
}
}
// OpenGLES 3.1 spec section 8.16 states that a texture is not mipmap complete if: // The internalformat specified for the texture is DEPTH_STENCIL format, the value of // DEPTH_STENCIL_TEXTURE_MODE is STENCIL_INDEX, and either the magnification filter is // not NEAREST or the minification filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST. // However, the ES 3.1 spec differs from the statement above, because it is incorrect. // See the issue at https://github.com/KhronosGroup/OpenGL-API/issues/33. // For multismaple texture, filter state of multisample texture is ignored(11.1.3.3). // So it shouldn't be judged as incomplete texture. So, we ignore filtering for multisample // texture completeness here. if (!IsMultisampled(mType) && baseImageDesc.format.info->depthBits > 0 &&
mDepthStencilTextureMode == GL_STENCIL_INDEX)
{ if ((samplerState.getMinFilter() != GL_NEAREST &&
samplerState.getMinFilter() != GL_NEAREST_MIPMAP_NEAREST) ||
samplerState.getMagFilter() != GL_NEAREST)
{ returnfalse;
}
}
returntrue;
}
// CopyImageSubData has more lax rules for texture completeness: format-based completeness rules are // ignored, so a texture can still be considered complete even if it violates format-specific // conditions bool TextureState::computeSamplerCompletenessForCopyImage(const SamplerState &samplerState, const State &state) const
{ // Buffer textures cannot be incomplete. if (mType == TextureType::Buffer)
{ returntrue;
}
if (!mImmutableFormat && mBaseLevel > mMaxLevel)
{ returnfalse;
} const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel()); if (baseImageDesc.size.width == 0 || baseImageDesc.size.height == 0 ||
baseImageDesc.size.depth == 0)
{ returnfalse;
} // The cases where the texture is incomplete because base level is out of range should be // handled by the above condition.
ASSERT(mBaseLevel < IMPLEMENTATION_MAX_TEXTURE_LEVELS || mImmutableFormat);
if (IsMipmapSupported(mType) && IsMipmapFiltered(samplerState.getMinFilter()))
{ if (!npotSupport)
{ if (!isPow2(baseImageDesc.size.width) || !isPow2(baseImageDesc.size.height))
{ returnfalse;
}
}
if (!computeMipmapCompleteness())
{ returnfalse;
}
} else
{ if (mType == TextureType::CubeMap && !isCubeComplete())
{ returnfalse;
}
}
// From GL_OES_EGL_image_external_essl3: If state is present in a sampler object bound to a // texture unit that would have been rejected by a call to TexParameter* for the texture bound // to that unit, the behavior of the implementation is as if the texture were incomplete. For // example, if TEXTURE_WRAP_S or TEXTURE_WRAP_T is set to anything but CLAMP_TO_EDGE on the // sampler object bound to a texture unit and the texture bound to that unit is an external // texture and EXT_EGL_image_external_wrap_modes is not enabled, the texture will be considered // incomplete. // Sampler object state which does not affect sampling for the type of texture bound // to a texture unit, such as TEXTURE_WRAP_R for an external texture, does not affect // completeness. if (mType == TextureType::External)
{ if (!state.getExtensions().EGLImageExternalWrapModesEXT)
{ if (samplerState.getWrapS() != GL_CLAMP_TO_EDGE ||
samplerState.getWrapT() != GL_CLAMP_TO_EDGE)
{ returnfalse;
}
}
// The mip chain will have either one or more sequential levels, or max levels, // but not a sparse one.
Optional<Extents> expectedSize; for (size_t enabledLevel = baseLevel; enabledLevel <= maxLevel; ++enabledLevel, ++levelCount)
{ // Note: for cube textures, we only check the first face.
TextureTarget target = TextureTypeToTarget(mType, 0);
size_t descIndex = GetImageDescIndex(target, enabledLevel); const Extents &levelSize = mImageDescs[descIndex].size;
void TextureState::setImageDesc(TextureTarget target, size_t level, const ImageDesc &desc)
{
size_t descIndex = GetImageDescIndex(target, level);
ASSERT(descIndex < mImageDescs.size());
mImageDescs[descIndex] = desc; if (desc.initState == InitState::MayNeedInit)
{
mInitState = InitState::MayNeedInit;
} else
{ // Scan for any uninitialized images. If there are none, set the init state of the entire // texture to initialized. The cost of the scan is only paid after doing image // initialization which is already very expensive. bool allImagesInitialized = true;
for (const ImageDesc &initDesc : mImageDescs)
{ if (initDesc.initState == InitState::MayNeedInit)
{
allImagesInitialized = false; break;
}
}
if (allImagesInitialized)
{
mInitState = InitState::Initialized;
}
}
}
// Note that an ImageIndex that represents an entire level of a cube map corresponds to 6 // ImageDescs, so if the cube map is cube complete, we return the ImageDesc of the first cube // face, and we don't allow using this function when the cube map is not cube complete. const ImageDesc &TextureState::getImageDesc(const ImageIndex &imageIndex) const
{ if (imageIndex.isEntireLevelCubeMap())
{
ASSERT(isCubeComplete()); const GLint levelIndex = imageIndex.getLevelIndex(); return getImageDesc(kCubeMapTextureTargetMin, levelIndex);
}
// Most if not all renderers clip these copies to the size of the source framebuffer, leaving // other pixels untouched. For safety in robust resource initialization, assume that that // clipping is going to occur when computing the region for which to ensure initialization. If // the copy lies entirely off the source framebuffer, initialize as though a zero-size box is // going to be set during the copy operation.
Box destBox; bool forceCopySubImage = false; if (context->isRobustResourceInitEnabled())
{ const FramebufferAttachment *sourceReadAttachment = source->getReadColorAttachment();
Extents fbSize = sourceReadAttachment->getSize(); // Force using copySubImage when the source area is out of bounds AND // we're not copying to and from the same texture
forceCopySubImage = ((sourceArea.x < 0) || (sourceArea.y < 0) ||
((sourceArea.x + sourceArea.width) > fbSize.width) ||
((sourceArea.y + sourceArea.height) > fbSize.height)) &&
(sourceReadAttachment->getResource() != this);
Rectangle clippedArea; if (ClipRectangle(sourceArea, Rectangle(0, 0, fbSize.width, fbSize.height), &clippedArea))
{ const Offset clippedOffset(clippedArea.x - sourceArea.x, clippedArea.y - sourceArea.y,
0);
destBox = Box(clippedOffset.x, clippedOffset.y, clippedOffset.z, clippedArea.width,
clippedArea.height, 1);
}
}
// If we need to initialize the destination texture we split the call into a create call, // an initializeContents call, and then a copySubImage call. This ensures the destination // texture exists before we try to clear it.
Extents size(sourceArea.width, sourceArea.height, 1); if (forceCopySubImage || doesSubImageNeedInit(context, index, destBox))
{
ANGLE_TRY(mTexture->setImage(context, index, internalFormat, size,
internalFormatInfo.format, internalFormatInfo.type,
PixelUnpackState(), nullptr, nullptr));
mState.setImageDesc(target, level, ImageDesc(size, Format(internalFormatInfo), initState));
ANGLE_TRY(ensureSubImageInitialized(context, index, destBox));
ANGLE_TRY(mTexture->copySubImage(context, index, Offset(), sourceArea, source));
} else
{
ANGLE_TRY(mTexture->copyImage(context, index, sourceArea, internalFormat, source));
}
// Most if not all renderers clip these copies to the size of the source framebuffer, leaving // other pixels untouched. For safety in robust resource initialization, assume that that // clipping is going to occur when computing the region for which to ensure initialization. If // the copy lies entirely off the source framebuffer, initialize as though a zero-size box is // going to be set during the copy operation. Note that this assumes that // ensureSubImageInitialized ensures initialization of the entire destination texture, and not // just a sub-region.
Box destBox; if (context->isRobustResourceInitEnabled())
{
Extents fbSize = source->getReadColorAttachment()->getSize();
Rectangle clippedArea; if (ClipRectangle(sourceArea, Rectangle(0, 0, fbSize.width, fbSize.height), &clippedArea))
{ const Offset clippedOffset(destOffset.x + clippedArea.x - sourceArea.x,
destOffset.y + clippedArea.y - sourceArea.y, 0);
destBox = Box(clippedOffset.x, clippedOffset.y, clippedOffset.z, clippedArea.width,
clippedArea.height, 1);
}
}
// Initialize source texture. // Note: we don't have a way to notify which portions of the image changed currently.
ANGLE_TRY(source->ensureInitialized(context));
ImageIndex index = ImageIndex::MakeFromTarget(target, level, ImageIndex::kEntireLevel);
angle::Result Texture::copyCompressedTexture(Context *context, const Texture *source)
{ // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
ANGLE_TRY(releaseTexImageInternal(context));
// Changing the texture to immutable can trigger a change in the base and max levels: // GLES 3.0.4 section 3.8.10 pg 158: // "For immutable-format textures, levelbase is clamped to the range[0;levels],levelmax is then // clamped to the range[levelbase;levels].
mDirtyBits.set(DIRTY_BIT_BASE_LEVEL);
mDirtyBits.set(DIRTY_BIT_MAX_LEVEL);
// Changing the texture to immutable can trigger a change in the base and max levels: // GLES 3.0.4 section 3.8.10 pg 158: // "For immutable-format textures, levelbase is clamped to the range[0;levels],levelmax is then // clamped to the range[levelbase;levels].
mDirtyBits.set(DIRTY_BIT_BASE_LEVEL);
mDirtyBits.set(DIRTY_BIT_MAX_LEVEL);
signalDirtyStorage(InitState::Initialized);
return angle::Result::Continue;
}
angle::Result Texture::generateMipmap(Context *context)
{ // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
ANGLE_TRY(releaseTexImageInternal(context));
// EGL_KHR_gl_image states that images are only orphaned when generating mipmaps if the texture // is not mip complete.
egl::RefCountObjectReleaser<egl::Image> releaseImage; if (!isMipmapComplete())
{
ANGLE_TRY(orphanImages(context, &releaseImage));
}
if (maxLevel <= baseLevel)
{ return angle::Result::Continue;
}
// If any dimension is zero, this is a no-op: // // > Otherwise, if level_base is not defined, or if any dimension is zero, all mipmap levels are // > left unchanged. This is not an error. const ImageDesc &baseImageInfo = mState.getImageDesc(mState.getBaseImageTarget(), baseLevel); if (baseImageInfo.size.empty())
{ return angle::Result::Continue;
}
// Clear the base image(s) immediately if needed if (context->isRobustResourceInitEnabled())
{
ImageIndexIterator it =
ImageIndexIterator::MakeGeneric(mState.mType, baseLevel, baseLevel + 1,
ImageIndex::kEntireLevel, ImageIndex::kEntireLevel); while (it.hasNext())
{ const ImageIndex index = it.next(); const ImageDesc &desc = mState.getImageDesc(index.getTarget(), index.getLevelIndex());
// Propagate the format and size of the base mip to the smaller ones. Cube maps are guaranteed // to have faces of the same size and format so any faces can be picked.
mState.setImageDescChain(baseLevel, maxLevel, baseImageInfo.size, baseImageInfo.format,
InitState::Initialized);
if (mBoundSurface)
{
ANGLE_TRY(releaseTexImageFromSurface(context));
}
mBoundSurface = surface;
// Set the image info to the size and format of the surface
ASSERT(mState.mType == TextureType::_2D || mState.mType == TextureType::Rectangle);
Extents size(surface->getWidth(), surface->getHeight(), 1);
ImageDesc desc(size, surface->getBindTexImageFormat(), InitState::Initialized);
mState.setImageDesc(NonCubeTextureTypeToTarget(mState.mType), 0, desc);
mState.mHasProtectedContent = surface->hasProtectedContent();
// Set to incomplete
mState.clearImageDesc(NonCubeTextureTypeToTarget(mState.mType), 0);
signalDirtyStorage(InitState::Initialized); return angle::Result::Continue;
}
angle::Result Texture::releaseTexImageInternal(Context *context)
{ if (mBoundSurface)
{ // Notify the surface
egl::Error eglErr = mBoundSurface->releaseTexImageFromTexture(context); // TODO(jmadill): Remove this once refactor is complete. http://anglebug.com/3041 if (eglErr.isError())
{
context->handleError(GL_INVALID_OPERATION, "Error releasing tex image from texture",
__FILE__, ANGLE_FUNCTION, __LINE__);
}
// Then, call the same method as from the surface
ANGLE_TRY(releaseTexImageFromSurface(context));
} return angle::Result::Continue;
}
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.