// // Copyright 2013 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. //
// validationES.h: Validation functions for generic OpenGL ES entry point parameters
namespace
{ bool CompressedTextureFormatRequiresExactSize(GLenum internalFormat)
{ // List of compressed format that require that the texture size is smaller than or a multiple of // the compressed block size. switch (internalFormat)
{ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE: case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE: case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: case GL_COMPRESSED_RGBA8_LOSSY_DECODE_ETC2_EAC_ANGLE: case GL_COMPRESSED_SRGB8_ALPHA8_LOSSY_DECODE_ETC2_EAC_ANGLE: case GL_COMPRESSED_RED_RGTC1_EXT: case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT: case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: case GL_COMPRESSED_RGBA_BPTC_UNORM_EXT: case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT: case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT: case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT: returntrue;
default: returnfalse;
}
} bool CompressedSubTextureFormatRequiresExactSize(GLenum internalFormat)
{ // Compressed sub textures have additional formats that requires exact size. // ES 3.1, Section 8.7, Page 171 return CompressedTextureFormatRequiresExactSize(internalFormat) ||
IsETC2EACFormat(internalFormat) || IsASTC2DFormat(internalFormat);
}
bool DifferenceCanOverflow(GLint a, GLint b)
{
CheckedNumeric<GLint> checkedA(a);
checkedA -= b; // Use negation to make sure that the difference can't overflow regardless of the order.
checkedA = -checkedA; return !checkedA.IsValid();
}
bool ValidReadPixelsTypeEnum(const Context *context, GLenum type)
{ switch (type)
{ // Types referenced in Table 3.4 of the ES 2.0.25 spec case GL_UNSIGNED_BYTE: case GL_UNSIGNED_SHORT_4_4_4_4: case GL_UNSIGNED_SHORT_5_5_5_1: case GL_UNSIGNED_SHORT_5_6_5: return context->getClientVersion() >= ES_2_0;
// Types referenced in Table 3.2 of the ES 3.0.5 spec (Except depth stencil) case GL_BYTE: case GL_INT: case GL_SHORT: case GL_UNSIGNED_INT: case GL_UNSIGNED_INT_10F_11F_11F_REV: case GL_UNSIGNED_INT_2_10_10_10_REV: case GL_UNSIGNED_INT_5_9_9_9_REV: case GL_UNSIGNED_SHORT: case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT: case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT: return context->getClientVersion() >= ES_3_0;
case GL_FLOAT: return context->getClientVersion() >= ES_3_0 ||
context->getExtensions().textureFloatOES ||
context->getExtensions().colorBufferHalfFloatEXT;
case GL_HALF_FLOAT: return context->getClientVersion() >= ES_3_0 ||
context->getExtensions().textureHalfFloatOES;
case GL_HALF_FLOAT_OES: return context->getExtensions().colorBufferHalfFloatEXT;
default: returnfalse;
}
}
bool ValidReadPixelsFormatEnum(const Context *context, GLenum format)
{ switch (format)
{ // Formats referenced in Table 3.4 of the ES 2.0.25 spec (Except luminance) case GL_RGBA: case GL_RGB: case GL_ALPHA: return context->getClientVersion() >= ES_2_0;
// Formats referenced in Table 3.2 of the ES 3.0.5 spec case GL_RG: case GL_RED: case GL_RGBA_INTEGER: case GL_RGB_INTEGER: case GL_RG_INTEGER: case GL_RED_INTEGER: return context->getClientVersion() >= ES_3_0;
case GL_SRGB_ALPHA_EXT: case GL_SRGB_EXT: return context->getExtensions().sRGBEXT;
case GL_BGRA_EXT: return context->getExtensions().readFormatBgraEXT;
case GL_RGBX8_ANGLE: return context->getExtensions().rgbxInternalFormatANGLE;
case GL_CLAMP_TO_BORDER: if (!context->getExtensions().textureBorderClampAny() &&
context->getClientVersion() < ES_3_2)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kExtensionNotEnabled); returnfalse;
} break;
case GL_REPEAT: case GL_MIRRORED_REPEAT: if (restrictedWrapModes)
{ // OES_EGL_image_external and ANGLE_texture_rectangle specifies this error.
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidWrapModeTexture); returnfalse;
} break;
case GL_NEAREST_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: case GL_LINEAR_MIPMAP_LINEAR: if (restrictedMinFilter)
{ // OES_EGL_image_external specifies this error.
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFilterTexture); returnfalse;
} break;
template <typename ParamType> bool ValidateTextureCompareFuncValue(const Context *context,
angle::EntryPoint entryPoint, const ParamType *params)
{ // Acceptable function parameters from GLES 3.0.2 spec, table 3.17 switch (ConvertToGLenum(params[0]))
{ case GL_LEQUAL: case GL_GEQUAL: case GL_LESS: case GL_GREATER: case GL_EQUAL: case GL_NOTEQUAL: case GL_ALWAYS: case GL_NEVER: break;
auto drawBufferMask =
framebuffer->getDrawBufferMask() & glState.getBlendStateExt().compareColorMask(0); auto fragmentOutputMask = program->getExecutable().getActiveOutputVariablesMask();
bool IsCompatibleDrawModeWithGeometryShader(PrimitiveMode drawMode,
PrimitiveMode geometryShaderInputPrimitiveType)
{ // [EXT_geometry_shader] Section 11.1gs.1, Geometry Shader Input Primitives switch (drawMode)
{ case PrimitiveMode::Points: return geometryShaderInputPrimitiveType == PrimitiveMode::Points; case PrimitiveMode::Lines: case PrimitiveMode::LineStrip: case PrimitiveMode::LineLoop: return geometryShaderInputPrimitiveType == PrimitiveMode::Lines; case PrimitiveMode::LinesAdjacency: case PrimitiveMode::LineStripAdjacency: return geometryShaderInputPrimitiveType == PrimitiveMode::LinesAdjacency; case PrimitiveMode::Triangles: case PrimitiveMode::TriangleFan: case PrimitiveMode::TriangleStrip: return geometryShaderInputPrimitiveType == PrimitiveMode::Triangles; case PrimitiveMode::TrianglesAdjacency: case PrimitiveMode::TriangleStripAdjacency: return geometryShaderInputPrimitiveType == PrimitiveMode::TrianglesAdjacency; default:
UNREACHABLE(); returnfalse;
}
}
// GLES1 texture parameters are a small subset of the others bool IsValidGLES1TextureParameter(GLenum pname)
{ switch (pname)
{ case GL_TEXTURE_MAG_FILTER: case GL_TEXTURE_MIN_FILTER: case GL_TEXTURE_WRAP_S: case GL_TEXTURE_WRAP_T: case GL_TEXTURE_WRAP_R: case GL_GENERATE_MIPMAP: case GL_TEXTURE_CROP_RECT_OES: returntrue; default: returnfalse;
}
}
if (extensions.blendEquationAdvancedKHR)
{
errorString = ValidateProgramDrawAdvancedBlendState(context, program);
}
return errorString;
}
} // anonymous namespace
void SetRobustLengthParam(const GLsizei *length, GLsizei value)
{ if (length)
{ // Currently we modify robust length parameters in the validation layer. We should be only // doing this in the Context instead. // TODO(http://anglebug.com/4406): Remove when possible.
*const_cast<GLsizei *>(length) = value;
}
}
bool ValidTextureTarget(const Context *context, TextureType type)
{ switch (type)
{ case TextureType::_2D: case TextureType::CubeMap: returntrue;
case TextureType::Rectangle: return context->getExtensions().textureRectangleANGLE;
case TextureType::_3D: return ((context->getClientMajorVersion() >= 3) ||
context->getExtensions().texture3DOES);
case TextureType::_2DArray: return (context->getClientMajorVersion() >= 3);
case TextureType::_2DMultisample: return (context->getClientVersion() >= Version(3, 1) ||
context->getExtensions().textureMultisampleANGLE); case TextureType::_2DMultisampleArray: return context->getExtensions().textureStorageMultisample2dArrayOES;
case TextureType::CubeMapArray: return (context->getClientVersion() >= Version(3, 2) ||
context->getExtensions().textureCubeMapArrayAny());
case TextureType::VideoImage: return context->getExtensions().videoTextureWEBGL;
case TextureType::Buffer: return (context->getClientVersion() >= Version(3, 2) ||
context->getExtensions().textureBufferAny());
default: returnfalse;
}
}
bool ValidTexture2DTarget(const Context *context, TextureType type)
{ switch (type)
{ case TextureType::_2D: case TextureType::CubeMap: returntrue;
case TextureType::Rectangle: return context->getExtensions().textureRectangleANGLE;
default: returnfalse;
}
}
bool ValidTexture3DTarget(const Context *context, TextureType target)
{ switch (target)
{ case TextureType::_3D: case TextureType::_2DArray: return (context->getClientMajorVersion() >= 3);
case TextureType::CubeMapArray: return (context->getClientVersion() >= Version(3, 2) ||
context->getExtensions().textureCubeMapArrayAny());
default: returnfalse;
}
}
// Most texture GL calls are not compatible with external textures, so we have a separate validation // function for use in the GL calls that do bool ValidTextureExternalTarget(const Context *context, TextureType target)
{ return (target == TextureType::External) &&
(context->getExtensions().EGLImageExternalOES ||
context->getExtensions().EGLStreamConsumerExternalNV);
}
// This function differs from ValidTextureTarget in that the target must be // usable as the destination of a 2D operation-- so a cube face is valid, but // GL_TEXTURE_CUBE_MAP is not. // Note: duplicate of IsInternalTextureTarget bool ValidTexture2DDestinationTarget(const Context *context, TextureTarget target)
{ switch (target)
{ case TextureTarget::_2D: case TextureTarget::CubeMapNegativeX: case TextureTarget::CubeMapNegativeY: case TextureTarget::CubeMapNegativeZ: case TextureTarget::CubeMapPositiveX: case TextureTarget::CubeMapPositiveY: case TextureTarget::CubeMapPositiveZ: returntrue; case TextureTarget::Rectangle: return context->getExtensions().textureRectangleANGLE; case TextureTarget::VideoImage: return context->getExtensions().videoTextureWEBGL; default: returnfalse;
}
}
if ((!context->getExtensions().geometryShaderAny() ||
!context->getExtensions().tessellationShaderEXT) &&
context->getClientVersion() < ES_3_2)
{ // It is an invalid operation to call DrawArrays or DrawArraysInstanced with a draw mode // that does not match the current transform feedback object's draw mode (if transform // feedback is active), (3.0.2, section 2.14, pg 86) return transformFeedbackPrimitiveMode == renderPrimitiveMode;
}
const ProgramExecutable *executable = context->getState().getLinkedProgramExecutable(context);
ASSERT(executable); if (executable->hasLinkedShaderStage(ShaderType::Geometry))
{ // If geometry shader is active, transform feedback mode must match what is output from this // stage.
renderPrimitiveMode = executable->getGeometryShaderOutputPrimitiveType();
} elseif (executable->hasLinkedShaderStage(ShaderType::TessEvaluation))
{ // Similarly with tessellation shaders, but only if no geometry shader is present. With // tessellation shaders, only triangles are possibly output. return transformFeedbackPrimitiveMode == PrimitiveMode::Triangles &&
executable->getTessGenMode() == GL_TRIANGLES;
}
// [GL_EXT_geometry_shader] Table 12.1gs switch (renderPrimitiveMode)
{ case PrimitiveMode::Points: return transformFeedbackPrimitiveMode == PrimitiveMode::Points; case PrimitiveMode::Lines: case PrimitiveMode::LineStrip: case PrimitiveMode::LineLoop: return transformFeedbackPrimitiveMode == PrimitiveMode::Lines; case PrimitiveMode::Triangles: case PrimitiveMode::TriangleFan: case PrimitiveMode::TriangleStrip: return transformFeedbackPrimitiveMode == PrimitiveMode::Triangles; case PrimitiveMode::Patches: return transformFeedbackPrimitiveMode == PrimitiveMode::Patches; default:
UNREACHABLE(); returnfalse;
}
}
bool ValidateDrawInstancedANGLE(const Context *context, angle::EntryPoint entryPoint)
{ // Verify there is at least one active attribute with a divisor of zero const State &state = context->getState(); const ProgramExecutable *executable = state.getLinkedProgramExecutable(context);
if (!executable)
{ // No executable means there is no Program/PPO bound, which is undefined behavior, but isn't // an error.
context->getState().getDebug().insertMessage(
GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, 0, GL_DEBUG_SEVERITY_HIGH,
std::string("Attempting to draw without a program"), gl::LOG_WARN, entryPoint); returntrue;
}
switch (target)
{ case GL_FRAMEBUFFER: returntrue;
case GL_READ_FRAMEBUFFER: case GL_DRAW_FRAMEBUFFER: return (context->getExtensions().framebufferBlitAny() ||
context->getClientMajorVersion() >= 3);
default: returnfalse;
}
}
bool ValidMipLevel(const Context *context, TextureType type, GLint level)
{ constauto &caps = context->getCaps(); int maxDimension = 0; switch (type)
{ case TextureType::_2D: case TextureType::_2DArray: case TextureType::_2DMultisample: case TextureType::_2DMultisampleArray: // TODO(http://anglebug.com/2775): It's a bit unclear what the "maximum allowable // level-of-detail" for multisample textures should be. Could maybe make it zero.
maxDimension = caps.max2DTextureSize; break;
case TextureType::CubeMap: case TextureType::CubeMapArray:
maxDimension = caps.maxCubeMapTextureSize; break;
case TextureType::External: case TextureType::Rectangle: case TextureType::VideoImage: case TextureType::Buffer: return level == 0;
case TextureType::_3D:
maxDimension = caps.max3DTextureSize; break;
bool ValidImageSizeParameters(const Context *context,
angle::EntryPoint entryPoint,
TextureType target,
GLint level,
GLsizei width,
GLsizei height,
GLsizei depth, bool isSubImage)
{ if (width < 0 || height < 0 || depth < 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeSize); returnfalse;
} // TexSubImage parameters can be NPOT without textureNPOT extension, // as long as the destination texture is POT. bool hasNPOTSupport =
context->getExtensions().textureNpotOES || context->getClientVersion() >= Version(3, 0); if (!isSubImage && !hasNPOTSupport &&
(level != 0 && (!isPow2(width) || !isPow2(height) || !isPow2(depth))))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kTextureNotPow2); returnfalse;
}
if (!ValidMipLevel(context, target, level))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel); returnfalse;
}
returntrue;
}
bool ValidCompressedBaseLevel(GLsizei size, GLuint blockSize, GLint level)
{ // Already checked in ValidMipLevel.
ASSERT(level < 32); // This function is used only for 4x4 BC formats.
ASSERT(blockSize == 4); // Use the constant value to avoid division. return ((size << level) % 4) == 0;
}
if (!formatInfo.compressed && !formatInfo.paletted)
{ returnfalse;
}
// A texture format can not be both block-compressed and paletted
ASSERT(!(formatInfo.compressed && formatInfo.paletted));
if (formatInfo.compressed)
{ // Only PVRTC1 requires dimensions to be powers of two if (IsPVRTC1Format(internalFormat))
{ if (!isPow2(width) || !isPow2(height))
{ returnfalse;
}
if (context->getLimitations().squarePvrtc1)
{ if (width != height)
{ returnfalse;
}
}
}
if (CompressedTextureFormatRequiresExactSize(internalFormat))
{ // In WebGL compatibility mode and D3D, enforce that the base level implied // by the compressed texture's mip level would conform to the block // size. if (context->isWebGL() ||
context->getLimitations().compressedBaseMipLevelMultipleOfFour)
{ // This check is performed only for BC formats.
ASSERT(formatInfo.compressedBlockDepth == 1); if (!ValidCompressedBaseLevel(width, formatInfo.compressedBlockWidth, level) ||
!ValidCompressedBaseLevel(height, formatInfo.compressedBlockHeight, level))
{ returnfalse;
}
} // non-WebGL and non-D3D check is not necessary for the following formats // From EXT_texture_compression_s3tc specification: // If the width or height is not a multiple of four, there will be 4x4 blocks at the // edge of the image that contain "extra" texels that are not part of the image. From // EXT_texture_compression_bptc & EXT_texture_compression_rgtc specification: If an // RGTC/BPTC image has a width or height that is not a multiple of four, the data // corresponding to texels outside the image are irrelevant and undefined.
}
}
if (formatInfo.paletted)
{ // TODO(http://anglebug.com/7688): multi-level paletted images if (level != 0)
{ returnfalse;
}
if (!isPow2(width) || !isPow2(height))
{ returnfalse;
}
}
// ANGLE does not support compressed 3D blocks (provided exclusively by ASTC 3D formats), so // there is no need to check the depth here. Only width and height determine whether a 2D array // element or a 2D slice of a sliced 3D texture fill the entire level. bool fillsEntireMip = xoffset == 0 && yoffset == 0 && static_cast<size_t>(width) == textureWidth && static_cast<size_t>(height) == textureHeight;
if (CompressedFormatRequiresWholeImage(internalFormat))
{ return fillsEntireMip;
}
// Allowed to either have data that is a multiple of block size or is smaller than the block // size but fills the entire mip bool sizeMultipleOfBlockSize = (width % formatInfo.compressedBlockWidth) == 0 &&
(height % formatInfo.compressedBlockHeight) == 0 &&
(depth % formatInfo.compressedBlockDepth) == 0; if (!sizeMultipleOfBlockSize && !fillsEntireMip)
{ returnfalse;
}
}
// ...the data would be unpacked from the buffer object such that the memory reads required // would exceed the data store size. const InternalFormat &formatInfo = GetInternalFormatInfo(format, type);
ASSERT(formatInfo.internalFormat != GL_NONE); const Extents size(width, height, depth); constauto &unpack = context->getState().getUnpackState();
bool ValidQueryType(const Context *context, QueryType queryType)
{ switch (queryType)
{ case QueryType::AnySamples: case QueryType::AnySamplesConservative: return context->getClientMajorVersion() >= 3 ||
context->getExtensions().occlusionQueryBooleanEXT; case QueryType::TransformFeedbackPrimitivesWritten: return (context->getClientMajorVersion() >= 3); case QueryType::TimeElapsed: return context->getExtensions().disjointTimerQueryEXT; case QueryType::CommandsCompleted: return context->getExtensions().syncQueryCHROMIUM; case QueryType::PrimitivesGenerated: return context->getClientVersion() >= ES_3_2 ||
context->getExtensions().geometryShaderAny(); default: returnfalse;
}
}
bool ValidateWebGLVertexAttribPointer(const Context *context,
angle::EntryPoint entryPoint,
VertexAttribType type,
GLboolean normalized,
GLsizei stride, constvoid *ptr, bool pureInteger)
{
ASSERT(context->isWebGL()); // WebGL 1.0 [Section 6.11] Vertex Attribute Data Stride // The WebGL API supports vertex attribute data strides up to 255 bytes. A call to // vertexAttribPointer will generate an INVALID_VALUE error if the value for the stride // parameter exceeds 255.
constexpr GLsizei kMaxWebGLStride = 255; if (stride > kMaxWebGLStride)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kStrideExceedsWebGLLimit); returnfalse;
}
// WebGL 1.0 [Section 6.4] Buffer Offset and Stride Requirements // The offset arguments to drawElements and vertexAttribPointer, and the stride argument to // vertexAttribPointer, must be a multiple of the size of the data type passed to the call, // or an INVALID_OPERATION error is generated.
angle::FormatID internalType = GetVertexFormatID(type, normalized, 1, pureInteger);
size_t typeSize = GetVertexFormatSize(internalType);
Program *GetValidProgramNoResolve(const Context *context,
angle::EntryPoint entryPoint,
ShaderProgramID id)
{ // ES3 spec (section 2.11.1) -- "Commands that accept shader or program object names will // generate the error INVALID_VALUE if the provided name is not the name of either a shader // or program object and INVALID_OPERATION if the provided name identifies an object // that is not the expected type."
Program *validProgram = context->getProgramNoResolveLink(id);
if (!validProgram)
{ if (context->getShader(id))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kExpectedProgramName);
} else
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidProgramName);
}
}
return validProgram;
}
Program *GetValidProgram(const Context *context, angle::EntryPoint entryPoint, ShaderProgramID id)
{
Program *program = GetValidProgramNoResolve(context, entryPoint, id); if (program)
{
program->resolveLink(context);
} return program;
}
Shader *GetValidShader(const Context *context, angle::EntryPoint entryPoint, ShaderProgramID id)
{ // See ValidProgram for spec details.
Shader *validShader = context->getShader(id);
if (!validShader)
{ if (context->getProgramNoResolveLink(id))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kExpectedShaderName);
} else
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidShaderName);
}
}
// Color attachment 0 is validated below because it is always valid constint colorAttachment = (attachment - GL_COLOR_ATTACHMENT0_EXT); if (colorAttachment >= context->getCaps().maxColorAttachments)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidAttachment); returnfalse;
}
} else
{ switch (attachment)
{ case GL_COLOR_ATTACHMENT0: case GL_DEPTH_ATTACHMENT: case GL_STENCIL_ATTACHMENT: break;
case GL_DEPTH_STENCIL_ATTACHMENT: if (!context->isWebGL() && context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidAttachment); returnfalse;
} break;
// Hack for the special WebGL 1 "DEPTH_STENCIL" internal format.
GLenum convertedInternalFormat = context->getConvertedRenderbufferFormat(internalformat);
// ANGLE_framebuffer_multisample does not explicitly state that the internal format must be // sized but it does state that the format must be in the ES2.0 spec table 4.5 which contains // only sized internal formats. const InternalFormat &formatInfo = GetSizedInternalFormatInfo(convertedInternalFormat); if (formatInfo.internalFormat == GL_NONE)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidRenderbufferInternalFormat); returnfalse;
}
// ES3.0 spec, section 4.3.2 states that linear filtering is only available for the // color buffer, leaving only nearest being unfiltered from above if ((mask & ~GL_COLOR_BUFFER_BIT) != 0 && filter != GL_NEAREST)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kBlitOnlyNearestForNonColor); returnfalse;
}
if (!readFramebuffer || !drawFramebuffer)
{
context->validationError(entryPoint, GL_INVALID_FRAMEBUFFER_OPERATION,
kBlitFramebufferMissing); returnfalse;
}
if (!ValidateFramebufferComplete(context, entryPoint, readFramebuffer))
{ returnfalse;
}
if (!ValidateFramebufferComplete(context, entryPoint, drawFramebuffer))
{ returnfalse;
}
// EXT_YUV_target disallows blitting to or from a YUV framebuffer if ((mask & GL_COLOR_BUFFER_BIT) != 0 &&
(readFramebuffer->hasYUVAttachment() || drawFramebuffer->hasYUVAttachment()))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kBlitYUVFramebuffer); returnfalse;
}
// The draw and read framebuffers can only match if: // - They are the default framebuffer AND // - The read/draw surfaces are different if ((readFramebuffer->id() == drawFramebuffer->id()) &&
((drawFramebuffer->id() != Framebuffer::kDefaultDrawFramebufferHandle) ||
(context->getCurrentDrawSurface() == context->getCurrentReadSurface())))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kBlitFeedbackLoop); returnfalse;
}
// Not allow blitting to MS buffers, therefore if renderToTextureSamples exist, // consider it MS. checkReadBufferResourceSamples = false if (!ValidateFramebufferNotMultisampled(context, entryPoint, drawFramebuffer, false))
{ returnfalse;
}
// This validation is specified in the WebGL 2.0 spec and not in the GLES 3.0.5 spec, but we // always run it in order to avoid triggering driver bugs. if (DifferenceCanOverflow(srcX0, srcX1) || DifferenceCanOverflow(srcY0, srcY1) ||
DifferenceCanOverflow(dstX0, dstX1) || DifferenceCanOverflow(dstY0, dstY1))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kBlitDimensionsOutOfRange); returnfalse;
}
if (readColorBuffer)
{ const Format &readFormat = readColorBuffer->getFormat();
for (size_t drawbufferIdx = 0;
drawbufferIdx < drawFramebuffer->getDrawbufferStateCount(); ++drawbufferIdx)
{ const FramebufferAttachment *attachment =
drawFramebuffer->getDrawBuffer(drawbufferIdx); if (attachment)
{ const Format &drawFormat = attachment->getFormat();
// The GL ES 3.0.2 spec (pg 193) states that: // 1) If the read buffer is fixed point format, the draw buffer must be as well // 2) If the read buffer is an unsigned integer format, the draw buffer must be // as well // 3) If the read buffer is a signed integer format, the draw buffer must be as // well // Changes with EXT_color_buffer_float: // Case 1) is changed to fixed point OR floating point
GLenum readComponentType = readFormat.info->componentType;
GLenum drawComponentType = drawFormat.info->componentType; bool readFixedPoint = (readComponentType == GL_UNSIGNED_NORMALIZED ||
readComponentType == GL_SIGNED_NORMALIZED); bool drawFixedPoint = (drawComponentType == GL_UNSIGNED_NORMALIZED ||
drawComponentType == GL_SIGNED_NORMALIZED);
if (readFormat.info->isInt() && filter == GL_LINEAR)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kBlitIntegerWithLinearFilter); returnfalse;
}
} // WebGL 2.0 BlitFramebuffer when blitting from a missing attachment // In OpenGL ES it is undefined what happens when an operation tries to blit from a missing // attachment and WebGL defines it to be an error. We do the check unconditionally as the // situation is an application error that would lead to a crash in ANGLE. elseif (drawFramebuffer->hasEnabledDrawBuffer())
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kBlitMissingColor); returnfalse;
}
}
GLenum masks[] = {GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT};
GLenum attachments[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT}; for (size_t i = 0; i < 2; i++)
{ if (mask & masks[i])
{ const FramebufferAttachment *readBuffer =
readFramebuffer->getAttachment(context, attachments[i]); const FramebufferAttachment *drawBuffer =
drawFramebuffer->getAttachment(context, attachments[i]);
if (readBuffer && drawBuffer)
{ if (!Format::EquivalentForBlit(readBuffer->getFormat(), drawBuffer->getFormat()))
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kBlitDepthOrStencilFormatMismatch); returnfalse;
}
if (context->isWebGL() && *readBuffer == *drawBuffer)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kBlitSameImageDepthOrStencil); returnfalse;
}
} // WebGL 2.0 BlitFramebuffer when blitting from a missing attachment elseif (drawBuffer)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kBlitMissingDepthOrStencil); returnfalse;
}
}
}
// OVR_multiview2: // Calling BlitFramebuffer will result in an INVALID_FRAMEBUFFER_OPERATION error if the // current draw framebuffer isMultiview() or the number of // views in the current read framebuffer is more than one. if (readFramebuffer->readDisallowedByMultiview())
{
context->validationError(entryPoint, GL_INVALID_FRAMEBUFFER_OPERATION, kBlitFromMultiview); returnfalse;
} if (drawFramebuffer->isMultiview())
{
context->validationError(entryPoint, GL_INVALID_FRAMEBUFFER_OPERATION, kBlitToMultiview); returnfalse;
}
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.