| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "GLESv2Validate.h" |
| #include <string.h> |
| |
| #define LIST_VALID_TEXFORMATS(f) \ |
| f(GL_DEPTH_COMPONENT) \ |
| f(GL_DEPTH_STENCIL) \ |
| f(GL_RED) \ |
| f(GL_RED_INTEGER) \ |
| f(GL_RG) \ |
| f(GL_RGB) \ |
| f(GL_RGBA) \ |
| f(GL_RGBA_INTEGER) \ |
| f(GL_RGB_INTEGER) \ |
| f(GL_RG_INTEGER) \ |
| |
| #define LIST_VALID_TEXTYPES(f) \ |
| f(GL_BYTE) \ |
| f(GL_FLOAT) \ |
| f(GL_FLOAT_32_UNSIGNED_INT_24_8_REV) \ |
| f(GL_HALF_FLOAT) \ |
| f(GL_HALF_FLOAT_OES) \ |
| f(GL_INT) \ |
| f(GL_SHORT) \ |
| f(GL_UNSIGNED_BYTE) \ |
| f(GL_UNSIGNED_INT) \ |
| f(GL_UNSIGNED_INT_10F_11F_11F_REV) \ |
| f(GL_UNSIGNED_INT_2_10_10_10_REV) \ |
| f(GL_UNSIGNED_INT_24_8) \ |
| f(GL_UNSIGNED_INT_5_9_9_9_REV) \ |
| f(GL_UNSIGNED_SHORT) \ |
| f(GL_UNSIGNED_SHORT_4_4_4_4) \ |
| f(GL_UNSIGNED_SHORT_5_5_5_1) \ |
| f(GL_UNSIGNED_SHORT_5_6_5) \ |
| |
| #define LIST_VALID_TEXFORMAT_COMBINATIONS(f) \ |
| f(GL_BGRA8_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE) \ |
| f(GL_R8, GL_RED, GL_UNSIGNED_BYTE) \ |
| f(GL_R8_SNORM, GL_RED, GL_BYTE) \ |
| f(GL_R16F, GL_RED, GL_FLOAT) \ |
| f(GL_R16F, GL_RED, GL_HALF_FLOAT) \ |
| f(GL_R32F, GL_RED, GL_FLOAT) \ |
| f(GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE) \ |
| f(GL_R8I, GL_RED_INTEGER, GL_BYTE) \ |
| f(GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT) \ |
| f(GL_R16I, GL_RED_INTEGER, GL_SHORT) \ |
| f(GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT) \ |
| f(GL_R32I, GL_RED_INTEGER, GL_INT) \ |
| f(GL_RG8, GL_RG, GL_UNSIGNED_BYTE) \ |
| f(GL_RG8_SNORM, GL_RG, GL_BYTE) \ |
| f(GL_RG16F, GL_RG, GL_HALF_FLOAT) \ |
| f(GL_RG16F, GL_RG, GL_FLOAT) \ |
| f(GL_RG32F, GL_RG, GL_FLOAT) \ |
| f(GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE) \ |
| f(GL_RG8I, GL_RG_INTEGER, GL_BYTE) \ |
| f(GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT) \ |
| f(GL_RG16I, GL_RG_INTEGER, GL_SHORT) \ |
| f(GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT) \ |
| f(GL_RG32I, GL_RG_INTEGER, GL_INT) \ |
| f(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE) \ |
| f(GL_SRGB8, GL_RGB, GL_UNSIGNED_BYTE) \ |
| f(GL_RGB565, GL_RGB, GL_UNSIGNED_BYTE) \ |
| f(GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5) \ |
| f(GL_RGB8_SNORM, GL_RGB, GL_BYTE) \ |
| f(GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV) \ |
| f(GL_R11F_G11F_B10F, GL_RGB, GL_HALF_FLOAT) \ |
| f(GL_R11F_G11F_B10F, GL_RGB, GL_FLOAT) \ |
| f(GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV) \ |
| f(GL_RGB9_E5, GL_RGB, GL_HALF_FLOAT) \ |
| f(GL_RGB9_E5, GL_RGB, GL_FLOAT) \ |
| f(GL_RGB16F, GL_RGB, GL_HALF_FLOAT) \ |
| f(GL_RGB16F, GL_RGB, GL_FLOAT) \ |
| f(GL_RGB32F, GL_RGB, GL_FLOAT) \ |
| f(GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_BYTE) \ |
| f(GL_RGB8I, GL_RGB_INTEGER, GL_BYTE) \ |
| f(GL_RGB16UI, GL_RGB_INTEGER, GL_UNSIGNED_SHORT) \ |
| f(GL_RGB16I, GL_RGB_INTEGER, GL_SHORT) \ |
| f(GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT) \ |
| f(GL_RGB32I, GL_RGB_INTEGER, GL_INT) \ |
| f(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_RGBA8_SNORM, GL_RGBA, GL_BYTE) \ |
| f(GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1) \ |
| f(GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV) \ |
| f(GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4) \ |
| f(GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV) \ |
| f(GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT) \ |
| f(GL_RGBA16F, GL_RGBA, GL_FLOAT) \ |
| f(GL_RGBA32F, GL_RGBA, GL_FLOAT) \ |
| f(GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE) \ |
| f(GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE) \ |
| f(GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV) \ |
| f(GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT) \ |
| f(GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT) \ |
| f(GL_RGBA32I, GL_RGBA_INTEGER, GL_INT) \ |
| f(GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT) \ |
| f(GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT) \ |
| f(GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT) \ |
| f(GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT) \ |
| f(GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT) \ |
| f(GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8) \ |
| f(GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV) \ |
| f(GL_COMPRESSED_R11_EAC, GL_RED, GL_FLOAT) \ |
| f(GL_COMPRESSED_SIGNED_R11_EAC, GL_RED, GL_FLOAT) \ |
| f(GL_COMPRESSED_RG11_EAC, GL_RG, GL_FLOAT) \ |
| f(GL_COMPRESSED_SIGNED_RG11_EAC, GL_RG, GL_FLOAT) \ |
| f(GL_COMPRESSED_RGB8_ETC2, GL_RGB, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ETC2, GL_RGB, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA8_ETC2_EAC, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_5x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_6x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_8x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_10x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| f(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, GL_RGBA, GL_UNSIGNED_BYTE) \ |
| |
| |
| bool GLESv2Validate::renderbufferParam(GLEScontext* ctx, GLenum pname){ |
| int glesMajorVersion = ctx->getMajorVersion(); |
| switch(pname){ |
| case GL_RENDERBUFFER_WIDTH: |
| case GL_RENDERBUFFER_HEIGHT: |
| case GL_RENDERBUFFER_INTERNAL_FORMAT: |
| case GL_RENDERBUFFER_RED_SIZE: |
| case GL_RENDERBUFFER_GREEN_SIZE: |
| case GL_RENDERBUFFER_BLUE_SIZE: |
| case GL_RENDERBUFFER_ALPHA_SIZE: |
| case GL_RENDERBUFFER_DEPTH_SIZE: |
| case GL_RENDERBUFFER_STENCIL_SIZE: |
| return true; |
| case GL_RENDERBUFFER_SAMPLES: |
| return glesMajorVersion >= 3; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::framebufferTarget(GLEScontext* ctx, GLenum target) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| switch (target) { |
| case GL_FRAMEBUFFER: |
| return true; |
| case GL_DRAW_FRAMEBUFFER: |
| case GL_READ_FRAMEBUFFER: |
| return glesMajorVersion >= 3; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::framebufferAttachment(GLEScontext* ctx, GLenum attachment) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| switch (attachment) { |
| case GL_COLOR_ATTACHMENT0: |
| case GL_DEPTH_ATTACHMENT: |
| case GL_STENCIL_ATTACHMENT: |
| return true; |
| case GL_COLOR_ATTACHMENT1: |
| case GL_COLOR_ATTACHMENT2: |
| case GL_COLOR_ATTACHMENT3: |
| case GL_COLOR_ATTACHMENT4: |
| case GL_COLOR_ATTACHMENT5: |
| case GL_COLOR_ATTACHMENT6: |
| case GL_COLOR_ATTACHMENT7: |
| case GL_COLOR_ATTACHMENT8: |
| case GL_COLOR_ATTACHMENT9: |
| case GL_COLOR_ATTACHMENT10: |
| case GL_COLOR_ATTACHMENT11: |
| case GL_COLOR_ATTACHMENT12: |
| case GL_COLOR_ATTACHMENT13: |
| case GL_COLOR_ATTACHMENT14: |
| case GL_COLOR_ATTACHMENT15: |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| return glesMajorVersion >= 3; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::bufferTarget(GLEScontext* ctx, GLenum target) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| int glesMinorVersion = ctx->getMinorVersion(); |
| switch (target) { |
| case GL_ARRAY_BUFFER: // Vertex attributes |
| case GL_ELEMENT_ARRAY_BUFFER: // Vertex array indices |
| return true; |
| // GLES 3.0 buffers |
| case GL_COPY_READ_BUFFER: // Buffer copy source |
| case GL_COPY_WRITE_BUFFER: // Buffer copy destination |
| case GL_PIXEL_PACK_BUFFER: // Pixel read target |
| case GL_PIXEL_UNPACK_BUFFER: // Texture data source |
| case GL_TRANSFORM_FEEDBACK_BUFFER: // Transform feedback buffer |
| case GL_UNIFORM_BUFFER: // Uniform block storage |
| return glesMajorVersion >= 3; |
| // GLES 3.1 buffers |
| case GL_ATOMIC_COUNTER_BUFFER: // Atomic counter storage |
| case GL_DISPATCH_INDIRECT_BUFFER: // Indirect compute dispatch commands |
| case GL_DRAW_INDIRECT_BUFFER: // Indirect command arguments |
| case GL_SHADER_STORAGE_BUFFER: // Read-write storage for shaders |
| return glesMajorVersion >= 3 && glesMinorVersion >= 1; |
| case GL_TEXTURE_BUFFER: |
| return glesMajorVersion >= 3 && |
| ((glesMinorVersion == 1 && ctx->getCaps()->textureBufferAny()) || |
| glesMinorVersion > 1 ); |
| default: |
| return false; |
| } |
| } |
| |
| bool GLESv2Validate::bufferUsage(GLEScontext* ctx, GLenum usage) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| switch(usage) { |
| case GL_STREAM_DRAW: |
| case GL_STATIC_DRAW: |
| case GL_DYNAMIC_DRAW: |
| return true; |
| case GL_STREAM_READ: |
| case GL_STATIC_READ: |
| case GL_DYNAMIC_READ: |
| case GL_STREAM_COPY: |
| case GL_STATIC_COPY: |
| case GL_DYNAMIC_COPY: |
| return glesMajorVersion >= 3; |
| } |
| return false; |
| |
| } |
| |
| |
| bool GLESv2Validate::bufferParam(GLEScontext* ctx, GLenum pname) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| switch (pname) { |
| case GL_BUFFER_SIZE: |
| case GL_BUFFER_USAGE: |
| return true; |
| case GL_BUFFER_ACCESS_FLAGS: |
| case GL_BUFFER_MAPPED: |
| case GL_BUFFER_MAP_LENGTH: |
| case GL_BUFFER_MAP_OFFSET: |
| return glesMajorVersion >= 3; |
| default: |
| return false; |
| } |
| } |
| |
| |
| bool GLESv2Validate::blendEquationMode(GLEScontext* ctx, GLenum mode){ |
| int glesMajorVersion = ctx->getMajorVersion(); |
| switch (mode) { |
| case GL_FUNC_ADD: |
| case GL_FUNC_SUBTRACT: |
| case GL_FUNC_REVERSE_SUBTRACT: |
| return true; |
| case GL_MIN: |
| case GL_MAX: |
| return glesMajorVersion >= 3; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::blendSrc(GLenum s) { |
| switch(s) { |
| case GL_ZERO: |
| case GL_ONE: |
| case GL_SRC_COLOR: |
| case GL_ONE_MINUS_SRC_COLOR: |
| case GL_DST_COLOR: |
| case GL_ONE_MINUS_DST_COLOR: |
| case GL_SRC_ALPHA: |
| case GL_ONE_MINUS_SRC_ALPHA: |
| case GL_DST_ALPHA: |
| case GL_ONE_MINUS_DST_ALPHA: |
| case GL_CONSTANT_COLOR: |
| case GL_ONE_MINUS_CONSTANT_COLOR: |
| case GL_CONSTANT_ALPHA: |
| case GL_ONE_MINUS_CONSTANT_ALPHA: |
| case GL_SRC_ALPHA_SATURATE: |
| return true; |
| } |
| return false; |
| } |
| |
| |
| bool GLESv2Validate::blendDst(GLenum d) { |
| switch(d) { |
| case GL_ZERO: |
| case GL_ONE: |
| case GL_SRC_COLOR: |
| case GL_ONE_MINUS_SRC_COLOR: |
| case GL_DST_COLOR: |
| case GL_ONE_MINUS_DST_COLOR: |
| case GL_SRC_ALPHA: |
| case GL_ONE_MINUS_SRC_ALPHA: |
| case GL_DST_ALPHA: |
| case GL_ONE_MINUS_DST_ALPHA: |
| case GL_CONSTANT_COLOR: |
| case GL_ONE_MINUS_CONSTANT_COLOR: |
| case GL_CONSTANT_ALPHA: |
| case GL_ONE_MINUS_CONSTANT_ALPHA: |
| case GL_SRC_ALPHA_SATURATE: |
| return true; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::textureTarget(GLEScontext* ctx, GLenum target) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| int glesMinorVersion = ctx->getMinorVersion(); |
| switch (target) { |
| case GL_TEXTURE_2D: |
| case GL_TEXTURE_CUBE_MAP: |
| return true; |
| case GL_TEXTURE_2D_ARRAY: |
| case GL_TEXTURE_3D: |
| return glesMajorVersion >= 3; |
| case GL_TEXTURE_2D_MULTISAMPLE: |
| return glesMajorVersion >= 3 && glesMinorVersion >= 1; |
| case GL_TEXTURE_BUFFER: |
| return glesMajorVersion >= 3 && ( glesMinorVersion > 1 || |
| (glesMinorVersion == 1 && ctx->getCaps()->textureBufferAny())); |
| default: |
| return false; |
| } |
| } |
| |
| bool GLESv2Validate::textureParams(GLEScontext* ctx, GLenum param) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| int glesMinorVersion = ctx->getMinorVersion(); |
| switch(param) { |
| case GL_TEXTURE_MIN_FILTER: |
| case GL_TEXTURE_MAG_FILTER: |
| case GL_TEXTURE_WRAP_S: |
| case GL_TEXTURE_WRAP_T: |
| case GL_TEXTURE_MAX_ANISOTROPY_EXT: |
| return true; |
| case GL_TEXTURE_SWIZZLE_R: |
| case GL_TEXTURE_SWIZZLE_G: |
| case GL_TEXTURE_SWIZZLE_B: |
| case GL_TEXTURE_SWIZZLE_A: |
| case GL_TEXTURE_MIN_LOD: |
| case GL_TEXTURE_MAX_LOD: |
| case GL_TEXTURE_BASE_LEVEL: |
| case GL_TEXTURE_MAX_LEVEL: |
| case GL_TEXTURE_COMPARE_MODE: |
| case GL_TEXTURE_COMPARE_FUNC: |
| case GL_TEXTURE_WRAP_R: |
| case GL_TEXTURE_IMMUTABLE_FORMAT: |
| case GL_TEXTURE_IMMUTABLE_LEVELS: |
| return glesMajorVersion >= 3; |
| case GL_DEPTH_STENCIL_TEXTURE_MODE: |
| return glesMajorVersion >= 3 && glesMinorVersion >= 1; |
| default: |
| return false; |
| } |
| } |
| |
| bool GLESv2Validate::hintTargetMode(GLenum target,GLenum mode){ |
| |
| switch(mode) { |
| case GL_FASTEST: |
| case GL_NICEST: |
| case GL_DONT_CARE: |
| break; |
| default: return false; |
| } |
| return target == GL_GENERATE_MIPMAP_HINT || target == GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES; |
| } |
| |
| bool GLESv2Validate::capability(GLenum cap){ |
| switch(cap){ |
| case GL_BLEND: |
| case GL_CULL_FACE: |
| case GL_DEPTH_TEST: |
| case GL_DITHER: |
| case GL_POLYGON_OFFSET_FILL: |
| case GL_SAMPLE_ALPHA_TO_COVERAGE: |
| case GL_SAMPLE_COVERAGE: |
| case GL_SCISSOR_TEST: |
| case GL_STENCIL_TEST: |
| return true; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::pixelStoreParam(GLEScontext* ctx, GLenum param){ |
| int glesMajorVersion = ctx->getMajorVersion(); |
| switch(param) { |
| case GL_PACK_ALIGNMENT: |
| case GL_UNPACK_ALIGNMENT: |
| return true; |
| case GL_PACK_ROW_LENGTH: |
| case GL_PACK_SKIP_PIXELS: |
| case GL_PACK_SKIP_ROWS: |
| case GL_UNPACK_ROW_LENGTH: |
| case GL_UNPACK_IMAGE_HEIGHT: |
| case GL_UNPACK_SKIP_PIXELS: |
| case GL_UNPACK_SKIP_ROWS: |
| case GL_UNPACK_SKIP_IMAGES: |
| return glesMajorVersion >= 3; |
| default: |
| return false; |
| } |
| } |
| |
| bool GLESv2Validate::readPixelFrmt(GLenum format){ |
| switch(format) { |
| case GL_ALPHA: |
| case GL_LUMINANCE_ALPHA: |
| case GL_RGB: |
| case GL_RGBA: |
| return true; |
| } |
| return false; |
| } |
| |
| |
| bool GLESv2Validate::shaderType(GLEScontext* ctx, GLenum type){ |
| int glesMajorVersion = ctx->getMajorVersion(); |
| int glesMinorVersion = ctx->getMinorVersion(); |
| switch (type) { |
| case GL_VERTEX_SHADER: |
| case GL_FRAGMENT_SHADER: |
| return true; |
| case GL_COMPUTE_SHADER: |
| return glesMajorVersion >= 3 && glesMinorVersion >= 1; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::precisionType(GLenum type){ |
| switch(type){ |
| case GL_LOW_FLOAT: |
| case GL_MEDIUM_FLOAT: |
| case GL_HIGH_FLOAT: |
| case GL_LOW_INT: |
| case GL_MEDIUM_INT: |
| case GL_HIGH_INT: |
| return true; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::arrayIndex(GLEScontext * ctx,GLuint index) { |
| return index < (GLuint)ctx->getCaps()->maxVertexAttribs; |
| } |
| |
| #define GL_RED 0x1903 |
| #define GL_RG 0x8227 |
| #define GL_R8 0x8229 |
| #define GL_RG8 0x822B |
| #define GL_R16F 0x822D |
| #define GL_RG16F 0x822F |
| #define GL_RGBA16F 0x881A |
| #define GL_RGB16F 0x881B |
| #define GL_R11F_G11F_B10F 0x8C3A |
| #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B |
| |
| bool GLESv2Validate::pixelType(GLEScontext * ctx,GLenum type) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| if (glesMajorVersion < 3) { |
| if(type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT |
| || type == GL_UNSIGNED_INT_10F_11F_11F_REV) |
| return true; |
| |
| return GLESvalidate::pixelType(ctx, type); |
| } |
| |
| #define GLES3_TYPE_CASE(type) \ |
| case type: \ |
| |
| switch (type) { |
| LIST_VALID_TEXTYPES(GLES3_TYPE_CASE) |
| return glesMajorVersion >= 3; |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| bool GLESv2Validate::pixelFrmt(GLEScontext* ctx,GLenum format) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| if (glesMajorVersion < 3) { |
| switch (format) { |
| case GL_DEPTH_COMPONENT: |
| // GLES3 compatible |
| // Required in dEQP |
| case GL_RED: |
| case GL_RG: |
| return true; |
| } |
| return GLESvalidate::pixelFrmt(ctx, format); |
| } |
| |
| #define GLES3_FORMAT_CASE(format) \ |
| case format: |
| |
| switch (format) { |
| LIST_VALID_TEXFORMATS(GLES3_FORMAT_CASE) |
| return glesMajorVersion >= 3; |
| default: |
| break; |
| } |
| return GLESvalidate::pixelFrmt(ctx, format); |
| } |
| |
| bool GLESv2Validate::pixelItnlFrmt(GLEScontext* ctx ,GLenum internalformat) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| switch (internalformat) { |
| case GL_R8: |
| case GL_RG8: |
| case GL_R16F: |
| case GL_RG16F: |
| case GL_RGBA16F: |
| case GL_RGB16F: |
| case GL_R11F_G11F_B10F: |
| case GL_RGB8: |
| case GL_RGBA8: |
| return true; |
| case GL_R8_SNORM: |
| case GL_R32F: |
| case GL_R8UI: |
| case GL_R8I: |
| case GL_R16UI: |
| case GL_R16I: |
| case GL_R32UI: |
| case GL_R32I: |
| case GL_RG8_SNORM: |
| case GL_RG32F: |
| case GL_RG8UI: |
| case GL_RG8I: |
| case GL_RG16UI: |
| case GL_RG16I: |
| case GL_RG32UI: |
| case GL_RG32I: |
| case GL_SRGB8: |
| case GL_RGB565: |
| case GL_RGB8_SNORM: |
| case GL_RGB9_E5: |
| case GL_RGB32F: |
| case GL_RGB8UI: |
| case GL_RGB8I: |
| case GL_RGB16UI: |
| case GL_RGB16I: |
| case GL_RGB32UI: |
| case GL_RGB32I: |
| case GL_SRGB8_ALPHA8: |
| case GL_RGBA8_SNORM: |
| case GL_RGB5_A1: |
| case GL_RGBA4: |
| case GL_RGB10_A2: |
| case GL_RGBA32F: |
| case GL_RGBA8UI: |
| case GL_RGBA8I: |
| case GL_RGB10_A2UI: |
| case GL_RGBA16UI: |
| case GL_RGBA16I: |
| case GL_RGBA32I: |
| case GL_RGBA32UI: |
| case GL_DEPTH_COMPONENT16: |
| case GL_DEPTH_COMPONENT24: |
| case GL_DEPTH_COMPONENT32F: |
| case GL_DEPTH24_STENCIL8: |
| case GL_DEPTH32F_STENCIL8: |
| case GL_COMPRESSED_R11_EAC: |
| case GL_COMPRESSED_SIGNED_R11_EAC: |
| case GL_COMPRESSED_RG11_EAC: |
| case GL_COMPRESSED_SIGNED_RG11_EAC: |
| case GL_COMPRESSED_RGB8_ETC2: |
| case GL_COMPRESSED_SRGB8_ETC2: |
| case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: |
| case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: |
| case GL_COMPRESSED_RGBA8_ETC2_EAC: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: |
| case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: |
| if (glesMajorVersion >= 3) { |
| return true; |
| } |
| break; |
| } |
| return pixelFrmt(ctx, internalformat); |
| } |
| |
| bool GLESv2Validate::pixelSizedFrmt(GLEScontext* ctx, GLenum internalformat, |
| GLenum format, GLenum type) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| if (internalformat == format) { |
| return true; |
| } |
| |
| if (glesMajorVersion < 3) { |
| switch (format) { |
| case GL_RED: |
| switch (type) { |
| case GL_UNSIGNED_BYTE: |
| return internalformat == GL_R8; |
| case GL_HALF_FLOAT: |
| case GL_FLOAT: |
| return internalformat == GL_R16F; |
| case GL_BYTE: |
| return internalformat == GL_R8_SNORM; |
| default: |
| return false; |
| } |
| break; |
| case GL_RG: |
| switch (type) { |
| case GL_UNSIGNED_BYTE: |
| return internalformat == GL_RG8; |
| case GL_HALF_FLOAT: |
| case GL_FLOAT: |
| return internalformat == GL_RG16F; |
| default: |
| return false; |
| } |
| break; |
| case GL_RGB: |
| switch (type) { |
| case GL_HALF_FLOAT: |
| case GL_FLOAT: |
| return internalformat == GL_RGB16F |
| || internalformat == GL_R11F_G11F_B10F; |
| case GL_UNSIGNED_INT_10F_11F_11F_REV: |
| return internalformat == GL_R11F_G11F_B10F; |
| default: |
| return internalformat == GL_RGB8 || |
| internalformat == GL_RGB; |
| } |
| break; |
| case GL_RGBA: |
| switch (type) { |
| case GL_HALF_FLOAT: |
| case GL_FLOAT: |
| return internalformat == GL_RGBA16F; |
| default: |
| return internalformat == GL_RGBA8 || |
| internalformat == GL_RGBA; |
| } |
| break; |
| } |
| } |
| |
| #define VALIDATE_FORMAT_COMBINATION(x, y, z) \ |
| if (internalformat == x && format == y && type == z) return true; \ |
| |
| LIST_VALID_TEXFORMAT_COMBINATIONS(VALIDATE_FORMAT_COMBINATION) |
| |
| return false; |
| } |
| |
| bool GLESv2Validate::isCompressedFormat(GLenum format) { |
| switch (format) { |
| case GL_ETC1_RGB8_OES: |
| case GL_COMPRESSED_R11_EAC: |
| case GL_COMPRESSED_SIGNED_R11_EAC: |
| case GL_COMPRESSED_RG11_EAC: |
| case GL_COMPRESSED_SIGNED_RG11_EAC: |
| case GL_COMPRESSED_RGB8_ETC2: |
| case GL_COMPRESSED_SRGB8_ETC2: |
| case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: |
| case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: |
| case GL_COMPRESSED_RGBA8_ETC2_EAC: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: |
| case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: |
| case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: |
| 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: |
| case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: |
| 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_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: |
| case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: |
| 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: |
| return true; |
| } |
| return false; |
| } |
| |
| void GLESv2Validate::getCompatibleFormatTypeForInternalFormat(GLenum internalformat, GLenum* format_out, GLenum* type_out) { |
| #define RETURN_COMPATIBLE_FORMAT(x, y, z) \ |
| if (internalformat == x) { \ |
| *format_out = y; \ |
| *type_out = z; \ |
| return; \ |
| } \ |
| |
| LIST_VALID_TEXFORMAT_COMBINATIONS(RETURN_COMPATIBLE_FORMAT) |
| } |
| bool GLESv2Validate::attribName(const GLchar* name){ |
| const GLchar* found = strstr(name,"gl_"); |
| return (!found) || |
| (found != name) ; // attrib name does not start with gl_ |
| } |
| |
| bool GLESv2Validate::attribIndex(int index, int max){ |
| return index >=0 && index < max; |
| } |
| |
| bool GLESv2Validate::programParam(GLEScontext* ctx, GLenum pname){ |
| int glesMajorVersion = ctx->getMajorVersion(); |
| int glesMinorVersion = ctx->getMinorVersion(); |
| switch(pname){ |
| case GL_DELETE_STATUS: |
| case GL_LINK_STATUS: |
| case GL_VALIDATE_STATUS: |
| case GL_INFO_LOG_LENGTH: |
| case GL_ATTACHED_SHADERS: |
| case GL_ACTIVE_ATTRIBUTES: |
| case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: |
| case GL_ACTIVE_UNIFORMS: |
| case GL_ACTIVE_UNIFORM_MAX_LENGTH: |
| return true; |
| case GL_ACTIVE_UNIFORM_BLOCKS: |
| case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: |
| case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: |
| case GL_PROGRAM_BINARY_LENGTH: |
| case GL_TRANSFORM_FEEDBACK_BUFFER_MODE: |
| case GL_TRANSFORM_FEEDBACK_VARYINGS: |
| case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: |
| case GL_PROGRAM_SEPARABLE: |
| return glesMajorVersion >= 3; |
| case GL_ACTIVE_ATOMIC_COUNTER_BUFFERS: |
| case GL_COMPUTE_WORK_GROUP_SIZE: |
| return glesMajorVersion >= 3 && glesMinorVersion >= 1; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::textureIsCubeMap(GLenum target){ |
| switch(target){ |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_X: |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: |
| return true; |
| } |
| return false; |
| } |
| |
| bool GLESv2Validate::textureTargetEx(GLEScontext* ctx, GLenum textarget) { |
| int glesMajorVersion = ctx->getMajorVersion(); |
| int glesMinorVersion = ctx->getMinorVersion(); |
| switch (textarget) { |
| case GL_TEXTURE_2D_MULTISAMPLE: |
| return glesMajorVersion >= 3 && glesMinorVersion >= 1; |
| default: |
| return GLESvalidate::textureTargetEx(textarget); |
| } |
| } |
| |
| int GLESv2Validate::sizeOfType(GLenum type) { |
| size_t retval = 0; |
| switch(type) { |
| case GL_BYTE: |
| case GL_UNSIGNED_BYTE: |
| retval = 1; |
| break; |
| case GL_SHORT: |
| case GL_UNSIGNED_SHORT: |
| case GL_HALF_FLOAT: |
| case GL_HALF_FLOAT_OES: |
| retval = 2; |
| break; |
| case GL_UNSIGNED_INT: |
| case GL_INT: |
| case GL_FLOAT: |
| case GL_FIXED: |
| case GL_BOOL: |
| retval = 4; |
| break; |
| #ifdef GL_DOUBLE |
| case GL_DOUBLE: |
| retval = 8; |
| break; |
| #endif |
| case GL_FLOAT_VEC2: |
| case GL_INT_VEC2: |
| case GL_UNSIGNED_INT_VEC2: |
| case GL_BOOL_VEC2: |
| retval = 8; |
| break; |
| case GL_INT_VEC3: |
| case GL_UNSIGNED_INT_VEC3: |
| case GL_BOOL_VEC3: |
| case GL_FLOAT_VEC3: |
| retval = 12; |
| break; |
| case GL_FLOAT_VEC4: |
| case GL_BOOL_VEC4: |
| case GL_INT_VEC4: |
| case GL_UNSIGNED_INT_VEC4: |
| case GL_FLOAT_MAT2: |
| retval = 16; |
| break; |
| case GL_FLOAT_MAT3: |
| retval = 36; |
| break; |
| case GL_FLOAT_MAT4: |
| retval = 64; |
| break; |
| case GL_FLOAT_MAT2x3: |
| case GL_FLOAT_MAT3x2: |
| retval = 4 * 6; |
| break; |
| case GL_FLOAT_MAT2x4: |
| case GL_FLOAT_MAT4x2: |
| retval = 4 * 8; |
| break; |
| case GL_FLOAT_MAT3x4: |
| case GL_FLOAT_MAT4x3: |
| retval = 4 * 12; |
| break; |
| case GL_SAMPLER_2D: |
| case GL_SAMPLER_CUBE: |
| retval = 4; |
| break; |
| case GL_UNSIGNED_SHORT_4_4_4_4: |
| case GL_UNSIGNED_SHORT_5_5_5_1: |
| case GL_UNSIGNED_SHORT_5_6_5: |
| case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT: |
| case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT: |
| retval = 2; |
| break; |
| case GL_INT_2_10_10_10_REV: |
| case GL_UNSIGNED_INT_10F_11F_11F_REV: |
| case GL_UNSIGNED_INT_5_9_9_9_REV: |
| case GL_UNSIGNED_INT_2_10_10_10_REV: |
| case GL_UNSIGNED_INT_24_8_OES:; |
| retval = 4; |
| break; |
| case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: |
| retval = 4 + 4; |
| break; |
| default: |
| fprintf(stderr, "%s: WARNING: unknown type 0x%x. assuming 32 bits.\n", __FUNCTION__, type); |
| retval = 4; |
| } |
| return retval; |
| } |
| |