diff options
Diffstat (limited to '')
69 files changed, 23987 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/js/desktop-gl-constants.js b/dom/canvas/test/webgl-conf/checkout/js/desktop-gl-constants.js new file mode 100644 index 0000000000..4d4833c2ce --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/desktop-gl-constants.js @@ -0,0 +1,2639 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// Defines a bunch of DesktopGL constants so we can make sure WebGL +// implementations disallow them. + +// Some of these are also GLES2/GLES3 constants that have been removed from +// WebGL. + +var desktopGL = { + 'MIN': 0x8007, + 'MAX': 0x8008, + 'QUADS': 0x0007, + 'QUAD_STRIP': 0x0008, + 'POLYGON': 0x0009, + '1': 0x0001, + '2': 0x0002, + '3': 0x0003, + '4': 0x0004, + 'STATIC_READ': 0x88E5, + 'CLIP_PLANE0': 0x3000, + 'PERSPECTIVE_CORRECTION_HINT': 0x0C50, + 'ACCUM_BUFFER_BIT': 0x0200, + 'RGB4': 0x804F, + 'RGB5': 0x8050, + 'RGB8': 0x8051, + 'RGB10': 0x8052, + 'RGB12': 0x8053, + 'RGB16': 0x8054, + 'RGBA2': 0x8055, + 'RGBA4': 0x8056, + 'RGB5_A1': 0x8057, + 'RGBA8': 0x8058, + 'RGB10_A2': 0x8059, + 'RGBA12': 0x805A, + 'RGBA16': 0x805B, + 'SMOOTH_POINT_SIZE_RANGE': 0x0B12, + 'SMOOTH_POINT_SIZE_GRANULARITY': 0x0B13, + 'SMOOTH_LINE_WIDTH_RANGE': 0x0B22, + 'SMOOTH_LINE_WIDTH_GRANULARITY': 0x0B23, + 'UNSIGNED_BYTE_3_3_2': 0x8032, + 'UNSIGNED_SHORT_4_4_4_4': 0x8033, + 'UNSIGNED_SHORT_5_5_5_1': 0x8034, + 'UNSIGNED_INT_8_8_8_8': 0x8035, + 'UNSIGNED_INT_10_10_10_2': 0x8036, + 'RESCALE_NORMAL': 0x803A, + 'TEXTURE_BINDING_3D': 0x806A, + 'PACK_SKIP_IMAGES': 0x806B, + 'PACK_IMAGE_HEIGHT': 0x806C, + 'UNPACK_SKIP_IMAGES': 0x806D, + 'UNPACK_IMAGE_HEIGHT': 0x806E, + 'TEXTURE_3D': 0x806F, + 'PROXY_TEXTURE_3D': 0x8070, + 'TEXTURE_DEPTH': 0x8071, + 'TEXTURE_WRAP_R': 0x8072, + 'MAX_3D_TEXTURE_SIZE': 0x8073, + 'BGR': 0x80E0, + 'BGRA': 0x80E1, + 'MAX_ELEMENTS_VERTICES': 0x80E8, + 'MAX_ELEMENTS_INDICES': 0x80E9, + 'CLAMP_TO_EDGE': 0x812F, + 'TEXTURE_MIN_LOD': 0x813A, + 'TEXTURE_MAX_LOD': 0x813B, + 'TEXTURE_BASE_LEVEL': 0x813C, + 'TEXTURE_MAX_LEVEL': 0x813D, + 'LIGHT_MODEL_COLOR_CONTROL': 0x81F8, + 'SINGLE_COLOR': 0x81F9, + 'SEPARATE_SPECULAR_COLOR': 0x81FA, + 'UNSIGNED_BYTE_2_3_3_REV': 0x8362, + 'UNSIGNED_SHORT_5_6_5': 0x8363, + 'UNSIGNED_SHORT_5_6_5_REV': 0x8364, + 'UNSIGNED_SHORT_4_4_4_4_REV': 0x8365, + 'UNSIGNED_SHORT_1_5_5_5_REV': 0x8366, + 'UNSIGNED_INT_8_8_8_8_REV': 0x8367, + 'UNSIGNED_INT_2_10_10_10_REV': 0x8368, + 'ALIASED_POINT_SIZE_RANGE': 0x846D, + 'ALIASED_LINE_WIDTH_RANGE': 0x846E, + 'MULTISAMPLE': 0x809D, + 'SAMPLE_ALPHA_TO_COVERAGE': 0x809E, + 'SAMPLE_ALPHA_TO_ONE': 0x809F, + 'SAMPLE_COVERAGE': 0x80A0, + 'SAMPLE_BUFFERS': 0x80A8, + 'SAMPLES': 0x80A9, + 'SAMPLE_COVERAGE_VALUE': 0x80AA, + 'SAMPLE_COVERAGE_INVERT': 0x80AB, + 'CLAMP_TO_BORDER': 0x812D, + 'TEXTURE0': 0x84C0, + 'TEXTURE1': 0x84C1, + 'TEXTURE2': 0x84C2, + 'TEXTURE3': 0x84C3, + 'TEXTURE4': 0x84C4, + 'TEXTURE5': 0x84C5, + 'TEXTURE6': 0x84C6, + 'TEXTURE7': 0x84C7, + 'TEXTURE8': 0x84C8, + 'TEXTURE9': 0x84C9, + 'TEXTURE10': 0x84CA, + 'TEXTURE11': 0x84CB, + 'TEXTURE12': 0x84CC, + 'TEXTURE13': 0x84CD, + 'TEXTURE14': 0x84CE, + 'TEXTURE15': 0x84CF, + 'TEXTURE16': 0x84D0, + 'TEXTURE17': 0x84D1, + 'TEXTURE18': 0x84D2, + 'TEXTURE19': 0x84D3, + 'TEXTURE20': 0x84D4, + 'TEXTURE21': 0x84D5, + 'TEXTURE22': 0x84D6, + 'TEXTURE23': 0x84D7, + 'TEXTURE24': 0x84D8, + 'TEXTURE25': 0x84D9, + 'TEXTURE26': 0x84DA, + 'TEXTURE27': 0x84DB, + 'TEXTURE28': 0x84DC, + 'TEXTURE29': 0x84DD, + 'TEXTURE30': 0x84DE, + 'TEXTURE31': 0x84DF, + 'ACTIVE_TEXTURE': 0x84E0, + 'CLIENT_ACTIVE_TEXTURE': 0x84E1, + 'MAX_TEXTURE_UNITS': 0x84E2, + 'TRANSPOSE_MODELVIEW_MATRIX': 0x84E3, + 'TRANSPOSE_PROJECTION_MATRIX': 0x84E4, + 'TRANSPOSE_TEXTURE_MATRIX': 0x84E5, + 'TRANSPOSE_COLOR_MATRIX': 0x84E6, + 'SUBTRACT': 0x84E7, + 'COMPRESSED_ALPHA': 0x84E9, + 'COMPRESSED_LUMINANCE': 0x84EA, + 'COMPRESSED_LUMINANCE_ALPHA': 0x84EB, + 'COMPRESSED_INTENSITY': 0x84EC, + 'COMPRESSED_RGB': 0x84ED, + 'COMPRESSED_RGBA': 0x84EE, + 'TEXTURE_COMPRESSION_HINT': 0x84EF, + 'NORMAL_MAP': 0x8511, + 'REFLECTION_MAP': 0x8512, + 'TEXTURE_CUBE_MAP': 0x8513, + 'TEXTURE_BINDING_CUBE_MAP': 0x8514, + 'TEXTURE_CUBE_MAP_POSITIVE_X': 0x8515, + 'TEXTURE_CUBE_MAP_NEGATIVE_X': 0x8516, + 'TEXTURE_CUBE_MAP_POSITIVE_Y': 0x8517, + 'TEXTURE_CUBE_MAP_NEGATIVE_Y': 0x8518, + 'TEXTURE_CUBE_MAP_POSITIVE_Z': 0x8519, + 'TEXTURE_CUBE_MAP_NEGATIVE_Z': 0x851A, + 'PROXY_TEXTURE_CUBE_MAP': 0x851B, + 'MAX_CUBE_MAP_TEXTURE_SIZE': 0x851C, + 'COMBINE': 0x8570, + 'COMBINE_RGB': 0x8571, + 'COMBINE_ALPHA': 0x8572, + 'RGB_SCALE': 0x8573, + 'ADD_SIGNED': 0x8574, + 'INTERPOLATE': 0x8575, + 'CONSTANT': 0x8576, + 'PRIMARY_COLOR': 0x8577, + 'PREVIOUS': 0x8578, + 'SOURCE0_RGB': 0x8580, + 'SOURCE1_RGB': 0x8581, + 'SOURCE2_RGB': 0x8582, + 'SOURCE0_ALPHA': 0x8588, + 'SOURCE1_ALPHA': 0x8589, + 'SOURCE2_ALPHA': 0x858A, + 'OPERAND0_RGB': 0x8590, + 'OPERAND1_RGB': 0x8591, + 'OPERAND2_RGB': 0x8592, + 'OPERAND0_ALPHA': 0x8598, + 'OPERAND1_ALPHA': 0x8599, + 'OPERAND2_ALPHA': 0x859A, + 'TEXTURE_COMPRESSED_IMAGE_SIZE': 0x86A0, + 'TEXTURE_COMPRESSED': 0x86A1, + 'NUM_COMPRESSED_TEXTURE_FORMATS': 0x86A2, + 'COMPRESSED_TEXTURE_FORMATS': 0x86A3, + 'DOT3_RGB': 0x86AE, + 'DOT3_RGBA': 0x86AF, + 'MULTISAMPLE_BIT': 0x20000000, + 'BLEND_DST_RGB': 0x80C8, + 'BLEND_SRC_RGB': 0x80C9, + 'BLEND_DST_ALPHA': 0x80CA, + 'BLEND_SRC_ALPHA': 0x80CB, + 'POINT_SIZE_MIN': 0x8126, + 'POINT_SIZE_MAX': 0x8127, + 'POINT_FADE_THRESHOLD_SIZE': 0x8128, + 'POINT_DISTANCE_ATTENUATION': 0x8129, + 'GENERATE_MIPMAP': 0x8191, + 'GENERATE_MIPMAP_HINT': 0x8192, + 'DEPTH_COMPONENT16': 0x81A5, + 'DEPTH_COMPONENT24': 0x81A6, + 'DEPTH_COMPONENT32': 0x81A7, + 'MIRRORED_REPEAT': 0x8370, + 'FOG_COORDINATE_SOURCE': 0x8450, + 'FOG_COORDINATE': 0x8451, + 'FRAGMENT_DEPTH': 0x8452, + 'CURRENT_FOG_COORDINATE': 0x8453, + 'FOG_COORDINATE_ARRAY_TYPE': 0x8454, + 'FOG_COORDINATE_ARRAY_STRIDE': 0x8455, + 'FOG_COORDINATE_ARRAY_POINTER': 0x8456, + 'FOG_COORDINATE_ARRAY': 0x8457, + 'COLOR_SUM': 0x8458, + 'CURRENT_SECONDARY_COLOR': 0x8459, + 'SECONDARY_COLOR_ARRAY_SIZE': 0x845A, + 'SECONDARY_COLOR_ARRAY_TYPE': 0x845B, + 'SECONDARY_COLOR_ARRAY_STRIDE': 0x845C, + 'SECONDARY_COLOR_ARRAY_POINTER': 0x845D, + 'SECONDARY_COLOR_ARRAY': 0x845E, + 'MAX_TEXTURE_LOD_BIAS': 0x84FD, + 'TEXTURE_FILTER_CONTROL': 0x8500, + 'TEXTURE_LOD_BIAS': 0x8501, + 'INCR_WRAP': 0x8507, + 'DECR_WRAP': 0x8508, + 'TEXTURE_DEPTH_SIZE': 0x884A, + 'DEPTH_TEXTURE_MODE': 0x884B, + 'TEXTURE_COMPARE_MODE': 0x884C, + 'TEXTURE_COMPARE_FUNC': 0x884D, + 'COMPARE_R_TO_TEXTURE': 0x884E, + 'BUFFER_SIZE': 0x8764, + 'BUFFER_USAGE': 0x8765, + 'QUERY_COUNTER_BITS': 0x8864, + 'CURRENT_QUERY': 0x8865, + 'QUERY_RESULT': 0x8866, + 'QUERY_RESULT_AVAILABLE': 0x8867, + 'ARRAY_BUFFER': 0x8892, + 'ELEMENT_ARRAY_BUFFER': 0x8893, + 'ARRAY_BUFFER_BINDING': 0x8894, + 'ELEMENT_ARRAY_BUFFER_BINDING': 0x8895, + 'VERTEX_ARRAY_BUFFER_BINDING': 0x8896, + 'NORMAL_ARRAY_BUFFER_BINDING': 0x8897, + 'COLOR_ARRAY_BUFFER_BINDING': 0x8898, + 'INDEX_ARRAY_BUFFER_BINDING': 0x8899, + 'TEXTURE_COORD_ARRAY_BUFFER_BINDING': 0x889A, + 'EDGE_FLAG_ARRAY_BUFFER_BINDING': 0x889B, + 'SECONDARY_COLOR_ARRAY_BUFFER_BINDING': 0x889C, + 'FOG_COORDINATE_ARRAY_BUFFER_BINDING': 0x889D, + 'WEIGHT_ARRAY_BUFFER_BINDING': 0x889E, + 'VERTEX_ATTRIB_ARRAY_BUFFER_BINDING': 0x889F, + 'READ_ONLY': 0x88B8, + 'WRITE_ONLY': 0x88B9, + 'READ_WRITE': 0x88BA, + 'BUFFER_ACCESS': 0x88BB, + 'BUFFER_MAPPED': 0x88BC, + 'BUFFER_MAP_POINTER': 0x88BD, + 'STREAM_DRAW': 0x88E0, + 'STREAM_READ': 0x88E1, + 'STREAM_COPY': 0x88E2, + 'STATIC_DRAW': 0x88E4, + 'STATIC_READ': 0x88E5, + 'STATIC_COPY': 0x88E6, + 'DYNAMIC_DRAW': 0x88E8, + 'DYNAMIC_READ': 0x88E9, + 'DYNAMIC_COPY': 0x88EA, + 'SAMPLES_PASSED': 0x8914, + 'VERTEX_ATTRIB_ARRAY_ENABLED': 0x8622, + 'VERTEX_ATTRIB_ARRAY_SIZE': 0x8623, + 'VERTEX_ATTRIB_ARRAY_STRIDE': 0x8624, + 'VERTEX_ATTRIB_ARRAY_TYPE': 0x8625, + 'CURRENT_VERTEX_ATTRIB': 0x8626, + 'VERTEX_PROGRAM_POINT_SIZE': 0x8642, + 'VERTEX_PROGRAM_TWO_SIDE': 0x8643, + 'VERTEX_ATTRIB_ARRAY_POINTER': 0x8645, + 'STENCIL_BACK_FUNC': 0x8800, + 'STENCIL_BACK_FAIL': 0x8801, + 'STENCIL_BACK_PASS_DEPTH_FAIL': 0x8802, + 'STENCIL_BACK_PASS_DEPTH_PASS': 0x8803, + 'MAX_DRAW_BUFFERS': 0x8824, + 'DRAW_BUFFER0': 0x8825, + 'DRAW_BUFFER1': 0x8826, + 'DRAW_BUFFER2': 0x8827, + 'DRAW_BUFFER3': 0x8828, + 'DRAW_BUFFER4': 0x8829, + 'DRAW_BUFFER5': 0x882A, + 'DRAW_BUFFER6': 0x882B, + 'DRAW_BUFFER7': 0x882C, + 'DRAW_BUFFER8': 0x882D, + 'DRAW_BUFFER9': 0x882E, + 'DRAW_BUFFER10': 0x882F, + 'DRAW_BUFFER11': 0x8830, + 'DRAW_BUFFER12': 0x8831, + 'DRAW_BUFFER13': 0x8832, + 'DRAW_BUFFER14': 0x8833, + 'DRAW_BUFFER15': 0x8834, + 'BLEND_EQUATION_ALPHA': 0x883D, + 'POINT_SPRITE': 0x8861, + 'COORD_REPLACE': 0x8862, + 'MAX_VERTEX_ATTRIBS': 0x8869, + 'VERTEX_ATTRIB_ARRAY_NORMALIZED': 0x886A, + 'MAX_TEXTURE_COORDS': 0x8871, + 'MAX_TEXTURE_IMAGE_UNITS': 0x8872, + 'FRAGMENT_SHADER': 0x8B30, + 'VERTEX_SHADER': 0x8B31, + 'MAX_FRAGMENT_UNIFORM_COMPONENTS': 0x8B49, + 'MAX_VERTEX_UNIFORM_COMPONENTS': 0x8B4A, + 'MAX_VARYING_FLOATS': 0x8B4B, + 'MAX_VERTEX_TEXTURE_IMAGE_UNITS': 0x8B4C, + 'MAX_COMBINED_TEXTURE_IMAGE_UNITS': 0x8B4D, + 'SHADER_TYPE': 0x8B4F, + 'FLOAT_VEC2': 0x8B50, + 'FLOAT_VEC3': 0x8B51, + 'FLOAT_VEC4': 0x8B52, + 'INT_VEC2': 0x8B53, + 'INT_VEC3': 0x8B54, + 'INT_VEC4': 0x8B55, + 'BOOL': 0x8B56, + 'BOOL_VEC2': 0x8B57, + 'BOOL_VEC3': 0x8B58, + 'BOOL_VEC4': 0x8B59, + 'FLOAT_MAT2': 0x8B5A, + 'FLOAT_MAT3': 0x8B5B, + 'FLOAT_MAT4': 0x8B5C, + 'SAMPLER_1D': 0x8B5D, + 'SAMPLER_2D': 0x8B5E, + 'SAMPLER_3D': 0x8B5F, + 'SAMPLER_CUBE': 0x8B60, + 'SAMPLER_1D_SHADOW': 0x8B61, + 'SAMPLER_2D_SHADOW': 0x8B62, + 'DELETE_STATUS': 0x8B80, + 'COMPILE_STATUS': 0x8B81, + 'LINK_STATUS': 0x8B82, + 'VALIDATE_STATUS': 0x8B83, + 'INFO_LOG_LENGTH': 0x8B84, + 'ATTACHED_SHADERS': 0x8B85, + 'ACTIVE_UNIFORMS': 0x8B86, + 'ACTIVE_UNIFORM_MAX_LENGTH': 0x8B87, + 'SHADER_SOURCE_LENGTH': 0x8B88, + 'ACTIVE_ATTRIBUTES': 0x8B89, + 'ACTIVE_ATTRIBUTE_MAX_LENGTH': 0x8B8A, + 'FRAGMENT_SHADER_DERIVATIVE_HINT': 0x8B8B, + 'SHADING_LANGUAGE_VERSION': 0x8B8C, + 'CURRENT_PROGRAM': 0x8B8D, + 'POINT_SPRITE_COORD_ORIGIN': 0x8CA0, + 'LOWER_LEFT': 0x8CA1, + 'UPPER_LEFT': 0x8CA2, + 'STENCIL_BACK_REF': 0x8CA3, + 'STENCIL_BACK_VALUE_MASK': 0x8CA4, + 'STENCIL_BACK_WRITEMASK': 0x8CA5, + 'CURRENT_RASTER_SECONDARY_COLOR': 0x845F, + 'PIXEL_PACK_BUFFER': 0x88EB, + 'PIXEL_UNPACK_BUFFER': 0x88EC, + 'PIXEL_PACK_BUFFER_BINDING': 0x88ED, + 'PIXEL_UNPACK_BUFFER_BINDING': 0x88EF, + 'FLOAT_MAT2x3': 0x8B65, + 'FLOAT_MAT2x4': 0x8B66, + 'FLOAT_MAT3x2': 0x8B67, + 'FLOAT_MAT3x4': 0x8B68, + 'FLOAT_MAT4x2': 0x8B69, + 'FLOAT_MAT4x3': 0x8B6A, + 'SRGB': 0x8C40, + 'SRGB8': 0x8C41, + 'SRGB_ALPHA': 0x8C42, + 'SRGB8_ALPHA8': 0x8C43, + 'SLUMINANCE_ALPHA': 0x8C44, + 'SLUMINANCE8_ALPHA8': 0x8C45, + 'SLUMINANCE': 0x8C46, + 'SLUMINANCE8': 0x8C47, + 'COMPRESSED_SRGB': 0x8C48, + 'COMPRESSED_SRGB_ALPHA': 0x8C49, + 'COMPRESSED_SLUMINANCE': 0x8C4A, + 'COMPRESSED_SLUMINANCE_ALPHA': 0x8C4B, + 'CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT': 0x0001, + 'MAJOR_VERSION': 0x821B, + 'MINOR_VERSION': 0x821C, + 'NUM_EXTENSIONS': 0x821D, + 'CONTEXT_FLAGS': 0x821E, + 'DEPTH_BUFFER': 0x8223, + 'STENCIL_BUFFER': 0x8224, + 'COMPRESSED_RED': 0x8225, + 'COMPRESSED_RG': 0x8226, + 'RGBA32F': 0x8814, + 'RGB32F': 0x8815, + 'RGBA16F': 0x881A, + 'RGB16F': 0x881B, + 'VERTEX_ATTRIB_ARRAY_INTEGER': 0x88FD, + 'MAX_ARRAY_TEXTURE_LAYERS': 0x88FF, + 'MIN_PROGRAM_TEXEL_OFFSET': 0x8904, + 'MAX_PROGRAM_TEXEL_OFFSET': 0x8905, + 'CLAMP_VERTEX_COLOR': 0x891A, + 'CLAMP_FRAGMENT_COLOR': 0x891B, + 'CLAMP_READ_COLOR': 0x891C, + 'FIXED_ONLY': 0x891D, + 'TEXTURE_RED_TYPE': 0x8C10, + 'TEXTURE_GREEN_TYPE': 0x8C11, + 'TEXTURE_BLUE_TYPE': 0x8C12, + 'TEXTURE_ALPHA_TYPE': 0x8C13, + 'TEXTURE_LUMINANCE_TYPE': 0x8C14, + 'TEXTURE_INTENSITY_TYPE': 0x8C15, + 'TEXTURE_DEPTH_TYPE': 0x8C16, + 'UNSIGNED_NORMALIZED': 0x8C17, + 'TEXTURE_1D_ARRAY': 0x8C18, + 'PROXY_TEXTURE_1D_ARRAY': 0x8C19, + 'TEXTURE_2D_ARRAY': 0x8C1A, + 'PROXY_TEXTURE_2D_ARRAY': 0x8C1B, + 'TEXTURE_BINDING_1D_ARRAY': 0x8C1C, + 'TEXTURE_BINDING_2D_ARRAY': 0x8C1D, + 'R11F_G11F_B10F': 0x8C3A, + 'UNSIGNED_INT_10F_11F_11F_REV': 0x8C3B, + 'RGB9_E5': 0x8C3D, + 'UNSIGNED_INT_5_9_9_9_REV': 0x8C3E, + 'TEXTURE_SHARED_SIZE': 0x8C3F, + 'TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH': 0x8C76, + 'TRANSFORM_FEEDBACK_BUFFER_MODE': 0x8C7F, + 'MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS': 0x8C80, + 'TRANSFORM_FEEDBACK_VARYINGS': 0x8C83, + 'TRANSFORM_FEEDBACK_BUFFER_START': 0x8C84, + 'TRANSFORM_FEEDBACK_BUFFER_SIZE': 0x8C85, + 'PRIMITIVES_GENERATED': 0x8C87, + 'TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN': 0x8C88, + 'RASTERIZER_DISCARD': 0x8C89, + 'MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS': 0x8C8A, + 'MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS': 0x8C8B, + 'INTERLEAVED_ATTRIBS': 0x8C8C, + 'SEPARATE_ATTRIBS': 0x8C8D, + 'TRANSFORM_FEEDBACK_BUFFER': 0x8C8E, + 'TRANSFORM_FEEDBACK_BUFFER_BINDING': 0x8C8F, + 'RGBA32UI': 0x8D70, + 'RGB32UI': 0x8D71, + 'RGBA16UI': 0x8D76, + 'RGB16UI': 0x8D77, + 'RGBA8UI': 0x8D7C, + 'RGB8UI': 0x8D7D, + 'RGBA32I': 0x8D82, + 'RGB32I': 0x8D83, + 'RGBA16I': 0x8D88, + 'RGB16I': 0x8D89, + 'RGBA8I': 0x8D8E, + 'RGB8I': 0x8D8F, + 'RED_INTEGER': 0x8D94, + 'GREEN_INTEGER': 0x8D95, + 'BLUE_INTEGER': 0x8D96, + 'ALPHA_INTEGER': 0x8D97, + 'RGB_INTEGER': 0x8D98, + 'RGBA_INTEGER': 0x8D99, + 'BGR_INTEGER': 0x8D9A, + 'BGRA_INTEGER': 0x8D9B, + 'SAMPLER_1D_ARRAY': 0x8DC0, + 'SAMPLER_2D_ARRAY': 0x8DC1, + 'SAMPLER_1D_ARRAY_SHADOW': 0x8DC3, + 'SAMPLER_2D_ARRAY_SHADOW': 0x8DC4, + 'SAMPLER_CUBE_SHADOW': 0x8DC5, + 'UNSIGNED_INT_VEC2': 0x8DC6, + 'UNSIGNED_INT_VEC3': 0x8DC7, + 'UNSIGNED_INT_VEC4': 0x8DC8, + 'INT_SAMPLER_1D': 0x8DC9, + 'INT_SAMPLER_2D': 0x8DCA, + 'INT_SAMPLER_3D': 0x8DCB, + 'INT_SAMPLER_CUBE': 0x8DCC, + 'INT_SAMPLER_1D_ARRAY': 0x8DCE, + 'INT_SAMPLER_2D_ARRAY': 0x8DCF, + 'UNSIGNED_INT_SAMPLER_1D': 0x8DD1, + 'UNSIGNED_INT_SAMPLER_2D': 0x8DD2, + 'UNSIGNED_INT_SAMPLER_3D': 0x8DD3, + 'UNSIGNED_INT_SAMPLER_CUBE': 0x8DD4, + 'UNSIGNED_INT_SAMPLER_1D_ARRAY': 0x8DD6, + 'UNSIGNED_INT_SAMPLER_2D_ARRAY': 0x8DD7, + 'QUERY_WAIT': 0x8E13, + 'QUERY_NO_WAIT': 0x8E14, + 'QUERY_BY_REGION_WAIT': 0x8E15, + 'QUERY_BY_REGION_NO_WAIT': 0x8E16, + 'MULTISAMPLE_3DFX': 0x86B2, + 'SAMPLE_BUFFERS_3DFX': 0x86B3, + 'SAMPLES_3DFX': 0x86B4, + 'MULTISAMPLE_BIT_3DFX': 0x20000000, + 'COMPRESSED_RGB_FXT1_3DFX': 0x86B0, + 'COMPRESSED_RGBA_FXT1_3DFX': 0x86B1, + 'UNPACK_CLIENT_STORAGE_APPLE': 0x85B2, + 'ELEMENT_ARRAY_APPLE': 0x8768, + 'ELEMENT_ARRAY_TYPE_APPLE': 0x8769, + 'ELEMENT_ARRAY_POINTER_APPLE': 0x876A, + 'HALF_APPLE': 0x140B, + 'RGBA_FLOAT32_APPLE': 0x8814, + 'RGB_FLOAT32_APPLE': 0x8815, + 'ALPHA_FLOAT32_APPLE': 0x8816, + 'INTENSITY_FLOAT32_APPLE': 0x8817, + 'LUMINANCE_FLOAT32_APPLE': 0x8818, + 'LUMINANCE_ALPHA_FLOAT32_APPLE': 0x8819, + 'RGBA_FLOAT16_APPLE': 0x881A, + 'RGB_FLOAT16_APPLE': 0x881B, + 'ALPHA_FLOAT16_APPLE': 0x881C, + 'INTENSITY_FLOAT16_APPLE': 0x881D, + 'LUMINANCE_FLOAT16_APPLE': 0x881E, + 'LUMINANCE_ALPHA_FLOAT16_APPLE': 0x881F, + 'COLOR_FLOAT_APPLE': 0x8A0F, + 'BUFFER_SERIALIZED_MODIFY_APPLE': 0x8A12, + 'BUFFER_FLUSHING_UNMAP_APPLE': 0x8A13, + 'MIN_PBUFFER_VIEWPORT_DIMS_APPLE': 0x8A10, + 'LIGHT_MODEL_SPECULAR_VECTOR_APPLE': 0x85B0, + 'TEXTURE_RANGE_LENGTH_APPLE': 0x85B7, + 'TEXTURE_RANGE_POINTER_APPLE': 0x85B8, + 'TEXTURE_STORAGE_HINT_APPLE': 0x85BC, + 'STORAGE_PRIVATE_APPLE': 0x85BD, + 'STORAGE_CACHED_APPLE': 0x85BE, + 'STORAGE_SHARED_APPLE': 0x85BF, + 'TRANSFORM_HINT_APPLE': 0x85B1, + 'VERTEX_ARRAY_BINDING_APPLE': 0x85B5, + 'VERTEX_ARRAY_RANGE_APPLE': 0x851D, + 'VERTEX_ARRAY_RANGE_LENGTH_APPLE': 0x851E, + 'VERTEX_ARRAY_STORAGE_HINT_APPLE': 0x851F, + 'MAX_VERTEX_ARRAY_RANGE_ELEMENT_APPLE': 0x8520, + 'VERTEX_ARRAY_RANGE_POINTER_APPLE': 0x8521, + 'STORAGE_CACHED_APPLE': 0x85BE, + 'STORAGE_SHARED_APPLE': 0x85BF, + 'YCBCR_422_APPLE': 0x85B9, + 'UNSIGNED_SHORT_8_8_APPLE': 0x85BA, + 'UNSIGNED_SHORT_8_8_REV_APPLE': 0x85BB, + 'RGBA_FLOAT_MODE_ARB': 0x8820, + 'CLAMP_VERTEX_COLOR_ARB': 0x891A, + 'CLAMP_FRAGMENT_COLOR_ARB': 0x891B, + 'CLAMP_READ_COLOR_ARB': 0x891C, + 'FIXED_ONLY_ARB': 0x891D, + 'DEPTH_COMPONENT32F': 0x8CAC, + 'DEPTH32F_STENCIL8': 0x8CAD, + 'FLOAT_32_UNSIGNED_INT_24_8_REV': 0x8DAD, + 'DEPTH_COMPONENT16_ARB': 0x81A5, + 'DEPTH_COMPONENT24_ARB': 0x81A6, + 'DEPTH_COMPONENT32_ARB': 0x81A7, + 'TEXTURE_DEPTH_SIZE_ARB': 0x884A, + 'DEPTH_TEXTURE_MODE_ARB': 0x884B, + 'MAX_DRAW_BUFFERS_ARB': 0x8824, + 'DRAW_BUFFER0_ARB': 0x8825, + 'DRAW_BUFFER1_ARB': 0x8826, + 'DRAW_BUFFER2_ARB': 0x8827, + 'DRAW_BUFFER3_ARB': 0x8828, + 'DRAW_BUFFER4_ARB': 0x8829, + 'DRAW_BUFFER5_ARB': 0x882A, + 'DRAW_BUFFER6_ARB': 0x882B, + 'DRAW_BUFFER7_ARB': 0x882C, + 'DRAW_BUFFER8_ARB': 0x882D, + 'DRAW_BUFFER9_ARB': 0x882E, + 'DRAW_BUFFER10_ARB': 0x882F, + 'DRAW_BUFFER11_ARB': 0x8830, + 'DRAW_BUFFER12_ARB': 0x8831, + 'DRAW_BUFFER13_ARB': 0x8832, + 'DRAW_BUFFER14_ARB': 0x8833, + 'DRAW_BUFFER15_ARB': 0x8834, + 'FRAGMENT_PROGRAM_ARB': 0x8804, + 'PROGRAM_ALU_INSTRUCTIONS_ARB': 0x8805, + 'PROGRAM_TEX_INSTRUCTIONS_ARB': 0x8806, + 'PROGRAM_TEX_INDIRECTIONS_ARB': 0x8807, + 'PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB': 0x8808, + 'PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB': 0x8809, + 'PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB': 0x880A, + 'MAX_PROGRAM_ALU_INSTRUCTIONS_ARB': 0x880B, + 'MAX_PROGRAM_TEX_INSTRUCTIONS_ARB': 0x880C, + 'MAX_PROGRAM_TEX_INDIRECTIONS_ARB': 0x880D, + 'MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB': 0x880E, + 'MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB': 0x880F, + 'MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB': 0x8810, + 'MAX_TEXTURE_COORDS_ARB': 0x8871, + 'MAX_TEXTURE_IMAGE_UNITS_ARB': 0x8872, + 'FRAGMENT_SHADER_ARB': 0x8B30, + 'MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB': 0x8B49, + 'FRAGMENT_SHADER_DERIVATIVE_HINT_ARB': 0x8B8B, + 'INVALID_FRAMEBUFFER_OPERATION': 0x0506, + 'FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING': 0x8210, + 'FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE': 0x8211, + 'FRAMEBUFFER_ATTACHMENT_RED_SIZE': 0x8212, + 'FRAMEBUFFER_ATTACHMENT_GREEN_SIZE': 0x8213, + 'FRAMEBUFFER_ATTACHMENT_BLUE_SIZE': 0x8214, + 'FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE': 0x8215, + 'FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE': 0x8216, + 'FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE': 0x8217, + 'FRAMEBUFFER_DEFAULT': 0x8218, + 'FRAMEBUFFER_UNDEFINED': 0x8219, + 'DEPTH_STENCIL_ATTACHMENT': 0x821A, + 'INDEX': 0x8222, + 'MAX_RENDERBUFFER_SIZE': 0x84E8, + 'DEPTH_STENCIL': 0x84F9, + 'UNSIGNED_INT_24_8': 0x84FA, + 'DEPTH24_STENCIL8': 0x88F0, + 'TEXTURE_STENCIL_SIZE': 0x88F1, + 'UNSIGNED_NORMALIZED': 0x8C17, + 'SRGB': 0x8C40, + 'DRAW_FRAMEBUFFER_BINDING': 0x8CA6, + 'FRAMEBUFFER_BINDING': 0x8CA6, + 'RENDERBUFFER_BINDING': 0x8CA7, + 'READ_FRAMEBUFFER': 0x8CA8, + 'DRAW_FRAMEBUFFER': 0x8CA9, + 'READ_FRAMEBUFFER_BINDING': 0x8CAA, + 'RENDERBUFFER_SAMPLES': 0x8CAB, + 'FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE': 0x8CD0, + 'FRAMEBUFFER_ATTACHMENT_OBJECT_NAME': 0x8CD1, + 'FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL': 0x8CD2, + 'FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE': 0x8CD3, + 'FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER': 0x8CD4, + 'FRAMEBUFFER_COMPLETE': 0x8CD5, + 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT': 0x8CD6, + 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT': 0x8CD7, + 'FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER': 0x8CDB, + 'FRAMEBUFFER_INCOMPLETE_READ_BUFFER': 0x8CDC, + 'FRAMEBUFFER_UNSUPPORTED': 0x8CDD, + 'MAX_COLOR_ATTACHMENTS': 0x8CDF, + 'COLOR_ATTACHMENT0': 0x8CE0, + 'COLOR_ATTACHMENT1': 0x8CE1, + 'COLOR_ATTACHMENT2': 0x8CE2, + 'COLOR_ATTACHMENT3': 0x8CE3, + 'COLOR_ATTACHMENT4': 0x8CE4, + 'COLOR_ATTACHMENT5': 0x8CE5, + 'COLOR_ATTACHMENT6': 0x8CE6, + 'COLOR_ATTACHMENT7': 0x8CE7, + 'COLOR_ATTACHMENT8': 0x8CE8, + 'COLOR_ATTACHMENT9': 0x8CE9, + 'COLOR_ATTACHMENT10': 0x8CEA, + 'COLOR_ATTACHMENT11': 0x8CEB, + 'COLOR_ATTACHMENT12': 0x8CEC, + 'COLOR_ATTACHMENT13': 0x8CED, + 'COLOR_ATTACHMENT14': 0x8CEE, + 'COLOR_ATTACHMENT15': 0x8CEF, + 'DEPTH_ATTACHMENT': 0x8D00, + 'STENCIL_ATTACHMENT': 0x8D20, + 'FRAMEBUFFER': 0x8D40, + 'RENDERBUFFER': 0x8D41, + 'RENDERBUFFER_WIDTH': 0x8D42, + 'RENDERBUFFER_HEIGHT': 0x8D43, + 'RENDERBUFFER_INTERNAL_FORMAT': 0x8D44, + 'STENCIL_INDEX1': 0x8D46, + 'STENCIL_INDEX4': 0x8D47, + 'STENCIL_INDEX8': 0x8D48, + 'STENCIL_INDEX16': 0x8D49, + 'RENDERBUFFER_RED_SIZE': 0x8D50, + 'RENDERBUFFER_GREEN_SIZE': 0x8D51, + 'RENDERBUFFER_BLUE_SIZE': 0x8D52, + 'RENDERBUFFER_ALPHA_SIZE': 0x8D53, + 'RENDERBUFFER_DEPTH_SIZE': 0x8D54, + 'RENDERBUFFER_STENCIL_SIZE': 0x8D55, + 'FRAMEBUFFER_INCOMPLETE_MULTISAMPLE': 0x8D56, + 'MAX_SAMPLES': 0x8D57, + 'FRAMEBUFFER_SRGB': 0x8DB9, + 'LINES_ADJACENCY_ARB': 0xA, + 'LINE_STRIP_ADJACENCY_ARB': 0xB, + 'TRIANGLES_ADJACENCY_ARB': 0xC, + 'TRIANGLE_STRIP_ADJACENCY_ARB': 0xD, + 'PROGRAM_POINT_SIZE_ARB': 0x8642, + 'MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB': 0x8C29, + 'FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER': 0x8CD4, + 'FRAMEBUFFER_ATTACHMENT_LAYERED_ARB': 0x8DA7, + 'FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB': 0x8DA8, + 'FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB': 0x8DA9, + 'GEOMETRY_SHADER_ARB': 0x8DD9, + 'GEOMETRY_VERTICES_OUT_ARB': 0x8DDA, + 'GEOMETRY_INPUT_TYPE_ARB': 0x8DDB, + 'GEOMETRY_OUTPUT_TYPE_ARB': 0x8DDC, + 'MAX_GEOMETRY_VARYING_COMPONENTS_ARB': 0x8DDD, + 'MAX_VERTEX_VARYING_COMPONENTS_ARB': 0x8DDE, + 'MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB': 0x8DDF, + 'MAX_GEOMETRY_OUTPUT_VERTICES_ARB': 0x8DE0, + 'MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB': 0x8DE1, + 'HALF_FLOAT_ARB': 0x140B, + 'HALF_FLOAT': 0x140B, + 'CONSTANT_COLOR': 0x8001, + 'ONE_MINUS_CONSTANT_COLOR': 0x8002, + 'CONSTANT_ALPHA': 0x8003, + 'ONE_MINUS_CONSTANT_ALPHA': 0x8004, + 'BLEND_COLOR': 0x8005, + 'FUNC_ADD': 0x8006, + 'MIN': 0x8007, + 'MAX': 0x8008, + 'BLEND_EQUATION': 0x8009, + 'FUNC_SUBTRACT': 0x800A, + 'FUNC_REVERSE_SUBTRACT': 0x800B, + 'CONVOLUTION_1D': 0x8010, + 'CONVOLUTION_2D': 0x8011, + 'SEPARABLE_2D': 0x8012, + 'CONVOLUTION_BORDER_MODE': 0x8013, + 'CONVOLUTION_FILTER_SCALE': 0x8014, + 'CONVOLUTION_FILTER_BIAS': 0x8015, + 'REDUCE': 0x8016, + 'CONVOLUTION_FORMAT': 0x8017, + 'CONVOLUTION_WIDTH': 0x8018, + 'CONVOLUTION_HEIGHT': 0x8019, + 'MAX_CONVOLUTION_WIDTH': 0x801A, + 'MAX_CONVOLUTION_HEIGHT': 0x801B, + 'POST_CONVOLUTION_RED_SCALE': 0x801C, + 'POST_CONVOLUTION_GREEN_SCALE': 0x801D, + 'POST_CONVOLUTION_BLUE_SCALE': 0x801E, + 'POST_CONVOLUTION_ALPHA_SCALE': 0x801F, + 'POST_CONVOLUTION_RED_BIAS': 0x8020, + 'POST_CONVOLUTION_GREEN_BIAS': 0x8021, + 'POST_CONVOLUTION_BLUE_BIAS': 0x8022, + 'POST_CONVOLUTION_ALPHA_BIAS': 0x8023, + 'HISTOGRAM': 0x8024, + 'PROXY_HISTOGRAM': 0x8025, + 'HISTOGRAM_WIDTH': 0x8026, + 'HISTOGRAM_FORMAT': 0x8027, + 'HISTOGRAM_RED_SIZE': 0x8028, + 'HISTOGRAM_GREEN_SIZE': 0x8029, + 'HISTOGRAM_BLUE_SIZE': 0x802A, + 'HISTOGRAM_ALPHA_SIZE': 0x802B, + 'HISTOGRAM_LUMINANCE_SIZE': 0x802C, + 'HISTOGRAM_SINK': 0x802D, + 'MINMAX': 0x802E, + 'MINMAX_FORMAT': 0x802F, + 'MINMAX_SINK': 0x8030, + 'TABLE_TOO_LARGE': 0x8031, + 'COLOR_MATRIX': 0x80B1, + 'COLOR_MATRIX_STACK_DEPTH': 0x80B2, + 'MAX_COLOR_MATRIX_STACK_DEPTH': 0x80B3, + 'POST_COLOR_MATRIX_RED_SCALE': 0x80B4, + 'POST_COLOR_MATRIX_GREEN_SCALE': 0x80B5, + 'POST_COLOR_MATRIX_BLUE_SCALE': 0x80B6, + 'POST_COLOR_MATRIX_ALPHA_SCALE': 0x80B7, + 'POST_COLOR_MATRIX_RED_BIAS': 0x80B8, + 'POST_COLOR_MATRIX_GREEN_BIAS': 0x80B9, + 'POST_COLOR_MATRIX_BLUE_BIAS': 0x80BA, + 'POST_COLOR_MATRIX_ALPHA_BIAS': 0x80BB, + 'COLOR_TABLE': 0x80D0, + 'POST_CONVOLUTION_COLOR_TABLE': 0x80D1, + 'POST_COLOR_MATRIX_COLOR_TABLE': 0x80D2, + 'PROXY_COLOR_TABLE': 0x80D3, + 'PROXY_POST_CONVOLUTION_COLOR_TABLE': 0x80D4, + 'PROXY_POST_COLOR_MATRIX_COLOR_TABLE': 0x80D5, + 'COLOR_TABLE_SCALE': 0x80D6, + 'COLOR_TABLE_BIAS': 0x80D7, + 'COLOR_TABLE_FORMAT': 0x80D8, + 'COLOR_TABLE_WIDTH': 0x80D9, + 'COLOR_TABLE_RED_SIZE': 0x80DA, + 'COLOR_TABLE_GREEN_SIZE': 0x80DB, + 'COLOR_TABLE_BLUE_SIZE': 0x80DC, + 'COLOR_TABLE_ALPHA_SIZE': 0x80DD, + 'COLOR_TABLE_LUMINANCE_SIZE': 0x80DE, + 'COLOR_TABLE_INTENSITY_SIZE': 0x80DF, + 'IGNORE_BORDER': 0x8150, + 'CONSTANT_BORDER': 0x8151, + 'WRAP_BORDER': 0x8152, + 'REPLICATE_BORDER': 0x8153, + 'CONVOLUTION_BORDER_COLOR': 0x8154, + 'VERTEX_ATTRIB_ARRAY_DIVISOR_ARB': 0x88FE, + 'MAP_READ_BIT': 0x0001, + 'MAP_WRITE_BIT': 0x0002, + 'MAP_INVALIDATE_RANGE_BIT': 0x0004, + 'MAP_INVALIDATE_BUFFER_BIT': 0x0008, + 'MAP_FLUSH_EXPLICIT_BIT': 0x0010, + 'MAP_UNSYNCHRONIZED_BIT': 0x0020, + 'MATRIX_PALETTE_ARB': 0x8840, + 'MAX_MATRIX_PALETTE_STACK_DEPTH_ARB': 0x8841, + 'MAX_PALETTE_MATRICES_ARB': 0x8842, + 'CURRENT_PALETTE_MATRIX_ARB': 0x8843, + 'MATRIX_INDEX_ARRAY_ARB': 0x8844, + 'CURRENT_MATRIX_INDEX_ARB': 0x8845, + 'MATRIX_INDEX_ARRAY_SIZE_ARB': 0x8846, + 'MATRIX_INDEX_ARRAY_TYPE_ARB': 0x8847, + 'MATRIX_INDEX_ARRAY_STRIDE_ARB': 0x8848, + 'MATRIX_INDEX_ARRAY_POINTER_ARB': 0x8849, + 'MULTISAMPLE_ARB': 0x809D, + 'SAMPLE_ALPHA_TO_COVERAGE_ARB': 0x809E, + 'SAMPLE_ALPHA_TO_ONE_ARB': 0x809F, + 'SAMPLE_COVERAGE_ARB': 0x80A0, + 'SAMPLE_BUFFERS_ARB': 0x80A8, + 'SAMPLES_ARB': 0x80A9, + 'SAMPLE_COVERAGE_VALUE_ARB': 0x80AA, + 'SAMPLE_COVERAGE_INVERT_ARB': 0x80AB, + 'MULTISAMPLE_BIT_ARB': 0x20000000, + 'TEXTURE0_ARB': 0x84C0, + 'TEXTURE1_ARB': 0x84C1, + 'TEXTURE2_ARB': 0x84C2, + 'TEXTURE3_ARB': 0x84C3, + 'TEXTURE4_ARB': 0x84C4, + 'TEXTURE5_ARB': 0x84C5, + 'TEXTURE6_ARB': 0x84C6, + 'TEXTURE7_ARB': 0x84C7, + 'TEXTURE8_ARB': 0x84C8, + 'TEXTURE9_ARB': 0x84C9, + 'TEXTURE10_ARB': 0x84CA, + 'TEXTURE11_ARB': 0x84CB, + 'TEXTURE12_ARB': 0x84CC, + 'TEXTURE13_ARB': 0x84CD, + 'TEXTURE14_ARB': 0x84CE, + 'TEXTURE15_ARB': 0x84CF, + 'TEXTURE16_ARB': 0x84D0, + 'TEXTURE17_ARB': 0x84D1, + 'TEXTURE18_ARB': 0x84D2, + 'TEXTURE19_ARB': 0x84D3, + 'TEXTURE20_ARB': 0x84D4, + 'TEXTURE21_ARB': 0x84D5, + 'TEXTURE22_ARB': 0x84D6, + 'TEXTURE23_ARB': 0x84D7, + 'TEXTURE24_ARB': 0x84D8, + 'TEXTURE25_ARB': 0x84D9, + 'TEXTURE26_ARB': 0x84DA, + 'TEXTURE27_ARB': 0x84DB, + 'TEXTURE28_ARB': 0x84DC, + 'TEXTURE29_ARB': 0x84DD, + 'TEXTURE30_ARB': 0x84DE, + 'TEXTURE31_ARB': 0x84DF, + 'ACTIVE_TEXTURE_ARB': 0x84E0, + 'CLIENT_ACTIVE_TEXTURE_ARB': 0x84E1, + 'MAX_TEXTURE_UNITS_ARB': 0x84E2, + 'QUERY_COUNTER_BITS_ARB': 0x8864, + 'CURRENT_QUERY_ARB': 0x8865, + 'QUERY_RESULT_ARB': 0x8866, + 'QUERY_RESULT_AVAILABLE_ARB': 0x8867, + 'SAMPLES_PASSED_ARB': 0x8914, + 'PIXEL_PACK_BUFFER_ARB': 0x88EB, + 'PIXEL_UNPACK_BUFFER_ARB': 0x88EC, + 'PIXEL_PACK_BUFFER_BINDING_ARB': 0x88ED, + 'PIXEL_UNPACK_BUFFER_BINDING_ARB': 0x88EF, + 'POINT_SIZE_MIN_ARB': 0x8126, + 'POINT_SIZE_MAX_ARB': 0x8127, + 'POINT_FADE_THRESHOLD_SIZE_ARB': 0x8128, + 'POINT_DISTANCE_ATTENUATION_ARB': 0x8129, + 'POINT_SPRITE_ARB': 0x8861, + 'COORD_REPLACE_ARB': 0x8862, + 'PROGRAM_OBJECT_ARB': 0x8B40, + 'SHADER_OBJECT_ARB': 0x8B48, + 'OBJECT_TYPE_ARB': 0x8B4E, + 'OBJECT_SUBTYPE_ARB': 0x8B4F, + 'FLOAT_VEC2_ARB': 0x8B50, + 'FLOAT_VEC3_ARB': 0x8B51, + 'FLOAT_VEC4_ARB': 0x8B52, + 'INT_VEC2_ARB': 0x8B53, + 'INT_VEC3_ARB': 0x8B54, + 'INT_VEC4_ARB': 0x8B55, + 'BOOL_ARB': 0x8B56, + 'BOOL_VEC2_ARB': 0x8B57, + 'BOOL_VEC3_ARB': 0x8B58, + 'BOOL_VEC4_ARB': 0x8B59, + 'FLOAT_MAT2_ARB': 0x8B5A, + 'FLOAT_MAT3_ARB': 0x8B5B, + 'FLOAT_MAT4_ARB': 0x8B5C, + 'SAMPLER_1D_ARB': 0x8B5D, + 'SAMPLER_2D_ARB': 0x8B5E, + 'SAMPLER_3D_ARB': 0x8B5F, + 'SAMPLER_CUBE_ARB': 0x8B60, + 'SAMPLER_1D_SHADOW_ARB': 0x8B61, + 'SAMPLER_2D_SHADOW_ARB': 0x8B62, + 'SAMPLER_2D_RECT_ARB': 0x8B63, + 'SAMPLER_2D_RECT_SHADOW_ARB': 0x8B64, + 'OBJECT_DELETE_STATUS_ARB': 0x8B80, + 'OBJECT_COMPILE_STATUS_ARB': 0x8B81, + 'OBJECT_LINK_STATUS_ARB': 0x8B82, + 'OBJECT_VALIDATE_STATUS_ARB': 0x8B83, + 'OBJECT_INFO_LOG_LENGTH_ARB': 0x8B84, + 'OBJECT_ATTACHED_OBJECTS_ARB': 0x8B85, + 'OBJECT_ACTIVE_UNIFORMS_ARB': 0x8B86, + 'OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB': 0x8B87, + 'ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH': 0x8A35, + 'UNIFORM_NAME_LENGTH': 0x8A39, + 'UNIFORM_BLOCK_NAME_LENGTH': 0x8A41, + 'OBJECT_SHADER_SOURCE_LENGTH_ARB': 0x8B88, + 'SHADING_LANGUAGE_VERSION_ARB': 0x8B8C, + 'TEXTURE_COMPARE_MODE_ARB': 0x884C, + 'TEXTURE_COMPARE_FUNC_ARB': 0x884D, + 'COMPARE_R_TO_TEXTURE_ARB': 0x884E, + 'TEXTURE_COMPARE_FAIL_VALUE_ARB': 0x80BF, + 'CLAMP_TO_BORDER_ARB': 0x812D, + 'TEXTURE_BUFFER_ARB': 0x8C2A, + 'MAX_TEXTURE_BUFFER_SIZE_ARB': 0x8C2B, + 'TEXTURE_BINDING_BUFFER_ARB': 0x8C2C, + 'TEXTURE_BUFFER_DATA_STORE_BINDING_ARB': 0x8C2D, + 'TEXTURE_BUFFER_FORMAT_ARB': 0x8C2E, + 'COMPRESSED_ALPHA_ARB': 0x84E9, + 'COMPRESSED_LUMINANCE_ARB': 0x84EA, + 'COMPRESSED_LUMINANCE_ALPHA_ARB': 0x84EB, + 'COMPRESSED_INTENSITY_ARB': 0x84EC, + 'COMPRESSED_RGB_ARB': 0x84ED, + 'COMPRESSED_RGBA_ARB': 0x84EE, + 'TEXTURE_COMPRESSION_HINT_ARB': 0x84EF, + 'TEXTURE_COMPRESSED_IMAGE_SIZE_ARB': 0x86A0, + 'TEXTURE_COMPRESSED_ARB': 0x86A1, + 'NUM_COMPRESSED_TEXTURE_FORMATS_ARB': 0x86A2, + 'COMPRESSED_TEXTURE_FORMATS_ARB': 0x86A3, + 'COMPRESSED_RED_RGTC1': 0x8DBB, + 'COMPRESSED_SIGNED_RED_RGTC1': 0x8DBC, + 'COMPRESSED_RG_RGTC2': 0x8DBD, + 'COMPRESSED_SIGNED_RG_RGTC2': 0x8DBE, + 'NORMAL_MAP_ARB': 0x8511, + 'REFLECTION_MAP_ARB': 0x8512, + 'TEXTURE_CUBE_MAP_ARB': 0x8513, + 'TEXTURE_BINDING_CUBE_MAP_ARB': 0x8514, + 'TEXTURE_CUBE_MAP_POSITIVE_X_ARB': 0x8515, + 'TEXTURE_CUBE_MAP_NEGATIVE_X_ARB': 0x8516, + 'TEXTURE_CUBE_MAP_POSITIVE_Y_ARB': 0x8517, + 'TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB': 0x8518, + 'TEXTURE_CUBE_MAP_POSITIVE_Z_ARB': 0x8519, + 'TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB': 0x851A, + 'PROXY_TEXTURE_CUBE_MAP_ARB': 0x851B, + 'MAX_CUBE_MAP_TEXTURE_SIZE_ARB': 0x851C, + 'SUBTRACT_ARB': 0x84E7, + 'COMBINE_ARB': 0x8570, + 'COMBINE_RGB_ARB': 0x8571, + 'COMBINE_ALPHA_ARB': 0x8572, + 'RGB_SCALE_ARB': 0x8573, + 'ADD_SIGNED_ARB': 0x8574, + 'INTERPOLATE_ARB': 0x8575, + 'CONSTANT_ARB': 0x8576, + 'PRIMARY_COLOR_ARB': 0x8577, + 'PREVIOUS_ARB': 0x8578, + 'SOURCE0_RGB_ARB': 0x8580, + 'SOURCE1_RGB_ARB': 0x8581, + 'SOURCE2_RGB_ARB': 0x8582, + 'SOURCE0_ALPHA_ARB': 0x8588, + 'SOURCE1_ALPHA_ARB': 0x8589, + 'SOURCE2_ALPHA_ARB': 0x858A, + 'OPERAND0_RGB_ARB': 0x8590, + 'OPERAND1_RGB_ARB': 0x8591, + 'OPERAND2_RGB_ARB': 0x8592, + 'OPERAND0_ALPHA_ARB': 0x8598, + 'OPERAND1_ALPHA_ARB': 0x8599, + 'OPERAND2_ALPHA_ARB': 0x859A, + 'DOT3_RGB_ARB': 0x86AE, + 'DOT3_RGBA_ARB': 0x86AF, + 'RGBA32F_ARB': 0x8814, + 'RGB32F_ARB': 0x8815, + 'ALPHA32F_ARB': 0x8816, + 'INTENSITY32F_ARB': 0x8817, + 'LUMINANCE32F_ARB': 0x8818, + 'LUMINANCE_ALPHA32F_ARB': 0x8819, + 'RGBA16F_ARB': 0x881A, + 'RGB16F_ARB': 0x881B, + 'ALPHA16F_ARB': 0x881C, + 'INTENSITY16F_ARB': 0x881D, + 'LUMINANCE16F_ARB': 0x881E, + 'LUMINANCE_ALPHA16F_ARB': 0x881F, + 'TEXTURE_RED_TYPE_ARB': 0x8C10, + 'TEXTURE_GREEN_TYPE_ARB': 0x8C11, + 'TEXTURE_BLUE_TYPE_ARB': 0x8C12, + 'TEXTURE_ALPHA_TYPE_ARB': 0x8C13, + 'TEXTURE_LUMINANCE_TYPE_ARB': 0x8C14, + 'TEXTURE_INTENSITY_TYPE_ARB': 0x8C15, + 'TEXTURE_DEPTH_TYPE_ARB': 0x8C16, + 'UNSIGNED_NORMALIZED_ARB': 0x8C17, + 'MIRRORED_REPEAT_ARB': 0x8370, + 'TEXTURE_RECTANGLE_ARB': 0x84F5, + 'TEXTURE_BINDING_RECTANGLE_ARB': 0x84F6, + 'PROXY_TEXTURE_RECTANGLE_ARB': 0x84F7, + 'MAX_RECTANGLE_TEXTURE_SIZE_ARB': 0x84F8, + 'SAMPLER_2D_RECT_ARB': 0x8B63, + 'SAMPLER_2D_RECT_SHADOW_ARB': 0x8B64, + 'RED': 0x1903, + 'RG': 0x8227, + 'RG_INTEGER': 0x8228, + 'R8': 0x8229, + 'R16': 0x822A, + 'RG8': 0x822B, + 'RG16': 0x822C, + 'R16F': 0x822D, + 'R32F': 0x822E, + 'RG16F': 0x822F, + 'RG32F': 0x8230, + 'R8I': 0x8231, + 'R8UI': 0x8232, + 'R16I': 0x8233, + 'R16UI': 0x8234, + 'R32I': 0x8235, + 'R32UI': 0x8236, + 'RG8I': 0x8237, + 'RG8UI': 0x8238, + 'RG16I': 0x8239, + 'RG16UI': 0x823A, + 'RG32I': 0x823B, + 'RG32UI': 0x823C, + 'TRANSPOSE_MODELVIEW_MATRIX_ARB': 0x84E3, + 'TRANSPOSE_PROJECTION_MATRIX_ARB': 0x84E4, + 'TRANSPOSE_TEXTURE_MATRIX_ARB': 0x84E5, + 'TRANSPOSE_COLOR_MATRIX_ARB': 0x84E6, + 'VERTEX_ARRAY_BINDING': 0x85B5, + 'MODELVIEW0_ARB': 0x1700, + 'MODELVIEW1_ARB': 0x850A, + 'MAX_VERTEX_UNITS_ARB': 0x86A4, + 'ACTIVE_VERTEX_UNITS_ARB': 0x86A5, + 'WEIGHT_SUM_UNITY_ARB': 0x86A6, + 'VERTEX_BLEND_ARB': 0x86A7, + 'CURRENT_WEIGHT_ARB': 0x86A8, + 'WEIGHT_ARRAY_TYPE_ARB': 0x86A9, + 'WEIGHT_ARRAY_STRIDE_ARB': 0x86AA, + 'WEIGHT_ARRAY_SIZE_ARB': 0x86AB, + 'WEIGHT_ARRAY_POINTER_ARB': 0x86AC, + 'WEIGHT_ARRAY_ARB': 0x86AD, + 'MODELVIEW2_ARB': 0x8722, + 'MODELVIEW3_ARB': 0x8723, + 'MODELVIEW4_ARB': 0x8724, + 'MODELVIEW5_ARB': 0x8725, + 'MODELVIEW6_ARB': 0x8726, + 'MODELVIEW7_ARB': 0x8727, + 'MODELVIEW8_ARB': 0x8728, + 'MODELVIEW9_ARB': 0x8729, + 'MODELVIEW10_ARB': 0x872A, + 'MODELVIEW11_ARB': 0x872B, + 'MODELVIEW12_ARB': 0x872C, + 'MODELVIEW13_ARB': 0x872D, + 'MODELVIEW14_ARB': 0x872E, + 'MODELVIEW15_ARB': 0x872F, + 'MODELVIEW16_ARB': 0x8730, + 'MODELVIEW17_ARB': 0x8731, + 'MODELVIEW18_ARB': 0x8732, + 'MODELVIEW19_ARB': 0x8733, + 'MODELVIEW20_ARB': 0x8734, + 'MODELVIEW21_ARB': 0x8735, + 'MODELVIEW22_ARB': 0x8736, + 'MODELVIEW23_ARB': 0x8737, + 'MODELVIEW24_ARB': 0x8738, + 'MODELVIEW25_ARB': 0x8739, + 'MODELVIEW26_ARB': 0x873A, + 'MODELVIEW27_ARB': 0x873B, + 'MODELVIEW28_ARB': 0x873C, + 'MODELVIEW29_ARB': 0x873D, + 'MODELVIEW30_ARB': 0x873E, + 'MODELVIEW31_ARB': 0x873F, + 'BUFFER_SIZE_ARB': 0x8764, + 'BUFFER_USAGE_ARB': 0x8765, + 'ARRAY_BUFFER_ARB': 0x8892, + 'ELEMENT_ARRAY_BUFFER_ARB': 0x8893, + 'ARRAY_BUFFER_BINDING_ARB': 0x8894, + 'ELEMENT_ARRAY_BUFFER_BINDING_ARB': 0x8895, + 'VERTEX_ARRAY_BUFFER_BINDING_ARB': 0x8896, + 'NORMAL_ARRAY_BUFFER_BINDING_ARB': 0x8897, + 'COLOR_ARRAY_BUFFER_BINDING_ARB': 0x8898, + 'INDEX_ARRAY_BUFFER_BINDING_ARB': 0x8899, + 'TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB': 0x889A, + 'EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB': 0x889B, + 'SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB': 0x889C, + 'FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB': 0x889D, + 'WEIGHT_ARRAY_BUFFER_BINDING_ARB': 0x889E, + 'VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB': 0x889F, + 'READ_ONLY_ARB': 0x88B8, + 'WRITE_ONLY_ARB': 0x88B9, + 'READ_WRITE_ARB': 0x88BA, + 'BUFFER_ACCESS_ARB': 0x88BB, + 'BUFFER_MAPPED_ARB': 0x88BC, + 'BUFFER_MAP_POINTER_ARB': 0x88BD, + 'STREAM_DRAW_ARB': 0x88E0, + 'STREAM_READ_ARB': 0x88E1, + 'STREAM_COPY_ARB': 0x88E2, + 'STATIC_DRAW_ARB': 0x88E4, + 'STATIC_READ_ARB': 0x88E5, + 'STATIC_COPY_ARB': 0x88E6, + 'DYNAMIC_DRAW_ARB': 0x88E8, + 'DYNAMIC_READ_ARB': 0x88E9, + 'DYNAMIC_COPY_ARB': 0x88EA, + 'COLOR_SUM_ARB': 0x8458, + 'VERTEX_PROGRAM_ARB': 0x8620, + 'VERTEX_ATTRIB_ARRAY_ENABLED_ARB': 0x8622, + 'VERTEX_ATTRIB_ARRAY_SIZE_ARB': 0x8623, + 'VERTEX_ATTRIB_ARRAY_STRIDE_ARB': 0x8624, + 'VERTEX_ATTRIB_ARRAY_TYPE_ARB': 0x8625, + 'CURRENT_VERTEX_ATTRIB_ARB': 0x8626, + 'PROGRAM_LENGTH_ARB': 0x8627, + 'PROGRAM_STRING_ARB': 0x8628, + 'MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB': 0x862E, + 'MAX_PROGRAM_MATRICES_ARB': 0x862F, + 'CURRENT_MATRIX_STACK_DEPTH_ARB': 0x8640, + 'CURRENT_MATRIX_ARB': 0x8641, + 'VERTEX_PROGRAM_POINT_SIZE_ARB': 0x8642, + 'VERTEX_PROGRAM_TWO_SIDE_ARB': 0x8643, + 'VERTEX_ATTRIB_ARRAY_POINTER_ARB': 0x8645, + 'PROGRAM_ERROR_POSITION_ARB': 0x864B, + 'PROGRAM_BINDING_ARB': 0x8677, + 'MAX_VERTEX_ATTRIBS_ARB': 0x8869, + 'VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB': 0x886A, + 'PROGRAM_ERROR_STRING_ARB': 0x8874, + 'PROGRAM_FORMAT_ASCII_ARB': 0x8875, + 'PROGRAM_FORMAT_ARB': 0x8876, + 'PROGRAM_INSTRUCTIONS_ARB': 0x88A0, + 'MAX_PROGRAM_INSTRUCTIONS_ARB': 0x88A1, + 'PROGRAM_NATIVE_INSTRUCTIONS_ARB': 0x88A2, + 'MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB': 0x88A3, + 'PROGRAM_TEMPORARIES_ARB': 0x88A4, + 'MAX_PROGRAM_TEMPORARIES_ARB': 0x88A5, + 'PROGRAM_NATIVE_TEMPORARIES_ARB': 0x88A6, + 'MAX_PROGRAM_NATIVE_TEMPORARIES_ARB': 0x88A7, + 'PROGRAM_PARAMETERS_ARB': 0x88A8, + 'MAX_PROGRAM_PARAMETERS_ARB': 0x88A9, + 'PROGRAM_NATIVE_PARAMETERS_ARB': 0x88AA, + 'MAX_PROGRAM_NATIVE_PARAMETERS_ARB': 0x88AB, + 'PROGRAM_ATTRIBS_ARB': 0x88AC, + 'MAX_PROGRAM_ATTRIBS_ARB': 0x88AD, + 'PROGRAM_NATIVE_ATTRIBS_ARB': 0x88AE, + 'MAX_PROGRAM_NATIVE_ATTRIBS_ARB': 0x88AF, + 'PROGRAM_ADDRESS_REGISTERS_ARB': 0x88B0, + 'MAX_PROGRAM_ADDRESS_REGISTERS_ARB': 0x88B1, + 'PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB': 0x88B2, + 'MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB': 0x88B3, + 'MAX_PROGRAM_LOCAL_PARAMETERS_ARB': 0x88B4, + 'MAX_PROGRAM_ENV_PARAMETERS_ARB': 0x88B5, + 'PROGRAM_UNDER_NATIVE_LIMITS_ARB': 0x88B6, + 'TRANSPOSE_CURRENT_MATRIX_ARB': 0x88B7, + 'MATRIX0_ARB': 0x88C0, + 'MATRIX1_ARB': 0x88C1, + 'MATRIX2_ARB': 0x88C2, + 'MATRIX3_ARB': 0x88C3, + 'MATRIX4_ARB': 0x88C4, + 'MATRIX5_ARB': 0x88C5, + 'MATRIX6_ARB': 0x88C6, + 'MATRIX7_ARB': 0x88C7, + 'MATRIX8_ARB': 0x88C8, + 'MATRIX9_ARB': 0x88C9, + 'MATRIX10_ARB': 0x88CA, + 'MATRIX11_ARB': 0x88CB, + 'MATRIX12_ARB': 0x88CC, + 'MATRIX13_ARB': 0x88CD, + 'MATRIX14_ARB': 0x88CE, + 'MATRIX15_ARB': 0x88CF, + 'MATRIX16_ARB': 0x88D0, + 'MATRIX17_ARB': 0x88D1, + 'MATRIX18_ARB': 0x88D2, + 'MATRIX19_ARB': 0x88D3, + 'MATRIX20_ARB': 0x88D4, + 'MATRIX21_ARB': 0x88D5, + 'MATRIX22_ARB': 0x88D6, + 'MATRIX23_ARB': 0x88D7, + 'MATRIX24_ARB': 0x88D8, + 'MATRIX25_ARB': 0x88D9, + 'MATRIX26_ARB': 0x88DA, + 'MATRIX27_ARB': 0x88DB, + 'MATRIX28_ARB': 0x88DC, + 'MATRIX29_ARB': 0x88DD, + 'MATRIX30_ARB': 0x88DE, + 'MATRIX31_ARB': 0x88DF, + 'VERTEX_SHADER_ARB': 0x8B31, + 'MAX_VERTEX_UNIFORM_COMPONENTS_ARB': 0x8B4A, + 'MAX_VARYING_FLOATS_ARB': 0x8B4B, + 'MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB': 0x8B4C, + 'MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB': 0x8B4D, + 'OBJECT_ACTIVE_ATTRIBUTES_ARB': 0x8B89, + 'OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB': 0x8B8A, + 'TEXTURE_POINT_MODE_ATIX': 0x60B0, + 'TEXTURE_POINT_ONE_COORD_ATIX': 0x60B1, + 'TEXTURE_POINT_SPRITE_ATIX': 0x60B2, + 'POINT_SPRITE_CULL_MODE_ATIX': 0x60B3, + 'POINT_SPRITE_CULL_CENTER_ATIX': 0x60B4, + 'POINT_SPRITE_CULL_CLIP_ATIX': 0x60B5, + 'MODULATE_ADD_ATIX': 0x8744, + 'MODULATE_SIGNED_ADD_ATIX': 0x8745, + 'MODULATE_SUBTRACT_ATIX': 0x8746, + 'SECONDARY_COLOR_ATIX': 0x8747, + 'TEXTURE_OUTPUT_RGB_ATIX': 0x8748, + 'TEXTURE_OUTPUT_ALPHA_ATIX': 0x8749, + 'OUTPUT_POINT_SIZE_ATIX': 0x610E, + 'MAX_DRAW_BUFFERS_ATI': 0x8824, + 'DRAW_BUFFER0_ATI': 0x8825, + 'DRAW_BUFFER1_ATI': 0x8826, + 'DRAW_BUFFER2_ATI': 0x8827, + 'DRAW_BUFFER3_ATI': 0x8828, + 'DRAW_BUFFER4_ATI': 0x8829, + 'DRAW_BUFFER5_ATI': 0x882A, + 'DRAW_BUFFER6_ATI': 0x882B, + 'DRAW_BUFFER7_ATI': 0x882C, + 'DRAW_BUFFER8_ATI': 0x882D, + 'DRAW_BUFFER9_ATI': 0x882E, + 'DRAW_BUFFER10_ATI': 0x882F, + 'DRAW_BUFFER11_ATI': 0x8830, + 'DRAW_BUFFER12_ATI': 0x8831, + 'DRAW_BUFFER13_ATI': 0x8832, + 'DRAW_BUFFER14_ATI': 0x8833, + 'DRAW_BUFFER15_ATI': 0x8834, + 'ELEMENT_ARRAY_ATI': 0x8768, + 'ELEMENT_ARRAY_TYPE_ATI': 0x8769, + 'ELEMENT_ARRAY_POINTER_ATI': 0x876A, + 'BUMP_ROT_MATRIX_ATI': 0x8775, + 'BUMP_ROT_MATRIX_SIZE_ATI': 0x8776, + 'BUMP_NUM_TEX_UNITS_ATI': 0x8777, + 'BUMP_TEX_UNITS_ATI': 0x8778, + 'DUDV_ATI': 0x8779, + 'DU8DV8_ATI': 0x877A, + 'BUMP_ENVMAP_ATI': 0x877B, + 'BUMP_TARGET_ATI': 0x877C, + 'RED_BIT_ATI': 0x00000001, + '2X_BIT_ATI': 0x00000001, + '4X_BIT_ATI': 0x00000002, + 'GREEN_BIT_ATI': 0x00000002, + 'COMP_BIT_ATI': 0x00000002, + 'BLUE_BIT_ATI': 0x00000004, + '8X_BIT_ATI': 0x00000004, + 'NEGATE_BIT_ATI': 0x00000004, + 'BIAS_BIT_ATI': 0x00000008, + 'HALF_BIT_ATI': 0x00000008, + 'QUARTER_BIT_ATI': 0x00000010, + 'EIGHTH_BIT_ATI': 0x00000020, + 'SATURATE_BIT_ATI': 0x00000040, + 'FRAGMENT_SHADER_ATI': 0x8920, + 'REG_0_ATI': 0x8921, + 'REG_1_ATI': 0x8922, + 'REG_2_ATI': 0x8923, + 'REG_3_ATI': 0x8924, + 'REG_4_ATI': 0x8925, + 'REG_5_ATI': 0x8926, + 'CON_0_ATI': 0x8941, + 'CON_1_ATI': 0x8942, + 'CON_2_ATI': 0x8943, + 'CON_3_ATI': 0x8944, + 'CON_4_ATI': 0x8945, + 'CON_5_ATI': 0x8946, + 'CON_6_ATI': 0x8947, + 'CON_7_ATI': 0x8948, + 'MOV_ATI': 0x8961, + 'ADD_ATI': 0x8963, + 'MUL_ATI': 0x8964, + 'SUB_ATI': 0x8965, + 'DOT3_ATI': 0x8966, + 'DOT4_ATI': 0x8967, + 'MAD_ATI': 0x8968, + 'LERP_ATI': 0x8969, + 'CND_ATI': 0x896A, + 'CND0_ATI': 0x896B, + 'DOT2_ADD_ATI': 0x896C, + 'SECONDARY_INTERPOLATOR_ATI': 0x896D, + 'NUM_FRAGMENT_REGISTERS_ATI': 0x896E, + 'NUM_FRAGMENT_CONSTANTS_ATI': 0x896F, + 'NUM_PASSES_ATI': 0x8970, + 'NUM_INSTRUCTIONS_PER_PASS_ATI': 0x8971, + 'NUM_INSTRUCTIONS_TOTAL_ATI': 0x8972, + 'NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI': 0x8973, + 'NUM_LOOPBACK_COMPONENTS_ATI': 0x8974, + 'COLOR_ALPHA_PAIRING_ATI': 0x8975, + 'SWIZZLE_STR_ATI': 0x8976, + 'SWIZZLE_STQ_ATI': 0x8977, + 'SWIZZLE_STR_DR_ATI': 0x8978, + 'SWIZZLE_STQ_DQ_ATI': 0x8979, + 'SWIZZLE_STRQ_ATI': 0x897A, + 'SWIZZLE_STRQ_DQ_ATI': 0x897B, + 'PN_TRIANGLES_ATI': 0x87F0, + 'MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI': 0x87F1, + 'PN_TRIANGLES_POINT_MODE_ATI': 0x87F2, + 'PN_TRIANGLES_NORMAL_MODE_ATI': 0x87F3, + 'PN_TRIANGLES_TESSELATION_LEVEL_ATI': 0x87F4, + 'PN_TRIANGLES_POINT_MODE_LINEAR_ATI': 0x87F5, + 'PN_TRIANGLES_POINT_MODE_CUBIC_ATI': 0x87F6, + 'PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI': 0x87F7, + 'PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI': 0x87F8, + 'STENCIL_BACK_FUNC_ATI': 0x8800, + 'STENCIL_BACK_FAIL_ATI': 0x8801, + 'STENCIL_BACK_PASS_DEPTH_FAIL_ATI': 0x8802, + 'STENCIL_BACK_PASS_DEPTH_PASS_ATI': 0x8803, + 'TEXT_FRAGMENT_SHADER_ATI': 0x8200, + 'COMPRESSED_LUMINANCE_ALPHA_3DC_ATI': 0x8837, + 'MODULATE_ADD_ATI': 0x8744, + 'MODULATE_SIGNED_ADD_ATI': 0x8745, + 'MODULATE_SUBTRACT_ATI': 0x8746, + 'RGBA_FLOAT32_ATI': 0x8814, + 'RGB_FLOAT32_ATI': 0x8815, + 'ALPHA_FLOAT32_ATI': 0x8816, + 'INTENSITY_FLOAT32_ATI': 0x8817, + 'LUMINANCE_FLOAT32_ATI': 0x8818, + 'LUMINANCE_ALPHA_FLOAT32_ATI': 0x8819, + 'RGBA_FLOAT16_ATI': 0x881A, + 'RGB_FLOAT16_ATI': 0x881B, + 'ALPHA_FLOAT16_ATI': 0x881C, + 'INTENSITY_FLOAT16_ATI': 0x881D, + 'LUMINANCE_FLOAT16_ATI': 0x881E, + 'LUMINANCE_ALPHA_FLOAT16_ATI': 0x881F, + 'MIRROR_CLAMP_ATI': 0x8742, + 'MIRROR_CLAMP_TO_EDGE_ATI': 0x8743, + 'STATIC_ATI': 0x8760, + 'DYNAMIC_ATI': 0x8761, + 'PRESERVE_ATI': 0x8762, + 'DISCARD_ATI': 0x8763, + 'OBJECT_BUFFER_SIZE_ATI': 0x8764, + 'OBJECT_BUFFER_USAGE_ATI': 0x8765, + 'ARRAY_OBJECT_BUFFER_ATI': 0x8766, + 'ARRAY_OBJECT_OFFSET_ATI': 0x8767, + 'MAX_VERTEX_STREAMS_ATI': 0x876B, + 'VERTEX_SOURCE_ATI': 0x876C, + 'VERTEX_STREAM0_ATI': 0x876D, + 'VERTEX_STREAM1_ATI': 0x876E, + 'VERTEX_STREAM2_ATI': 0x876F, + 'VERTEX_STREAM3_ATI': 0x8770, + 'VERTEX_STREAM4_ATI': 0x8771, + 'VERTEX_STREAM5_ATI': 0x8772, + 'VERTEX_STREAM6_ATI': 0x8773, + 'VERTEX_STREAM7_ATI': 0x8774, + '422_EXT': 0x80CC, + '422_REV_EXT': 0x80CD, + '422_AVERAGE_EXT': 0x80CE, + '422_REV_AVERAGE_EXT': 0x80CF, + 'CG_VERTEX_SHADER_EXT': 0x890E, + 'CG_FRAGMENT_SHADER_EXT': 0x890F, + 'ABGR_EXT': 0x8000, + 'BGR_EXT': 0x80E0, + 'BGRA_EXT': 0x80E1, + 'MAX_VERTEX_BINDABLE_UNIFORMS_EXT': 0x8DE2, + 'MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT': 0x8DE3, + 'MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT': 0x8DE4, + 'MAX_BINDABLE_UNIFORM_SIZE_EXT': 0x8DED, + 'UNIFORM_BUFFER_EXT': 0x8DEE, + 'UNIFORM_BUFFER_BINDING_EXT': 0x8DEF, + 'CONSTANT_COLOR_EXT': 0x8001, + 'ONE_MINUS_CONSTANT_COLOR_EXT': 0x8002, + 'CONSTANT_ALPHA_EXT': 0x8003, + 'ONE_MINUS_CONSTANT_ALPHA_EXT': 0x8004, + 'BLEND_COLOR_EXT': 0x8005, + 'BLEND_EQUATION_RGB_EXT': 0x8009, + 'BLEND_EQUATION_ALPHA_EXT': 0x883D, + 'BLEND_DST_RGB_EXT': 0x80C8, + 'BLEND_SRC_RGB_EXT': 0x80C9, + 'BLEND_DST_ALPHA_EXT': 0x80CA, + 'BLEND_SRC_ALPHA_EXT': 0x80CB, + 'FUNC_ADD_EXT': 0x8006, + 'MIN_EXT': 0x8007, + 'MAX_EXT': 0x8008, + 'BLEND_EQUATION_EXT': 0x8009, + 'FUNC_SUBTRACT_EXT': 0x800A, + 'FUNC_REVERSE_SUBTRACT_EXT': 0x800B, + 'CLIP_VOLUME_CLIPPING_HINT_EXT': 0x80F0, + 'CMYK_EXT': 0x800C, + 'CMYKA_EXT': 0x800D, + 'PACK_CMYK_HINT_EXT': 0x800E, + 'UNPACK_CMYK_HINT_EXT': 0x800F, + 'ARRAY_ELEMENT_LOCK_FIRST_EXT': 0x81A8, + 'ARRAY_ELEMENT_LOCK_COUNT_EXT': 0x81A9, + 'CONVOLUTION_1D_EXT': 0x8010, + 'CONVOLUTION_2D_EXT': 0x8011, + 'SEPARABLE_2D_EXT': 0x8012, + 'CONVOLUTION_BORDER_MODE_EXT': 0x8013, + 'CONVOLUTION_FILTER_SCALE_EXT': 0x8014, + 'CONVOLUTION_FILTER_BIAS_EXT': 0x8015, + 'REDUCE_EXT': 0x8016, + 'CONVOLUTION_FORMAT_EXT': 0x8017, + 'CONVOLUTION_WIDTH_EXT': 0x8018, + 'CONVOLUTION_HEIGHT_EXT': 0x8019, + 'MAX_CONVOLUTION_WIDTH_EXT': 0x801A, + 'MAX_CONVOLUTION_HEIGHT_EXT': 0x801B, + 'POST_CONVOLUTION_RED_SCALE_EXT': 0x801C, + 'POST_CONVOLUTION_GREEN_SCALE_EXT': 0x801D, + 'POST_CONVOLUTION_BLUE_SCALE_EXT': 0x801E, + 'POST_CONVOLUTION_ALPHA_SCALE_EXT': 0x801F, + 'POST_CONVOLUTION_RED_BIAS_EXT': 0x8020, + 'POST_CONVOLUTION_GREEN_BIAS_EXT': 0x8021, + 'POST_CONVOLUTION_BLUE_BIAS_EXT': 0x8022, + 'POST_CONVOLUTION_ALPHA_BIAS_EXT': 0x8023, + 'TANGENT_ARRAY_EXT': 0x8439, + 'BINORMAL_ARRAY_EXT': 0x843A, + 'CURRENT_TANGENT_EXT': 0x843B, + 'CURRENT_BINORMAL_EXT': 0x843C, + 'TANGENT_ARRAY_TYPE_EXT': 0x843E, + 'TANGENT_ARRAY_STRIDE_EXT': 0x843F, + 'BINORMAL_ARRAY_TYPE_EXT': 0x8440, + 'BINORMAL_ARRAY_STRIDE_EXT': 0x8441, + 'TANGENT_ARRAY_POINTER_EXT': 0x8442, + 'BINORMAL_ARRAY_POINTER_EXT': 0x8443, + 'MAP1_TANGENT_EXT': 0x8444, + 'MAP2_TANGENT_EXT': 0x8445, + 'MAP1_BINORMAL_EXT': 0x8446, + 'MAP2_BINORMAL_EXT': 0x8447, + 'DEPTH_BOUNDS_TEST_EXT': 0x8890, + 'DEPTH_BOUNDS_EXT': 0x8891, + 'PROGRAM_MATRIX_EXT': 0x8E2D, + 'TRANSPOSE_PROGRAM_MATRIX_EXT': 0x8E2E, + 'PROGRAM_MATRIX_STACK_DEPTH_EXT': 0x8E2F, + 'MAX_ELEMENTS_VERTICES': 0x80E8, + 'MAX_ELEMENTS_INDICES': 0x80E9, + 'FOG_COORDINATE_SOURCE_EXT': 0x8450, + 'FOG_COORDINATE_EXT': 0x8451, + 'FRAGMENT_DEPTH_EXT': 0x8452, + 'CURRENT_FOG_COORDINATE_EXT': 0x8453, + 'FOG_COORDINATE_ARRAY_TYPE_EXT': 0x8454, + 'FOG_COORDINATE_ARRAY_STRIDE_EXT': 0x8455, + 'FOG_COORDINATE_ARRAY_POINTER_EXT': 0x8456, + 'FOG_COORDINATE_ARRAY_EXT': 0x8457, + 'FRAGMENT_LIGHTING_EXT': 0x8400, + 'FRAGMENT_COLOR_MATERIAL_EXT': 0x8401, + 'FRAGMENT_COLOR_MATERIAL_FACE_EXT': 0x8402, + 'FRAGMENT_COLOR_MATERIAL_PARAMETER_EXT': 0x8403, + 'MAX_FRAGMENT_LIGHTS_EXT': 0x8404, + 'MAX_ACTIVE_LIGHTS_EXT': 0x8405, + 'CURRENT_RASTER_NORMAL_EXT': 0x8406, + 'LIGHT_ENV_MODE_EXT': 0x8407, + 'FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_EXT': 0x8408, + 'FRAGMENT_LIGHT_MODEL_TWO_SIDE_EXT': 0x8409, + 'FRAGMENT_LIGHT_MODEL_AMBIENT_EXT': 0x840A, + 'FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_EXT': 0x840B, + 'FRAGMENT_LIGHT0_EXT': 0x840C, + 'FRAGMENT_LIGHT7_EXT': 0x8413, + 'DRAW_FRAMEBUFFER_BINDING_EXT': 0x8CA6, + 'READ_FRAMEBUFFER_EXT': 0x8CA8, + 'DRAW_FRAMEBUFFER_EXT': 0x8CA9, + 'READ_FRAMEBUFFER_BINDING_EXT': 0x8CAA, + 'RENDERBUFFER_SAMPLES_EXT': 0x8CAB, + 'FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT': 0x8D56, + 'MAX_SAMPLES_EXT': 0x8D57, + 'INVALID_FRAMEBUFFER_OPERATION_EXT': 0x0506, + 'MAX_RENDERBUFFER_SIZE_EXT': 0x84E8, + 'FRAMEBUFFER_BINDING_EXT': 0x8CA6, + 'RENDERBUFFER_BINDING_EXT': 0x8CA7, + 'FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT': 0x8CD0, + 'FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT': 0x8CD1, + 'FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT': 0x8CD2, + 'FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT': 0x8CD3, + 'FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT': 0x8CD4, + 'FRAMEBUFFER_COMPLETE_EXT': 0x8CD5, + 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT': 0x8CD6, + 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT': 0x8CD7, + 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT': 0x8CD9, + 'FRAMEBUFFER_INCOMPLETE_FORMATS_EXT': 0x8CDA, + 'FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT': 0x8CDB, + 'FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT': 0x8CDC, + 'FRAMEBUFFER_UNSUPPORTED_EXT': 0x8CDD, + 'MAX_COLOR_ATTACHMENTS_EXT': 0x8CDF, + 'COLOR_ATTACHMENT0_EXT': 0x8CE0, + 'COLOR_ATTACHMENT1_EXT': 0x8CE1, + 'COLOR_ATTACHMENT2_EXT': 0x8CE2, + 'COLOR_ATTACHMENT3_EXT': 0x8CE3, + 'COLOR_ATTACHMENT4_EXT': 0x8CE4, + 'COLOR_ATTACHMENT5_EXT': 0x8CE5, + 'COLOR_ATTACHMENT6_EXT': 0x8CE6, + 'COLOR_ATTACHMENT7_EXT': 0x8CE7, + 'COLOR_ATTACHMENT8_EXT': 0x8CE8, + 'COLOR_ATTACHMENT9_EXT': 0x8CE9, + 'COLOR_ATTACHMENT10_EXT': 0x8CEA, + 'COLOR_ATTACHMENT11_EXT': 0x8CEB, + 'COLOR_ATTACHMENT12_EXT': 0x8CEC, + 'COLOR_ATTACHMENT13_EXT': 0x8CED, + 'COLOR_ATTACHMENT14_EXT': 0x8CEE, + 'COLOR_ATTACHMENT15_EXT': 0x8CEF, + 'DEPTH_ATTACHMENT_EXT': 0x8D00, + 'STENCIL_ATTACHMENT_EXT': 0x8D20, + 'FRAMEBUFFER_EXT': 0x8D40, + 'RENDERBUFFER_EXT': 0x8D41, + 'RENDERBUFFER_WIDTH_EXT': 0x8D42, + 'RENDERBUFFER_HEIGHT_EXT': 0x8D43, + 'RENDERBUFFER_INTERNAL_FORMAT_EXT': 0x8D44, + 'STENCIL_INDEX1_EXT': 0x8D46, + 'STENCIL_INDEX4_EXT': 0x8D47, + 'STENCIL_INDEX8_EXT': 0x8D48, + 'STENCIL_INDEX16_EXT': 0x8D49, + 'RENDERBUFFER_RED_SIZE_EXT': 0x8D50, + 'RENDERBUFFER_GREEN_SIZE_EXT': 0x8D51, + 'RENDERBUFFER_BLUE_SIZE_EXT': 0x8D52, + 'RENDERBUFFER_ALPHA_SIZE_EXT': 0x8D53, + 'RENDERBUFFER_DEPTH_SIZE_EXT': 0x8D54, + 'RENDERBUFFER_STENCIL_SIZE_EXT': 0x8D55, + 'FRAMEBUFFER_SRGB_EXT': 0x8DB9, + 'FRAMEBUFFER_SRGB_CAPABLE_EXT': 0x8DBA, + 'LINES_ADJACENCY_EXT': 0xA, + 'LINE_STRIP_ADJACENCY_EXT': 0xB, + 'TRIANGLES_ADJACENCY_EXT': 0xC, + 'TRIANGLE_STRIP_ADJACENCY_EXT': 0xD, + 'PROGRAM_POINT_SIZE_EXT': 0x8642, + 'MAX_VARYING_COMPONENTS_EXT': 0x8B4B, + 'MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT': 0x8C29, + 'FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT': 0x8CD4, + 'FRAMEBUFFER_ATTACHMENT_LAYERED_EXT': 0x8DA7, + 'FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT': 0x8DA8, + 'FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT': 0x8DA9, + 'GEOMETRY_SHADER_EXT': 0x8DD9, + 'GEOMETRY_VERTICES_OUT_EXT': 0x8DDA, + 'GEOMETRY_INPUT_TYPE_EXT': 0x8DDB, + 'GEOMETRY_OUTPUT_TYPE_EXT': 0x8DDC, + 'MAX_GEOMETRY_VARYING_COMPONENTS_EXT': 0x8DDD, + 'MAX_VERTEX_VARYING_COMPONENTS_EXT': 0x8DDE, + 'MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT': 0x8DDF, + 'MAX_GEOMETRY_OUTPUT_VERTICES_EXT': 0x8DE0, + 'MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT': 0x8DE1, + 'VERTEX_ATTRIB_ARRAY_INTEGER_EXT': 0x88FD, + 'SAMPLER_1D_ARRAY_EXT': 0x8DC0, + 'SAMPLER_2D_ARRAY_EXT': 0x8DC1, + 'SAMPLER_BUFFER_EXT': 0x8DC2, + 'SAMPLER_1D_ARRAY_SHADOW_EXT': 0x8DC3, + 'SAMPLER_2D_ARRAY_SHADOW_EXT': 0x8DC4, + 'SAMPLER_CUBE_SHADOW_EXT': 0x8DC5, + 'UNSIGNED_INT_VEC2_EXT': 0x8DC6, + 'UNSIGNED_INT_VEC3_EXT': 0x8DC7, + 'UNSIGNED_INT_VEC4_EXT': 0x8DC8, + 'INT_SAMPLER_1D_EXT': 0x8DC9, + 'INT_SAMPLER_2D_EXT': 0x8DCA, + 'INT_SAMPLER_3D_EXT': 0x8DCB, + 'INT_SAMPLER_CUBE_EXT': 0x8DCC, + 'INT_SAMPLER_2D_RECT_EXT': 0x8DCD, + 'INT_SAMPLER_1D_ARRAY_EXT': 0x8DCE, + 'INT_SAMPLER_2D_ARRAY_EXT': 0x8DCF, + 'INT_SAMPLER_BUFFER_EXT': 0x8DD0, + 'UNSIGNED_INT_SAMPLER_1D_EXT': 0x8DD1, + 'UNSIGNED_INT_SAMPLER_2D_EXT': 0x8DD2, + 'UNSIGNED_INT_SAMPLER_3D_EXT': 0x8DD3, + 'UNSIGNED_INT_SAMPLER_CUBE_EXT': 0x8DD4, + 'UNSIGNED_INT_SAMPLER_2D_RECT_EXT': 0x8DD5, + 'UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT': 0x8DD6, + 'UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT': 0x8DD7, + 'UNSIGNED_INT_SAMPLER_BUFFER_EXT': 0x8DD8, + 'HISTOGRAM_EXT': 0x8024, + 'PROXY_HISTOGRAM_EXT': 0x8025, + 'HISTOGRAM_WIDTH_EXT': 0x8026, + 'HISTOGRAM_FORMAT_EXT': 0x8027, + 'HISTOGRAM_RED_SIZE_EXT': 0x8028, + 'HISTOGRAM_GREEN_SIZE_EXT': 0x8029, + 'HISTOGRAM_BLUE_SIZE_EXT': 0x802A, + 'HISTOGRAM_ALPHA_SIZE_EXT': 0x802B, + 'HISTOGRAM_LUMINANCE_SIZE_EXT': 0x802C, + 'HISTOGRAM_SINK_EXT': 0x802D, + 'MINMAX_EXT': 0x802E, + 'MINMAX_FORMAT_EXT': 0x802F, + 'MINMAX_SINK_EXT': 0x8030, + 'FRAGMENT_MATERIAL_EXT': 0x8349, + 'FRAGMENT_NORMAL_EXT': 0x834A, + 'FRAGMENT_COLOR_EXT': 0x834C, + 'ATTENUATION_EXT': 0x834D, + 'SHADOW_ATTENUATION_EXT': 0x834E, + 'TEXTURE_APPLICATION_MODE_EXT': 0x834F, + 'TEXTURE_LIGHT_EXT': 0x8350, + 'TEXTURE_MATERIAL_FACE_EXT': 0x8351, + 'TEXTURE_MATERIAL_PARAMETER_EXT': 0x8352, + 'FRAGMENT_DEPTH_EXT': 0x8452, + 'MULTISAMPLE_EXT': 0x809D, + 'SAMPLE_ALPHA_TO_MASK_EXT': 0x809E, + 'SAMPLE_ALPHA_TO_ONE_EXT': 0x809F, + 'SAMPLE_MASK_EXT': 0x80A0, + '1PASS_EXT': 0x80A1, + '2PASS_0_EXT': 0x80A2, + '2PASS_1_EXT': 0x80A3, + '4PASS_0_EXT': 0x80A4, + '4PASS_1_EXT': 0x80A5, + '4PASS_2_EXT': 0x80A6, + '4PASS_3_EXT': 0x80A7, + 'SAMPLE_BUFFERS_EXT': 0x80A8, + 'SAMPLES_EXT': 0x80A9, + 'SAMPLE_MASK_VALUE_EXT': 0x80AA, + 'SAMPLE_MASK_INVERT_EXT': 0x80AB, + 'SAMPLE_PATTERN_EXT': 0x80AC, + 'MULTISAMPLE_BIT_EXT': 0x20000000, + 'DEPTH_STENCIL_EXT': 0x84F9, + 'UNSIGNED_INT_24_8_EXT': 0x84FA, + 'DEPTH24_STENCIL8_EXT': 0x88F0, + 'TEXTURE_STENCIL_SIZE_EXT': 0x88F1, + 'R11F_G11F_B10F_EXT': 0x8C3A, + 'UNSIGNED_INT_10F_11F_11F_REV_EXT': 0x8C3B, + 'RGBA_SIGNED_COMPONENTS_EXT': 0x8C3C, + 'UNSIGNED_BYTE_3_3_2_EXT': 0x8032, + 'UNSIGNED_SHORT_4_4_4_4_EXT': 0x8033, + 'UNSIGNED_SHORT_5_5_5_1_EXT': 0x8034, + 'UNSIGNED_INT_8_8_8_8_EXT': 0x8035, + 'UNSIGNED_INT_10_10_10_2_EXT': 0x8036, + 'TEXTURE_1D': 0x0DE0, + 'TEXTURE_2D': 0x0DE1, + 'PROXY_TEXTURE_1D': 0x8063, + 'PROXY_TEXTURE_2D': 0x8064, + 'TEXTURE_3D_EXT': 0x806F, + 'PROXY_TEXTURE_3D_EXT': 0x8070, + 'COLOR_TABLE_FORMAT_EXT': 0x80D8, + 'COLOR_TABLE_WIDTH_EXT': 0x80D9, + 'COLOR_TABLE_RED_SIZE_EXT': 0x80DA, + 'COLOR_TABLE_GREEN_SIZE_EXT': 0x80DB, + 'COLOR_TABLE_BLUE_SIZE_EXT': 0x80DC, + 'COLOR_TABLE_ALPHA_SIZE_EXT': 0x80DD, + 'COLOR_TABLE_LUMINANCE_SIZE_EXT': 0x80DE, + 'COLOR_TABLE_INTENSITY_SIZE_EXT': 0x80DF, + 'COLOR_INDEX1_EXT': 0x80E2, + 'COLOR_INDEX2_EXT': 0x80E3, + 'COLOR_INDEX4_EXT': 0x80E4, + 'COLOR_INDEX8_EXT': 0x80E5, + 'COLOR_INDEX12_EXT': 0x80E6, + 'COLOR_INDEX16_EXT': 0x80E7, + 'TEXTURE_INDEX_SIZE_EXT': 0x80ED, + 'TEXTURE_CUBE_MAP_ARB': 0x8513, + 'PROXY_TEXTURE_CUBE_MAP_ARB': 0x851B, + 'PIXEL_PACK_BUFFER_EXT': 0x88EB, + 'PIXEL_UNPACK_BUFFER_EXT': 0x88EC, + 'PIXEL_PACK_BUFFER_BINDING_EXT': 0x88ED, + 'PIXEL_UNPACK_BUFFER_BINDING_EXT': 0x88EF, + 'PIXEL_TRANSFORM_2D_EXT': 0x8330, + 'PIXEL_MAG_FILTER_EXT': 0x8331, + 'PIXEL_MIN_FILTER_EXT': 0x8332, + 'PIXEL_CUBIC_WEIGHT_EXT': 0x8333, + 'CUBIC_EXT': 0x8334, + 'AVERAGE_EXT': 0x8335, + 'PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT': 0x8336, + 'MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT': 0x8337, + 'PIXEL_TRANSFORM_2D_MATRIX_EXT': 0x8338, + 'POINT_SIZE_MIN_EXT': 0x8126, + 'POINT_SIZE_MAX_EXT': 0x8127, + 'POINT_FADE_THRESHOLD_SIZE_EXT': 0x8128, + 'DISTANCE_ATTENUATION_EXT': 0x8129, + 'POLYGON_OFFSET_EXT': 0x8037, + 'POLYGON_OFFSET_FACTOR_EXT': 0x8038, + 'POLYGON_OFFSET_BIAS_EXT': 0x8039, + 'RESCALE_NORMAL_EXT': 0x803A, + 'COLOR_SUM_EXT': 0x8458, + 'CURRENT_SECONDARY_COLOR_EXT': 0x8459, + 'SECONDARY_COLOR_ARRAY_SIZE_EXT': 0x845A, + 'SECONDARY_COLOR_ARRAY_TYPE_EXT': 0x845B, + 'SECONDARY_COLOR_ARRAY_STRIDE_EXT': 0x845C, + 'SECONDARY_COLOR_ARRAY_POINTER_EXT': 0x845D, + 'SECONDARY_COLOR_ARRAY_EXT': 0x845E, + 'LIGHT_MODEL_COLOR_CONTROL_EXT': 0x81F8, + 'SINGLE_COLOR_EXT': 0x81F9, + 'SEPARATE_SPECULAR_COLOR_EXT': 0x81FA, + 'SHARED_TEXTURE_PALETTE_EXT': 0x81FB, + 'STENCIL_TAG_BITS_EXT': 0x88F2, + 'STENCIL_CLEAR_TAG_VALUE_EXT': 0x88F3, + 'STENCIL_TEST_TWO_SIDE_EXT': 0x8910, + 'ACTIVE_STENCIL_FACE_EXT': 0x8911, + 'INCR_WRAP_EXT': 0x8507, + 'DECR_WRAP_EXT': 0x8508, + 'ALPHA4_EXT': 0x803B, + 'ALPHA8_EXT': 0x803C, + 'ALPHA12_EXT': 0x803D, + 'ALPHA16_EXT': 0x803E, + 'LUMINANCE4_EXT': 0x803F, + 'LUMINANCE8_EXT': 0x8040, + 'LUMINANCE12_EXT': 0x8041, + 'LUMINANCE16_EXT': 0x8042, + 'LUMINANCE4_ALPHA4_EXT': 0x8043, + 'LUMINANCE6_ALPHA2_EXT': 0x8044, + 'LUMINANCE8_ALPHA8_EXT': 0x8045, + 'LUMINANCE12_ALPHA4_EXT': 0x8046, + 'LUMINANCE12_ALPHA12_EXT': 0x8047, + 'LUMINANCE16_ALPHA16_EXT': 0x8048, + 'INTENSITY_EXT': 0x8049, + 'INTENSITY4_EXT': 0x804A, + 'INTENSITY8_EXT': 0x804B, + 'INTENSITY12_EXT': 0x804C, + 'INTENSITY16_EXT': 0x804D, + 'RGB2_EXT': 0x804E, + 'RGB4_EXT': 0x804F, + 'RGB5_EXT': 0x8050, + 'RGB8_EXT': 0x8051, + 'RGB10_EXT': 0x8052, + 'RGB12_EXT': 0x8053, + 'RGB16_EXT': 0x8054, + 'RGBA2_EXT': 0x8055, + 'RGBA4_EXT': 0x8056, + 'RGB5_A1_EXT': 0x8057, + 'RGBA8_EXT': 0x8058, + 'RGB10_A2_EXT': 0x8059, + 'RGBA12_EXT': 0x805A, + 'RGBA16_EXT': 0x805B, + 'TEXTURE_RED_SIZE_EXT': 0x805C, + 'TEXTURE_GREEN_SIZE_EXT': 0x805D, + 'TEXTURE_BLUE_SIZE_EXT': 0x805E, + 'TEXTURE_ALPHA_SIZE_EXT': 0x805F, + 'TEXTURE_LUMINANCE_SIZE_EXT': 0x8060, + 'TEXTURE_INTENSITY_SIZE_EXT': 0x8061, + 'REPLACE_EXT': 0x8062, + 'PROXY_TEXTURE_1D_EXT': 0x8063, + 'PROXY_TEXTURE_2D_EXT': 0x8064, + 'PACK_SKIP_IMAGES_EXT': 0x806B, + 'PACK_IMAGE_HEIGHT_EXT': 0x806C, + 'UNPACK_SKIP_IMAGES_EXT': 0x806D, + 'UNPACK_IMAGE_HEIGHT_EXT': 0x806E, + 'TEXTURE_3D_EXT': 0x806F, + 'PROXY_TEXTURE_3D_EXT': 0x8070, + 'TEXTURE_DEPTH_EXT': 0x8071, + 'TEXTURE_WRAP_R_EXT': 0x8072, + 'MAX_3D_TEXTURE_SIZE_EXT': 0x8073, + 'COMPARE_REF_DEPTH_TO_TEXTURE_EXT': 0x884E, + 'MAX_ARRAY_TEXTURE_LAYERS_EXT': 0x88FF, + 'TEXTURE_1D_ARRAY_EXT': 0x8C18, + 'PROXY_TEXTURE_1D_ARRAY_EXT': 0x8C19, + 'TEXTURE_2D_ARRAY_EXT': 0x8C1A, + 'PROXY_TEXTURE_2D_ARRAY_EXT': 0x8C1B, + 'TEXTURE_BINDING_1D_ARRAY_EXT': 0x8C1C, + 'TEXTURE_BINDING_2D_ARRAY_EXT': 0x8C1D, + 'TEXTURE_BUFFER_EXT': 0x8C2A, + 'MAX_TEXTURE_BUFFER_SIZE_EXT': 0x8C2B, + 'TEXTURE_BINDING_BUFFER_EXT': 0x8C2C, + 'TEXTURE_BUFFER_DATA_STORE_BINDING_EXT': 0x8C2D, + 'TEXTURE_BUFFER_FORMAT_EXT': 0x8C2E, + 'COMPRESSED_RGB_S3TC_DXT1_EXT': 0x83F0, + 'COMPRESSED_RGBA_S3TC_DXT1_EXT': 0x83F1, + 'COMPRESSED_LUMINANCE_LATC1_EXT': 0x8C70, + 'COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT': 0x8C71, + 'COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT': 0x8C72, + 'COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT': 0x8C73, + 'COMPRESSED_RED_RGTC1_EXT': 0x8DBB, + 'COMPRESSED_SIGNED_RED_RGTC1_EXT': 0x8DBC, + 'COMPRESSED_RED_GREEN_RGTC2_EXT': 0x8DBD, + 'COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT': 0x8DBE, + 'COMPRESSED_RGB_S3TC_DXT1_EXT': 0x83F0, + 'COMPRESSED_RGBA_S3TC_DXT1_EXT': 0x83F1, + 'COMPRESSED_RGBA_S3TC_DXT3_EXT': 0x83F2, + 'COMPRESSED_RGBA_S3TC_DXT5_EXT': 0x83F3, + 'NORMAL_MAP_EXT': 0x8511, + 'REFLECTION_MAP_EXT': 0x8512, + 'TEXTURE_CUBE_MAP_EXT': 0x8513, + 'TEXTURE_BINDING_CUBE_MAP_EXT': 0x8514, + 'TEXTURE_CUBE_MAP_POSITIVE_X_EXT': 0x8515, + 'TEXTURE_CUBE_MAP_NEGATIVE_X_EXT': 0x8516, + 'TEXTURE_CUBE_MAP_POSITIVE_Y_EXT': 0x8517, + 'TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT': 0x8518, + 'TEXTURE_CUBE_MAP_POSITIVE_Z_EXT': 0x8519, + 'TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT': 0x851A, + 'PROXY_TEXTURE_CUBE_MAP_EXT': 0x851B, + 'MAX_CUBE_MAP_TEXTURE_SIZE_EXT': 0x851C, + 'CLAMP_TO_EDGE_EXT': 0x812F, + 'COMBINE_EXT': 0x8570, + 'COMBINE_RGB_EXT': 0x8571, + 'COMBINE_ALPHA_EXT': 0x8572, + 'RGB_SCALE_EXT': 0x8573, + 'ADD_SIGNED_EXT': 0x8574, + 'INTERPOLATE_EXT': 0x8575, + 'CONSTANT_EXT': 0x8576, + 'PRIMARY_COLOR_EXT': 0x8577, + 'PREVIOUS_EXT': 0x8578, + 'SOURCE0_RGB_EXT': 0x8580, + 'SOURCE1_RGB_EXT': 0x8581, + 'SOURCE2_RGB_EXT': 0x8582, + 'SOURCE0_ALPHA_EXT': 0x8588, + 'SOURCE1_ALPHA_EXT': 0x8589, + 'SOURCE2_ALPHA_EXT': 0x858A, + 'OPERAND0_RGB_EXT': 0x8590, + 'OPERAND1_RGB_EXT': 0x8591, + 'OPERAND2_RGB_EXT': 0x8592, + 'OPERAND0_ALPHA_EXT': 0x8598, + 'OPERAND1_ALPHA_EXT': 0x8599, + 'OPERAND2_ALPHA_EXT': 0x859A, + 'DOT3_RGB_EXT': 0x8740, + 'DOT3_RGBA_EXT': 0x8741, + 'TEXTURE_MAX_ANISOTROPY_EXT': 0x84FE, + 'MAX_TEXTURE_MAX_ANISOTROPY_EXT': 0x84FF, + 'RGBA32UI_EXT': 0x8D70, + 'RGB32UI_EXT': 0x8D71, + 'ALPHA32UI_EXT': 0x8D72, + 'INTENSITY32UI_EXT': 0x8D73, + 'LUMINANCE32UI_EXT': 0x8D74, + 'LUMINANCE_ALPHA32UI_EXT': 0x8D75, + 'RGBA16UI_EXT': 0x8D76, + 'RGB16UI_EXT': 0x8D77, + 'ALPHA16UI_EXT': 0x8D78, + 'INTENSITY16UI_EXT': 0x8D79, + 'LUMINANCE16UI_EXT': 0x8D7A, + 'LUMINANCE_ALPHA16UI_EXT': 0x8D7B, + 'RGBA8UI_EXT': 0x8D7C, + 'RGB8UI_EXT': 0x8D7D, + 'ALPHA8UI_EXT': 0x8D7E, + 'INTENSITY8UI_EXT': 0x8D7F, + 'LUMINANCE8UI_EXT': 0x8D80, + 'LUMINANCE_ALPHA8UI_EXT': 0x8D81, + 'RGBA32I_EXT': 0x8D82, + 'RGB32I_EXT': 0x8D83, + 'ALPHA32I_EXT': 0x8D84, + 'INTENSITY32I_EXT': 0x8D85, + 'LUMINANCE32I_EXT': 0x8D86, + 'LUMINANCE_ALPHA32I_EXT': 0x8D87, + 'RGBA16I_EXT': 0x8D88, + 'RGB16I_EXT': 0x8D89, + 'ALPHA16I_EXT': 0x8D8A, + 'INTENSITY16I_EXT': 0x8D8B, + 'LUMINANCE16I_EXT': 0x8D8C, + 'LUMINANCE_ALPHA16I_EXT': 0x8D8D, + 'RGBA8I_EXT': 0x8D8E, + 'RGB8I_EXT': 0x8D8F, + 'ALPHA8I_EXT': 0x8D90, + 'INTENSITY8I_EXT': 0x8D91, + 'LUMINANCE8I_EXT': 0x8D92, + 'LUMINANCE_ALPHA8I_EXT': 0x8D93, + 'RED_INTEGER_EXT': 0x8D94, + 'GREEN_INTEGER_EXT': 0x8D95, + 'BLUE_INTEGER_EXT': 0x8D96, + 'ALPHA_INTEGER_EXT': 0x8D97, + 'RGB_INTEGER_EXT': 0x8D98, + 'RGBA_INTEGER_EXT': 0x8D99, + 'BGR_INTEGER_EXT': 0x8D9A, + 'BGRA_INTEGER_EXT': 0x8D9B, + 'LUMINANCE_INTEGER_EXT': 0x8D9C, + 'LUMINANCE_ALPHA_INTEGER_EXT': 0x8D9D, + 'RGBA_INTEGER_MODE_EXT': 0x8D9E, + 'MAX_TEXTURE_LOD_BIAS_EXT': 0x84FD, + 'TEXTURE_FILTER_CONTROL_EXT': 0x8500, + 'TEXTURE_LOD_BIAS_EXT': 0x8501, + 'MIRROR_CLAMP_EXT': 0x8742, + 'MIRROR_CLAMP_TO_EDGE_EXT': 0x8743, + 'MIRROR_CLAMP_TO_BORDER_EXT': 0x8912, + 'TEXTURE_PRIORITY_EXT': 0x8066, + 'TEXTURE_RESIDENT_EXT': 0x8067, + 'TEXTURE_1D_BINDING_EXT': 0x8068, + 'TEXTURE_2D_BINDING_EXT': 0x8069, + 'TEXTURE_3D_BINDING_EXT': 0x806A, + 'PERTURB_EXT': 0x85AE, + 'TEXTURE_NORMAL_EXT': 0x85AF, + 'TEXTURE_RECTANGLE_EXT': 0x84F5, + 'TEXTURE_BINDING_RECTANGLE_EXT': 0x84F6, + 'PROXY_TEXTURE_RECTANGLE_EXT': 0x84F7, + 'MAX_RECTANGLE_TEXTURE_SIZE_EXT': 0x84F8, + 'SRGB_EXT': 0x8C40, + 'SRGB8_EXT': 0x8C41, + 'SRGB_ALPHA_EXT': 0x8C42, + 'SRGB8_ALPHA8_EXT': 0x8C43, + 'SLUMINANCE_ALPHA_EXT': 0x8C44, + 'SLUMINANCE8_ALPHA8_EXT': 0x8C45, + 'SLUMINANCE_EXT': 0x8C46, + 'SLUMINANCE8_EXT': 0x8C47, + 'COMPRESSED_SRGB_EXT': 0x8C48, + 'COMPRESSED_SRGB_ALPHA_EXT': 0x8C49, + 'COMPRESSED_SLUMINANCE_EXT': 0x8C4A, + 'COMPRESSED_SLUMINANCE_ALPHA_EXT': 0x8C4B, + 'COMPRESSED_SRGB_S3TC_DXT1_EXT': 0x8C4C, + 'COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT': 0x8C4D, + 'COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT': 0x8C4E, + 'COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT': 0x8C4F, + 'RGB9_E5_EXT': 0x8C3D, + 'UNSIGNED_INT_5_9_9_9_REV_EXT': 0x8C3E, + 'TEXTURE_SHARED_SIZE_EXT': 0x8C3F, + 'TEXTURE_SWIZZLE_R_EXT': 0x8E42, + 'TEXTURE_SWIZZLE_G_EXT': 0x8E43, + 'TEXTURE_SWIZZLE_B_EXT': 0x8E44, + 'TEXTURE_SWIZZLE_A_EXT': 0x8E45, + 'TEXTURE_SWIZZLE_RGBA_EXT': 0x8E46, + 'TIME_ELAPSED_EXT': 0x88BF, + 'TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT': 0x8C76, + 'TRANSFORM_FEEDBACK_BUFFER_MODE_EXT': 0x8C7F, + 'MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT': 0x8C80, + 'TRANSFORM_FEEDBACK_VARYINGS_EXT': 0x8C83, + 'TRANSFORM_FEEDBACK_BUFFER_START_EXT': 0x8C84, + 'TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT': 0x8C85, + 'PRIMITIVES_GENERATED_EXT': 0x8C87, + 'TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT': 0x8C88, + 'RASTERIZER_DISCARD_EXT': 0x8C89, + 'MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT': 0x8C8A, + 'MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT': 0x8C8B, + 'INTERLEAVED_ATTRIBS_EXT': 0x8C8C, + 'SEPARATE_ATTRIBS_EXT': 0x8C8D, + 'TRANSFORM_FEEDBACK_BUFFER_EXT': 0x8C8E, + 'TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT': 0x8C8F, + 'DOUBLE_EXT': 0x140A, + 'VERTEX_ARRAY_EXT': 0x8074, + 'NORMAL_ARRAY_EXT': 0x8075, + 'COLOR_ARRAY_EXT': 0x8076, + 'INDEX_ARRAY_EXT': 0x8077, + 'TEXTURE_COORD_ARRAY_EXT': 0x8078, + 'EDGE_FLAG_ARRAY_EXT': 0x8079, + 'VERTEX_ARRAY_SIZE_EXT': 0x807A, + 'VERTEX_ARRAY_TYPE_EXT': 0x807B, + 'VERTEX_ARRAY_STRIDE_EXT': 0x807C, + 'VERTEX_ARRAY_COUNT_EXT': 0x807D, + 'NORMAL_ARRAY_TYPE_EXT': 0x807E, + 'NORMAL_ARRAY_STRIDE_EXT': 0x807F, + 'NORMAL_ARRAY_COUNT_EXT': 0x8080, + 'COLOR_ARRAY_SIZE_EXT': 0x8081, + 'COLOR_ARRAY_TYPE_EXT': 0x8082, + 'COLOR_ARRAY_STRIDE_EXT': 0x8083, + 'COLOR_ARRAY_COUNT_EXT': 0x8084, + 'INDEX_ARRAY_TYPE_EXT': 0x8085, + 'INDEX_ARRAY_STRIDE_EXT': 0x8086, + 'INDEX_ARRAY_COUNT_EXT': 0x8087, + 'TEXTURE_COORD_ARRAY_SIZE_EXT': 0x8088, + 'TEXTURE_COORD_ARRAY_TYPE_EXT': 0x8089, + 'TEXTURE_COORD_ARRAY_STRIDE_EXT': 0x808A, + 'TEXTURE_COORD_ARRAY_COUNT_EXT': 0x808B, + 'EDGE_FLAG_ARRAY_STRIDE_EXT': 0x808C, + 'EDGE_FLAG_ARRAY_COUNT_EXT': 0x808D, + 'VERTEX_ARRAY_POINTER_EXT': 0x808E, + 'NORMAL_ARRAY_POINTER_EXT': 0x808F, + 'COLOR_ARRAY_POINTER_EXT': 0x8090, + 'INDEX_ARRAY_POINTER_EXT': 0x8091, + 'TEXTURE_COORD_ARRAY_POINTER_EXT': 0x8092, + 'EDGE_FLAG_ARRAY_POINTER_EXT': 0x8093, + 'BGRA': 0x80E1, + 'VERTEX_SHADER_EXT': 0x8780, + 'VERTEX_SHADER_BINDING_EXT': 0x8781, + 'OP_INDEX_EXT': 0x8782, + 'OP_NEGATE_EXT': 0x8783, + 'OP_DOT3_EXT': 0x8784, + 'OP_DOT4_EXT': 0x8785, + 'OP_MUL_EXT': 0x8786, + 'OP_ADD_EXT': 0x8787, + 'OP_MADD_EXT': 0x8788, + 'OP_FRAC_EXT': 0x8789, + 'OP_MAX_EXT': 0x878A, + 'OP_MIN_EXT': 0x878B, + 'OP_SET_GE_EXT': 0x878C, + 'OP_SET_LT_EXT': 0x878D, + 'OP_CLAMP_EXT': 0x878E, + 'OP_FLOOR_EXT': 0x878F, + 'OP_ROUND_EXT': 0x8790, + 'OP_EXP_BASE_2_EXT': 0x8791, + 'OP_LOG_BASE_2_EXT': 0x8792, + 'OP_POWER_EXT': 0x8793, + 'OP_RECIP_EXT': 0x8794, + 'OP_RECIP_SQRT_EXT': 0x8795, + 'OP_SUB_EXT': 0x8796, + 'OP_CROSS_PRODUCT_EXT': 0x8797, + 'OP_MULTIPLY_MATRIX_EXT': 0x8798, + 'OP_MOV_EXT': 0x8799, + 'OUTPUT_VERTEX_EXT': 0x879A, + 'OUTPUT_COLOR0_EXT': 0x879B, + 'OUTPUT_COLOR1_EXT': 0x879C, + 'OUTPUT_TEXTURE_COORD0_EXT': 0x879D, + 'OUTPUT_TEXTURE_COORD1_EXT': 0x879E, + 'OUTPUT_TEXTURE_COORD2_EXT': 0x879F, + 'OUTPUT_TEXTURE_COORD3_EXT': 0x87A0, + 'OUTPUT_TEXTURE_COORD4_EXT': 0x87A1, + 'OUTPUT_TEXTURE_COORD5_EXT': 0x87A2, + 'OUTPUT_TEXTURE_COORD6_EXT': 0x87A3, + 'OUTPUT_TEXTURE_COORD7_EXT': 0x87A4, + 'OUTPUT_TEXTURE_COORD8_EXT': 0x87A5, + 'OUTPUT_TEXTURE_COORD9_EXT': 0x87A6, + 'OUTPUT_TEXTURE_COORD10_EXT': 0x87A7, + 'OUTPUT_TEXTURE_COORD11_EXT': 0x87A8, + 'OUTPUT_TEXTURE_COORD12_EXT': 0x87A9, + 'OUTPUT_TEXTURE_COORD13_EXT': 0x87AA, + 'OUTPUT_TEXTURE_COORD14_EXT': 0x87AB, + 'OUTPUT_TEXTURE_COORD15_EXT': 0x87AC, + 'OUTPUT_TEXTURE_COORD16_EXT': 0x87AD, + 'OUTPUT_TEXTURE_COORD17_EXT': 0x87AE, + 'OUTPUT_TEXTURE_COORD18_EXT': 0x87AF, + 'OUTPUT_TEXTURE_COORD19_EXT': 0x87B0, + 'OUTPUT_TEXTURE_COORD20_EXT': 0x87B1, + 'OUTPUT_TEXTURE_COORD21_EXT': 0x87B2, + 'OUTPUT_TEXTURE_COORD22_EXT': 0x87B3, + 'OUTPUT_TEXTURE_COORD23_EXT': 0x87B4, + 'OUTPUT_TEXTURE_COORD24_EXT': 0x87B5, + 'OUTPUT_TEXTURE_COORD25_EXT': 0x87B6, + 'OUTPUT_TEXTURE_COORD26_EXT': 0x87B7, + 'OUTPUT_TEXTURE_COORD27_EXT': 0x87B8, + 'OUTPUT_TEXTURE_COORD28_EXT': 0x87B9, + 'OUTPUT_TEXTURE_COORD29_EXT': 0x87BA, + 'OUTPUT_TEXTURE_COORD30_EXT': 0x87BB, + 'OUTPUT_TEXTURE_COORD31_EXT': 0x87BC, + 'OUTPUT_FOG_EXT': 0x87BD, + 'SCALAR_EXT': 0x87BE, + 'VECTOR_EXT': 0x87BF, + 'MATRIX_EXT': 0x87C0, + 'VARIANT_EXT': 0x87C1, + 'INVARIANT_EXT': 0x87C2, + 'LOCAL_CONSTANT_EXT': 0x87C3, + 'LOCAL_EXT': 0x87C4, + 'MAX_VERTEX_SHADER_INSTRUCTIONS_EXT': 0x87C5, + 'MAX_VERTEX_SHADER_VARIANTS_EXT': 0x87C6, + 'MAX_VERTEX_SHADER_INVARIANTS_EXT': 0x87C7, + 'MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT': 0x87C8, + 'MAX_VERTEX_SHADER_LOCALS_EXT': 0x87C9, + 'MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT': 0x87CA, + 'MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT': 0x87CB, + 'MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT': 0x87CC, + 'MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT': 0x87CD, + 'MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT': 0x87CE, + 'VERTEX_SHADER_INSTRUCTIONS_EXT': 0x87CF, + 'VERTEX_SHADER_VARIANTS_EXT': 0x87D0, + 'VERTEX_SHADER_INVARIANTS_EXT': 0x87D1, + 'VERTEX_SHADER_LOCAL_CONSTANTS_EXT': 0x87D2, + 'VERTEX_SHADER_LOCALS_EXT': 0x87D3, + 'VERTEX_SHADER_OPTIMIZED_EXT': 0x87D4, + 'X_EXT': 0x87D5, + 'Y_EXT': 0x87D6, + 'Z_EXT': 0x87D7, + 'W_EXT': 0x87D8, + 'NEGATIVE_X_EXT': 0x87D9, + 'NEGATIVE_Y_EXT': 0x87DA, + 'NEGATIVE_Z_EXT': 0x87DB, + 'NEGATIVE_W_EXT': 0x87DC, + 'ZERO_EXT': 0x87DD, + 'ONE_EXT': 0x87DE, + 'NEGATIVE_ONE_EXT': 0x87DF, + 'NORMALIZED_RANGE_EXT': 0x87E0, + 'FULL_RANGE_EXT': 0x87E1, + 'CURRENT_VERTEX_EXT': 0x87E2, + 'MVP_MATRIX_EXT': 0x87E3, + 'VARIANT_VALUE_EXT': 0x87E4, + 'VARIANT_DATATYPE_EXT': 0x87E5, + 'VARIANT_ARRAY_STRIDE_EXT': 0x87E6, + 'VARIANT_ARRAY_TYPE_EXT': 0x87E7, + 'VARIANT_ARRAY_EXT': 0x87E8, + 'VARIANT_ARRAY_POINTER_EXT': 0x87E9, + 'INVARIANT_VALUE_EXT': 0x87EA, + 'INVARIANT_DATATYPE_EXT': 0x87EB, + 'LOCAL_CONSTANT_VALUE_EXT': 0x87EC, + 'LOCAL_CONSTANT_DATATYPE_EXT': 0x87ED, + 'MODELVIEW0_STACK_DEPTH_EXT': 0x0BA3, + 'MODELVIEW0_MATRIX_EXT': 0x0BA6, + 'MODELVIEW0_EXT': 0x1700, + 'MODELVIEW1_STACK_DEPTH_EXT': 0x8502, + 'MODELVIEW1_MATRIX_EXT': 0x8506, + 'VERTEX_WEIGHTING_EXT': 0x8509, + 'MODELVIEW1_EXT': 0x850A, + 'CURRENT_VERTEX_WEIGHT_EXT': 0x850B, + 'VERTEX_WEIGHT_ARRAY_EXT': 0x850C, + 'VERTEX_WEIGHT_ARRAY_SIZE_EXT': 0x850D, + 'VERTEX_WEIGHT_ARRAY_TYPE_EXT': 0x850E, + 'VERTEX_WEIGHT_ARRAY_STRIDE_EXT': 0x850F, + 'VERTEX_WEIGHT_ARRAY_POINTER_EXT': 0x8510, + 'OCCLUSION_TEST_HP': 0x8165, + 'OCCLUSION_TEST_RESULT_HP': 0x8166, + 'MIRRORED_REPEAT_IBM': 0x8370, + 'RED_MIN_CLAMP_INGR': 0x8560, + 'GREEN_MIN_CLAMP_INGR': 0x8561, + 'BLUE_MIN_CLAMP_INGR': 0x8562, + 'ALPHA_MIN_CLAMP_INGR': 0x8563, + 'RED_MAX_CLAMP_INGR': 0x8564, + 'GREEN_MAX_CLAMP_INGR': 0x8565, + 'BLUE_MAX_CLAMP_INGR': 0x8566, + 'ALPHA_MAX_CLAMP_INGR': 0x8567, + 'INTERLACE_READ_INGR': 0x8568, + 'PARALLEL_ARRAYS_INTEL': 0x83F4, + 'VERTEX_ARRAY_PARALLEL_POINTERS_INTEL': 0x83F5, + 'NORMAL_ARRAY_PARALLEL_POINTERS_INTEL': 0x83F6, + 'COLOR_ARRAY_PARALLEL_POINTERS_INTEL': 0x83F7, + 'TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL': 0x83F8, + 'KTX_FRONT_REGION': 0x0, + 'KTX_BACK_REGION': 0x1, + 'KTX_Z_REGION': 0x2, + 'KTX_STENCIL_REGION': 0x3, + 'TEXTURE_1D_STACK_MESAX': 0x8759, + 'TEXTURE_2D_STACK_MESAX': 0x875A, + 'PROXY_TEXTURE_1D_STACK_MESAX': 0x875B, + 'PROXY_TEXTURE_2D_STACK_MESAX': 0x875C, + 'TEXTURE_1D_STACK_BINDING_MESAX': 0x875D, + 'TEXTURE_2D_STACK_BINDING_MESAX': 0x875E, + 'PACK_INVERT_MESA': 0x8758, + 'UNSIGNED_SHORT_8_8_MESA': 0x85BA, + 'UNSIGNED_SHORT_8_8_REV_MESA': 0x85BB, + 'YCBCR_MESA': 0x8757, + 'QUERY_WAIT_NV': 0x8E13, + 'QUERY_NO_WAIT_NV': 0x8E14, + 'QUERY_BY_REGION_WAIT_NV': 0x8E15, + 'QUERY_BY_REGION_NO_WAIT_NV': 0x8E16, + 'DEPTH_STENCIL_TO_RGBA_NV': 0x886E, + 'DEPTH_STENCIL_TO_BGRA_NV': 0x886F, + 'DEPTH_COMPONENT32F_NV': 0x8DAB, + 'DEPTH32F_STENCIL8_NV': 0x8DAC, + 'FLOAT_32_UNSIGNED_INT_24_8_REV_NV': 0x8DAD, + 'DEPTH_BUFFER_FLOAT_MODE_NV': 0x8DAF, + 'DEPTH_CLAMP_NV': 0x864F, + 'SAMPLE_COUNT_BITS_NV': 0x8864, + 'CURRENT_SAMPLE_COUNT_QUERY_NV': 0x8865, + 'QUERY_RESULT_NV': 0x8866, + 'QUERY_RESULT_AVAILABLE_NV': 0x8867, + 'SAMPLE_COUNT_NV': 0x8914, + 'EVAL_2D_NV': 0x86C0, + 'EVAL_TRIANGULAR_2D_NV': 0x86C1, + 'MAP_TESSELLATION_NV': 0x86C2, + 'MAP_ATTRIB_U_ORDER_NV': 0x86C3, + 'MAP_ATTRIB_V_ORDER_NV': 0x86C4, + 'EVAL_FRACTIONAL_TESSELLATION_NV': 0x86C5, + 'EVAL_VERTEX_ATTRIB0_NV': 0x86C6, + 'EVAL_VERTEX_ATTRIB1_NV': 0x86C7, + 'EVAL_VERTEX_ATTRIB2_NV': 0x86C8, + 'EVAL_VERTEX_ATTRIB3_NV': 0x86C9, + 'EVAL_VERTEX_ATTRIB4_NV': 0x86CA, + 'EVAL_VERTEX_ATTRIB5_NV': 0x86CB, + 'EVAL_VERTEX_ATTRIB6_NV': 0x86CC, + 'EVAL_VERTEX_ATTRIB7_NV': 0x86CD, + 'EVAL_VERTEX_ATTRIB8_NV': 0x86CE, + 'EVAL_VERTEX_ATTRIB9_NV': 0x86CF, + 'EVAL_VERTEX_ATTRIB10_NV': 0x86D0, + 'EVAL_VERTEX_ATTRIB11_NV': 0x86D1, + 'EVAL_VERTEX_ATTRIB12_NV': 0x86D2, + 'EVAL_VERTEX_ATTRIB13_NV': 0x86D3, + 'EVAL_VERTEX_ATTRIB14_NV': 0x86D4, + 'EVAL_VERTEX_ATTRIB15_NV': 0x86D5, + 'MAX_MAP_TESSELLATION_NV': 0x86D6, + 'MAX_RATIONAL_EVAL_ORDER_NV': 0x86D7, + 'SAMPLE_POSITION_NV': 0x8E50, + 'SAMPLE_MASK_NV': 0x8E51, + 'SAMPLE_MASK_VALUE_NV': 0x8E52, + 'TEXTURE_BINDING_RENDERBUFFER_NV': 0x8E53, + 'TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV': 0x8E54, + 'TEXTURE_RENDERBUFFER_NV': 0x8E55, + 'SAMPLER_RENDERBUFFER_NV': 0x8E56, + 'INT_SAMPLER_RENDERBUFFER_NV': 0x8E57, + 'UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV': 0x8E58, + 'MAX_SAMPLE_MASK_WORDS_NV': 0x8E59, + 'ALL_COMPLETED_NV': 0x84F2, + 'FENCE_STATUS_NV': 0x84F3, + 'FENCE_CONDITION_NV': 0x84F4, + 'FLOAT_R_NV': 0x8880, + 'FLOAT_RG_NV': 0x8881, + 'FLOAT_RGB_NV': 0x8882, + 'FLOAT_RGBA_NV': 0x8883, + 'FLOAT_R16_NV': 0x8884, + 'FLOAT_R32_NV': 0x8885, + 'FLOAT_RG16_NV': 0x8886, + 'FLOAT_RG32_NV': 0x8887, + 'FLOAT_RGB16_NV': 0x8888, + 'FLOAT_RGB32_NV': 0x8889, + 'FLOAT_RGBA16_NV': 0x888A, + 'FLOAT_RGBA32_NV': 0x888B, + 'TEXTURE_FLOAT_COMPONENTS_NV': 0x888C, + 'FLOAT_CLEAR_COLOR_VALUE_NV': 0x888D, + 'FLOAT_RGBA_MODE_NV': 0x888E, + 'FOG_DISTANCE_MODE_NV': 0x855A, + 'EYE_RADIAL_NV': 0x855B, + 'EYE_PLANE_ABSOLUTE_NV': 0x855C, + 'MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV': 0x8868, + 'FRAGMENT_PROGRAM_NV': 0x8870, + 'MAX_TEXTURE_COORDS_NV': 0x8871, + 'MAX_TEXTURE_IMAGE_UNITS_NV': 0x8872, + 'FRAGMENT_PROGRAM_BINDING_NV': 0x8873, + 'PROGRAM_ERROR_STRING_NV': 0x8874, + 'MAX_PROGRAM_EXEC_INSTRUCTIONS_NV': 0x88F4, + 'MAX_PROGRAM_CALL_DEPTH_NV': 0x88F5, + 'MAX_PROGRAM_IF_DEPTH_NV': 0x88F6, + 'MAX_PROGRAM_LOOP_DEPTH_NV': 0x88F7, + 'MAX_PROGRAM_LOOP_COUNT_NV': 0x88F8, + 'RENDERBUFFER_COVERAGE_SAMPLES_NV': 0x8CAB, + 'RENDERBUFFER_COLOR_SAMPLES_NV': 0x8E10, + 'MAX_MULTISAMPLE_COVERAGE_MODES_NV': 0x8E11, + 'MULTISAMPLE_COVERAGE_MODES_NV': 0x8E12, + 'GEOMETRY_PROGRAM_NV': 0x8C26, + 'MAX_PROGRAM_OUTPUT_VERTICES_NV': 0x8C27, + 'MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV': 0x8C28, + 'MIN_PROGRAM_TEXEL_OFFSET_NV': 0x8904, + 'MAX_PROGRAM_TEXEL_OFFSET_NV': 0x8905, + 'PROGRAM_ATTRIB_COMPONENTS_NV': 0x8906, + 'PROGRAM_RESULT_COMPONENTS_NV': 0x8907, + 'MAX_PROGRAM_ATTRIB_COMPONENTS_NV': 0x8908, + 'MAX_PROGRAM_RESULT_COMPONENTS_NV': 0x8909, + 'MAX_PROGRAM_GENERIC_ATTRIBS_NV': 0x8DA5, + 'MAX_PROGRAM_GENERIC_RESULTS_NV': 0x8DA6, + 'HALF_FLOAT_NV': 0x140B, + 'MAX_SHININESS_NV': 0x8504, + 'MAX_SPOT_EXPONENT_NV': 0x8505, + 'MULTISAMPLE_FILTER_HINT_NV': 0x8534, + 'PIXEL_COUNTER_BITS_NV': 0x8864, + 'CURRENT_OCCLUSION_QUERY_ID_NV': 0x8865, + 'PIXEL_COUNT_NV': 0x8866, + 'PIXEL_COUNT_AVAILABLE_NV': 0x8867, + 'DEPTH_STENCIL_NV': 0x84F9, + 'UNSIGNED_INT_24_8_NV': 0x84FA, + 'MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV': 0x8DA0, + 'MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV': 0x8DA1, + 'VERTEX_PROGRAM_PARAMETER_BUFFER_NV': 0x8DA2, + 'GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV': 0x8DA3, + 'FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV': 0x8DA4, + 'WRITE_PIXEL_DATA_RANGE_NV': 0x8878, + 'READ_PIXEL_DATA_RANGE_NV': 0x8879, + 'WRITE_PIXEL_DATA_RANGE_LENGTH_NV': 0x887A, + 'READ_PIXEL_DATA_RANGE_LENGTH_NV': 0x887B, + 'WRITE_PIXEL_DATA_RANGE_POINTER_NV': 0x887C, + 'READ_PIXEL_DATA_RANGE_POINTER_NV': 0x887D, + 'POINT_SPRITE_NV': 0x8861, + 'COORD_REPLACE_NV': 0x8862, + 'POINT_SPRITE_R_MODE_NV': 0x8863, + 'FRAME_NV': 0x8E26, + 'FIELDS_NV': 0x8E27, + 'CURRENT_TIME_NV': 0x8E28, + 'NUM_FILL_STREAMS_NV': 0x8E29, + 'PRESENT_TIME_NV': 0x8E2A, + 'PRESENT_DURATION_NV': 0x8E2B, + 'PRIMITIVE_RESTART_NV': 0x8558, + 'PRIMITIVE_RESTART_INDEX_NV': 0x8559, + 'REGISTER_COMBINERS_NV': 0x8522, + 'VARIABLE_A_NV': 0x8523, + 'VARIABLE_B_NV': 0x8524, + 'VARIABLE_C_NV': 0x8525, + 'VARIABLE_D_NV': 0x8526, + 'VARIABLE_E_NV': 0x8527, + 'VARIABLE_F_NV': 0x8528, + 'VARIABLE_G_NV': 0x8529, + 'CONSTANT_COLOR0_NV': 0x852A, + 'CONSTANT_COLOR1_NV': 0x852B, + 'PRIMARY_COLOR_NV': 0x852C, + 'SECONDARY_COLOR_NV': 0x852D, + 'SPARE0_NV': 0x852E, + 'SPARE1_NV': 0x852F, + 'DISCARD_NV': 0x8530, + 'E_TIMES_F_NV': 0x8531, + 'SPARE0_PLUS_SECONDARY_COLOR_NV': 0x8532, + 'UNSIGNED_IDENTITY_NV': 0x8536, + 'UNSIGNED_INVERT_NV': 0x8537, + 'EXPAND_NORMAL_NV': 0x8538, + 'EXPAND_NEGATE_NV': 0x8539, + 'HALF_BIAS_NORMAL_NV': 0x853A, + 'HALF_BIAS_NEGATE_NV': 0x853B, + 'SIGNED_IDENTITY_NV': 0x853C, + 'SIGNED_NEGATE_NV': 0x853D, + 'SCALE_BY_TWO_NV': 0x853E, + 'SCALE_BY_FOUR_NV': 0x853F, + 'SCALE_BY_ONE_HALF_NV': 0x8540, + 'BIAS_BY_NEGATIVE_ONE_HALF_NV': 0x8541, + 'COMBINER_INPUT_NV': 0x8542, + 'COMBINER_MAPPING_NV': 0x8543, + 'COMBINER_COMPONENT_USAGE_NV': 0x8544, + 'COMBINER_AB_DOT_PRODUCT_NV': 0x8545, + 'COMBINER_CD_DOT_PRODUCT_NV': 0x8546, + 'COMBINER_MUX_SUM_NV': 0x8547, + 'COMBINER_SCALE_NV': 0x8548, + 'COMBINER_BIAS_NV': 0x8549, + 'COMBINER_AB_OUTPUT_NV': 0x854A, + 'COMBINER_CD_OUTPUT_NV': 0x854B, + 'COMBINER_SUM_OUTPUT_NV': 0x854C, + 'MAX_GENERAL_COMBINERS_NV': 0x854D, + 'NUM_GENERAL_COMBINERS_NV': 0x854E, + 'COLOR_SUM_CLAMP_NV': 0x854F, + 'COMBINER0_NV': 0x8550, + 'COMBINER1_NV': 0x8551, + 'COMBINER2_NV': 0x8552, + 'COMBINER3_NV': 0x8553, + 'COMBINER4_NV': 0x8554, + 'COMBINER5_NV': 0x8555, + 'COMBINER6_NV': 0x8556, + 'COMBINER7_NV': 0x8557, + 'PER_STAGE_CONSTANTS_NV': 0x8535, + 'EMBOSS_LIGHT_NV': 0x855D, + 'EMBOSS_CONSTANT_NV': 0x855E, + 'EMBOSS_MAP_NV': 0x855F, + 'NORMAL_MAP_NV': 0x8511, + 'REFLECTION_MAP_NV': 0x8512, + 'COMBINE4_NV': 0x8503, + 'SOURCE3_RGB_NV': 0x8583, + 'SOURCE3_ALPHA_NV': 0x858B, + 'OPERAND3_RGB_NV': 0x8593, + 'OPERAND3_ALPHA_NV': 0x859B, + 'TEXTURE_UNSIGNED_REMAP_MODE_NV': 0x888F, + 'TEXTURE_RECTANGLE_NV': 0x84F5, + 'TEXTURE_BINDING_RECTANGLE_NV': 0x84F6, + 'PROXY_TEXTURE_RECTANGLE_NV': 0x84F7, + 'MAX_RECTANGLE_TEXTURE_SIZE_NV': 0x84F8, + 'OFFSET_TEXTURE_RECTANGLE_NV': 0x864C, + 'OFFSET_TEXTURE_RECTANGLE_SCALE_NV': 0x864D, + 'DOT_PRODUCT_TEXTURE_RECTANGLE_NV': 0x864E, + 'RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV': 0x86D9, + 'UNSIGNED_INT_S8_S8_8_8_NV': 0x86DA, + 'UNSIGNED_INT_8_8_S8_S8_REV_NV': 0x86DB, + 'DSDT_MAG_INTENSITY_NV': 0x86DC, + 'SHADER_CONSISTENT_NV': 0x86DD, + 'TEXTURE_SHADER_NV': 0x86DE, + 'SHADER_OPERATION_NV': 0x86DF, + 'CULL_MODES_NV': 0x86E0, + 'OFFSET_TEXTURE_2D_MATRIX_NV': 0x86E1, + 'OFFSET_TEXTURE_MATRIX_NV': 0x86E1, + 'OFFSET_TEXTURE_2D_SCALE_NV': 0x86E2, + 'OFFSET_TEXTURE_SCALE_NV': 0x86E2, + 'OFFSET_TEXTURE_BIAS_NV': 0x86E3, + 'OFFSET_TEXTURE_2D_BIAS_NV': 0x86E3, + 'PREVIOUS_TEXTURE_INPUT_NV': 0x86E4, + 'CONST_EYE_NV': 0x86E5, + 'PASS_THROUGH_NV': 0x86E6, + 'CULL_FRAGMENT_NV': 0x86E7, + 'OFFSET_TEXTURE_2D_NV': 0x86E8, + 'DEPENDENT_AR_TEXTURE_2D_NV': 0x86E9, + 'DEPENDENT_GB_TEXTURE_2D_NV': 0x86EA, + 'DOT_PRODUCT_NV': 0x86EC, + 'DOT_PRODUCT_DEPTH_REPLACE_NV': 0x86ED, + 'DOT_PRODUCT_TEXTURE_2D_NV': 0x86EE, + 'DOT_PRODUCT_TEXTURE_CUBE_MAP_NV': 0x86F0, + 'DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV': 0x86F1, + 'DOT_PRODUCT_REFLECT_CUBE_MAP_NV': 0x86F2, + 'DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV': 0x86F3, + 'HILO_NV': 0x86F4, + 'DSDT_NV': 0x86F5, + 'DSDT_MAG_NV': 0x86F6, + 'DSDT_MAG_VIB_NV': 0x86F7, + 'HILO16_NV': 0x86F8, + 'SIGNED_HILO_NV': 0x86F9, + 'SIGNED_HILO16_NV': 0x86FA, + 'SIGNED_RGBA_NV': 0x86FB, + 'SIGNED_RGBA8_NV': 0x86FC, + 'SIGNED_RGB_NV': 0x86FE, + 'SIGNED_RGB8_NV': 0x86FF, + 'SIGNED_LUMINANCE_NV': 0x8701, + 'SIGNED_LUMINANCE8_NV': 0x8702, + 'SIGNED_LUMINANCE_ALPHA_NV': 0x8703, + 'SIGNED_LUMINANCE8_ALPHA8_NV': 0x8704, + 'SIGNED_ALPHA_NV': 0x8705, + 'SIGNED_ALPHA8_NV': 0x8706, + 'SIGNED_INTENSITY_NV': 0x8707, + 'SIGNED_INTENSITY8_NV': 0x8708, + 'DSDT8_NV': 0x8709, + 'DSDT8_MAG8_NV': 0x870A, + 'DSDT8_MAG8_INTENSITY8_NV': 0x870B, + 'SIGNED_RGB_UNSIGNED_ALPHA_NV': 0x870C, + 'SIGNED_RGB8_UNSIGNED_ALPHA8_NV': 0x870D, + 'HI_SCALE_NV': 0x870E, + 'LO_SCALE_NV': 0x870F, + 'DS_SCALE_NV': 0x8710, + 'DT_SCALE_NV': 0x8711, + 'MAGNITUDE_SCALE_NV': 0x8712, + 'VIBRANCE_SCALE_NV': 0x8713, + 'HI_BIAS_NV': 0x8714, + 'LO_BIAS_NV': 0x8715, + 'DS_BIAS_NV': 0x8716, + 'DT_BIAS_NV': 0x8717, + 'MAGNITUDE_BIAS_NV': 0x8718, + 'VIBRANCE_BIAS_NV': 0x8719, + 'TEXTURE_BORDER_VALUES_NV': 0x871A, + 'TEXTURE_HI_SIZE_NV': 0x871B, + 'TEXTURE_LO_SIZE_NV': 0x871C, + 'TEXTURE_DS_SIZE_NV': 0x871D, + 'TEXTURE_DT_SIZE_NV': 0x871E, + 'TEXTURE_MAG_SIZE_NV': 0x871F, + 'UNSIGNED_INT_S8_S8_8_8_NV': 0x86DA, + 'UNSIGNED_INT_8_8_S8_S8_REV_NV': 0x86DB, + 'DSDT_MAG_INTENSITY_NV': 0x86DC, + 'DOT_PRODUCT_TEXTURE_3D_NV': 0x86EF, + 'HILO_NV': 0x86F4, + 'DSDT_NV': 0x86F5, + 'DSDT_MAG_NV': 0x86F6, + 'DSDT_MAG_VIB_NV': 0x86F7, + 'HILO16_NV': 0x86F8, + 'SIGNED_HILO_NV': 0x86F9, + 'SIGNED_HILO16_NV': 0x86FA, + 'SIGNED_RGBA_NV': 0x86FB, + 'SIGNED_RGBA8_NV': 0x86FC, + 'SIGNED_RGB_NV': 0x86FE, + 'SIGNED_RGB8_NV': 0x86FF, + 'SIGNED_LUMINANCE_NV': 0x8701, + 'SIGNED_LUMINANCE8_NV': 0x8702, + 'SIGNED_LUMINANCE_ALPHA_NV': 0x8703, + 'SIGNED_LUMINANCE8_ALPHA8_NV': 0x8704, + 'SIGNED_ALPHA_NV': 0x8705, + 'SIGNED_ALPHA8_NV': 0x8706, + 'SIGNED_INTENSITY_NV': 0x8707, + 'SIGNED_INTENSITY8_NV': 0x8708, + 'DSDT8_NV': 0x8709, + 'DSDT8_MAG8_NV': 0x870A, + 'DSDT8_MAG8_INTENSITY8_NV': 0x870B, + 'SIGNED_RGB_UNSIGNED_ALPHA_NV': 0x870C, + 'SIGNED_RGB8_UNSIGNED_ALPHA8_NV': 0x870D, + 'OFFSET_PROJECTIVE_TEXTURE_2D_NV': 0x8850, + 'OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV': 0x8851, + 'OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV': 0x8852, + 'OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV': 0x8853, + 'OFFSET_HILO_TEXTURE_2D_NV': 0x8854, + 'OFFSET_HILO_TEXTURE_RECTANGLE_NV': 0x8855, + 'OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV': 0x8856, + 'OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV': 0x8857, + 'DEPENDENT_HILO_TEXTURE_2D_NV': 0x8858, + 'DEPENDENT_RGB_TEXTURE_3D_NV': 0x8859, + 'DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV': 0x885A, + 'DOT_PRODUCT_PASS_THROUGH_NV': 0x885B, + 'DOT_PRODUCT_TEXTURE_1D_NV': 0x885C, + 'DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV': 0x885D, + 'HILO8_NV': 0x885E, + 'SIGNED_HILO8_NV': 0x885F, + 'FORCE_BLUE_TO_ONE_NV': 0x8860, + 'BACK_PRIMARY_COLOR_NV': 0x8C77, + 'BACK_SECONDARY_COLOR_NV': 0x8C78, + 'TEXTURE_COORD_NV': 0x8C79, + 'CLIP_DISTANCE_NV': 0x8C7A, + 'VERTEX_ID_NV': 0x8C7B, + 'PRIMITIVE_ID_NV': 0x8C7C, + 'GENERIC_ATTRIB_NV': 0x8C7D, + 'TRANSFORM_FEEDBACK_ATTRIBS_NV': 0x8C7E, + 'TRANSFORM_FEEDBACK_BUFFER_MODE_NV': 0x8C7F, + 'MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV': 0x8C80, + 'ACTIVE_VARYINGS_NV': 0x8C81, + 'ACTIVE_VARYING_MAX_LENGTH_NV': 0x8C82, + 'TRANSFORM_FEEDBACK_VARYINGS_NV': 0x8C83, + 'TRANSFORM_FEEDBACK_BUFFER_START_NV': 0x8C84, + 'TRANSFORM_FEEDBACK_BUFFER_SIZE_NV': 0x8C85, + 'TRANSFORM_FEEDBACK_RECORD_NV': 0x8C86, + 'PRIMITIVES_GENERATED_NV': 0x8C87, + 'TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV': 0x8C88, + 'RASTERIZER_DISCARD_NV': 0x8C89, + 'MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV': 0x8C8A, + 'MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV': 0x8C8B, + 'INTERLEAVED_ATTRIBS_NV': 0x8C8C, + 'SEPARATE_ATTRIBS_NV': 0x8C8D, + 'TRANSFORM_FEEDBACK_BUFFER_NV': 0x8C8E, + 'TRANSFORM_FEEDBACK_BUFFER_BINDING_NV': 0x8C8F, + 'VERTEX_ARRAY_RANGE_NV': 0x851D, + 'VERTEX_ARRAY_RANGE_LENGTH_NV': 0x851E, + 'VERTEX_ARRAY_RANGE_VALID_NV': 0x851F, + 'MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV': 0x8520, + 'VERTEX_ARRAY_RANGE_POINTER_NV': 0x8521, + 'VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV': 0x8533, + 'VERTEX_PROGRAM_NV': 0x8620, + 'VERTEX_STATE_PROGRAM_NV': 0x8621, + 'ATTRIB_ARRAY_SIZE_NV': 0x8623, + 'ATTRIB_ARRAY_STRIDE_NV': 0x8624, + 'ATTRIB_ARRAY_TYPE_NV': 0x8625, + 'CURRENT_ATTRIB_NV': 0x8626, + 'PROGRAM_LENGTH_NV': 0x8627, + 'PROGRAM_STRING_NV': 0x8628, + 'MODELVIEW_PROJECTION_NV': 0x8629, + 'IDENTITY_NV': 0x862A, + 'INVERSE_NV': 0x862B, + 'TRANSPOSE_NV': 0x862C, + 'INVERSE_TRANSPOSE_NV': 0x862D, + 'MAX_TRACK_MATRIX_STACK_DEPTH_NV': 0x862E, + 'MAX_TRACK_MATRICES_NV': 0x862F, + 'MATRIX0_NV': 0x8630, + 'MATRIX1_NV': 0x8631, + 'MATRIX2_NV': 0x8632, + 'MATRIX3_NV': 0x8633, + 'MATRIX4_NV': 0x8634, + 'MATRIX5_NV': 0x8635, + 'MATRIX6_NV': 0x8636, + 'MATRIX7_NV': 0x8637, + 'CURRENT_MATRIX_STACK_DEPTH_NV': 0x8640, + 'CURRENT_MATRIX_NV': 0x8641, + 'VERTEX_PROGRAM_POINT_SIZE_NV': 0x8642, + 'VERTEX_PROGRAM_TWO_SIDE_NV': 0x8643, + 'PROGRAM_PARAMETER_NV': 0x8644, + 'ATTRIB_ARRAY_POINTER_NV': 0x8645, + 'PROGRAM_TARGET_NV': 0x8646, + 'PROGRAM_RESIDENT_NV': 0x8647, + 'TRACK_MATRIX_NV': 0x8648, + 'TRACK_MATRIX_TRANSFORM_NV': 0x8649, + 'VERTEX_PROGRAM_BINDING_NV': 0x864A, + 'PROGRAM_ERROR_POSITION_NV': 0x864B, + 'VERTEX_ATTRIB_ARRAY0_NV': 0x8650, + 'VERTEX_ATTRIB_ARRAY1_NV': 0x8651, + 'VERTEX_ATTRIB_ARRAY2_NV': 0x8652, + 'VERTEX_ATTRIB_ARRAY3_NV': 0x8653, + 'VERTEX_ATTRIB_ARRAY4_NV': 0x8654, + 'VERTEX_ATTRIB_ARRAY5_NV': 0x8655, + 'VERTEX_ATTRIB_ARRAY6_NV': 0x8656, + 'VERTEX_ATTRIB_ARRAY7_NV': 0x8657, + 'VERTEX_ATTRIB_ARRAY8_NV': 0x8658, + 'VERTEX_ATTRIB_ARRAY9_NV': 0x8659, + 'VERTEX_ATTRIB_ARRAY10_NV': 0x865A, + 'VERTEX_ATTRIB_ARRAY11_NV': 0x865B, + 'VERTEX_ATTRIB_ARRAY12_NV': 0x865C, + 'VERTEX_ATTRIB_ARRAY13_NV': 0x865D, + 'VERTEX_ATTRIB_ARRAY14_NV': 0x865E, + 'VERTEX_ATTRIB_ARRAY15_NV': 0x865F, + 'MAP1_VERTEX_ATTRIB0_4_NV': 0x8660, + 'MAP1_VERTEX_ATTRIB1_4_NV': 0x8661, + 'MAP1_VERTEX_ATTRIB2_4_NV': 0x8662, + 'MAP1_VERTEX_ATTRIB3_4_NV': 0x8663, + 'MAP1_VERTEX_ATTRIB4_4_NV': 0x8664, + 'MAP1_VERTEX_ATTRIB5_4_NV': 0x8665, + 'MAP1_VERTEX_ATTRIB6_4_NV': 0x8666, + 'MAP1_VERTEX_ATTRIB7_4_NV': 0x8667, + 'MAP1_VERTEX_ATTRIB8_4_NV': 0x8668, + 'MAP1_VERTEX_ATTRIB9_4_NV': 0x8669, + 'MAP1_VERTEX_ATTRIB10_4_NV': 0x866A, + 'MAP1_VERTEX_ATTRIB11_4_NV': 0x866B, + 'MAP1_VERTEX_ATTRIB12_4_NV': 0x866C, + 'MAP1_VERTEX_ATTRIB13_4_NV': 0x866D, + 'MAP1_VERTEX_ATTRIB14_4_NV': 0x866E, + 'MAP1_VERTEX_ATTRIB15_4_NV': 0x866F, + 'MAP2_VERTEX_ATTRIB0_4_NV': 0x8670, + 'MAP2_VERTEX_ATTRIB1_4_NV': 0x8671, + 'MAP2_VERTEX_ATTRIB2_4_NV': 0x8672, + 'MAP2_VERTEX_ATTRIB3_4_NV': 0x8673, + 'MAP2_VERTEX_ATTRIB4_4_NV': 0x8674, + 'MAP2_VERTEX_ATTRIB5_4_NV': 0x8675, + 'MAP2_VERTEX_ATTRIB6_4_NV': 0x8676, + 'MAP2_VERTEX_ATTRIB7_4_NV': 0x8677, + 'MAP2_VERTEX_ATTRIB8_4_NV': 0x8678, + 'MAP2_VERTEX_ATTRIB9_4_NV': 0x8679, + 'MAP2_VERTEX_ATTRIB10_4_NV': 0x867A, + 'MAP2_VERTEX_ATTRIB11_4_NV': 0x867B, + 'MAP2_VERTEX_ATTRIB12_4_NV': 0x867C, + 'MAP2_VERTEX_ATTRIB13_4_NV': 0x867D, + 'MAP2_VERTEX_ATTRIB14_4_NV': 0x867E, + 'MAP2_VERTEX_ATTRIB15_4_NV': 0x867F, + 'MAX_PROGRAM_EXEC_INSTRUCTIONS_NV': 0x88F4, + 'MAX_PROGRAM_CALL_DEPTH_NV': 0x88F5, + 'MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB': 0x8B4C, + 'BYTE': 0x1400, + 'PALETTE4_RGB8_OES': 0x8B90, + 'PALETTE4_RGBA8_OES': 0x8B91, + 'PALETTE4_R5_G6_B5_OES': 0x8B92, + 'PALETTE4_RGBA4_OES': 0x8B93, + 'PALETTE4_RGB5_A1_OES': 0x8B94, + 'PALETTE8_RGB8_OES': 0x8B95, + 'PALETTE8_RGBA8_OES': 0x8B96, + 'PALETTE8_R5_G6_B5_OES': 0x8B97, + 'PALETTE8_RGBA4_OES': 0x8B98, + 'PALETTE8_RGB5_A1_OES': 0x8B99, + 'IMPLEMENTATION_COLOR_READ_TYPE_OES': 0x8B9A, + 'IMPLEMENTATION_COLOR_READ_FORMAT_OES': 0x8B9B, + 'INTERLACE_OML': 0x8980, + 'INTERLACE_READ_OML': 0x8981, + 'PACK_RESAMPLE_OML': 0x8984, + 'UNPACK_RESAMPLE_OML': 0x8985, + 'RESAMPLE_REPLICATE_OML': 0x8986, + 'RESAMPLE_ZERO_FILL_OML': 0x8987, + 'RESAMPLE_AVERAGE_OML': 0x8988, + 'RESAMPLE_DECIMATE_OML': 0x8989, + 'FORMAT_SUBSAMPLE_24_24_OML': 0x8982, + 'FORMAT_SUBSAMPLE_244_244_OML': 0x8983, + 'VERTEX23_BIT_PGI': 0x00000004, + 'VERTEX4_BIT_PGI': 0x00000008, + 'COLOR3_BIT_PGI': 0x00010000, + 'COLOR4_BIT_PGI': 0x00020000, + 'EDGEFLAG_BIT_PGI': 0x00040000, + 'INDEX_BIT_PGI': 0x00080000, + 'MAT_AMBIENT_BIT_PGI': 0x00100000, + 'MAT_AMBIENT_AND_DIFFUSE_BIT_PGI': 0x00200000, + 'MAT_DIFFUSE_BIT_PGI': 0x00400000, + 'MAT_EMISSION_BIT_PGI': 0x00800000, + 'MAT_COLOR_INDEXES_BIT_PGI': 0x01000000, + 'MAT_SHININESS_BIT_PGI': 0x02000000, + 'MAT_SPECULAR_BIT_PGI': 0x04000000, + 'NORMAL_BIT_PGI': 0x08000000, + 'TEXCOORD1_BIT_PGI': 0x10000000, + 'TEXCOORD2_BIT_PGI': 0x20000000, + 'TEXCOORD3_BIT_PGI': 0x40000000, + 'TEXCOORD4_BIT_PGI': 0x80000000, + 'SCREEN_COORDINATES_REND': 0x8490, + 'INVERTED_SCREEN_W_REND': 0x8491, + 'RGB_S3TC': 0x83A0, + 'RGB4_S3TC': 0x83A1, + 'RGBA_S3TC': 0x83A2, + 'RGBA4_S3TC': 0x83A3, + 'RGBA_DXT5_S3TC': 0x83A4, + 'RGBA4_DXT5_S3TC': 0x83A5, + 'EXTENDED_RANGE_SGIS': 0x85A5, + 'MIN_RED_SGIS': 0x85A6, + 'MAX_RED_SGIS': 0x85A7, + 'MIN_GREEN_SGIS': 0x85A8, + 'MAX_GREEN_SGIS': 0x85A9, + 'MIN_BLUE_SGIS': 0x85AA, + 'MAX_BLUE_SGIS': 0x85AB, + 'MIN_ALPHA_SGIS': 0x85AC, + 'MAX_ALPHA_SGIS': 0x85AD, + 'GENERATE_MIPMAP_SGIS': 0x8191, + 'GENERATE_MIPMAP_HINT_SGIS': 0x8192, + 'MULTISAMPLE_SGIS': 0x809D, + 'SAMPLE_ALPHA_TO_MASK_SGIS': 0x809E, + 'SAMPLE_ALPHA_TO_ONE_SGIS': 0x809F, + 'SAMPLE_MASK_SGIS': 0x80A0, + '1PASS_SGIS': 0x80A1, + '2PASS_0_SGIS': 0x80A2, + '2PASS_1_SGIS': 0x80A3, + '4PASS_0_SGIS': 0x80A4, + '4PASS_1_SGIS': 0x80A5, + '4PASS_2_SGIS': 0x80A6, + '4PASS_3_SGIS': 0x80A7, + 'SAMPLE_BUFFERS_SGIS': 0x80A8, + 'SAMPLES_SGIS': 0x80A9, + 'SAMPLE_MASK_VALUE_SGIS': 0x80AA, + 'SAMPLE_MASK_INVERT_SGIS': 0x80AB, + 'SAMPLE_PATTERN_SGIS': 0x80AC, + 'MULTISAMPLE_BIT_EXT': 0x20000000, + 'EYE_DISTANCE_TO_POINT_SGIS': 0x81F0, + 'OBJECT_DISTANCE_TO_POINT_SGIS': 0x81F1, + 'EYE_DISTANCE_TO_LINE_SGIS': 0x81F2, + 'OBJECT_DISTANCE_TO_LINE_SGIS': 0x81F3, + 'EYE_POINT_SGIS': 0x81F4, + 'OBJECT_POINT_SGIS': 0x81F5, + 'EYE_LINE_SGIS': 0x81F6, + 'OBJECT_LINE_SGIS': 0x81F7, + 'CLAMP_TO_BORDER_SGIS': 0x812D, + 'CLAMP_TO_EDGE_SGIS': 0x812F, + 'TEXTURE_MIN_LOD_SGIS': 0x813A, + 'TEXTURE_MAX_LOD_SGIS': 0x813B, + 'TEXTURE_BASE_LEVEL_SGIS': 0x813C, + 'TEXTURE_MAX_LEVEL_SGIS': 0x813D, + 'ASYNC_MARKER_SGIX': 0x8329, + 'ASYNC_HISTOGRAM_SGIX': 0x832C, + 'MAX_ASYNC_HISTOGRAM_SGIX': 0x832D, + 'ASYNC_TEX_IMAGE_SGIX': 0x835C, + 'ASYNC_DRAW_PIXELS_SGIX': 0x835D, + 'ASYNC_READ_PIXELS_SGIX': 0x835E, + 'MAX_ASYNC_TEX_IMAGE_SGIX': 0x835F, + 'MAX_ASYNC_DRAW_PIXELS_SGIX': 0x8360, + 'MAX_ASYNC_READ_PIXELS_SGIX': 0x8361, + 'ALPHA_MIN_SGIX': 0x8320, + 'ALPHA_MAX_SGIX': 0x8321, + 'CONVOLUTION_HINT_SGIX': 0x8316, + 'DEPTH_COMPONENT16_SGIX': 0x81A5, + 'DEPTH_COMPONENT24_SGIX': 0x81A6, + 'DEPTH_COMPONENT32_SGIX': 0x81A7, + 'FOG_OFFSET_SGIX': 0x8198, + 'FOG_OFFSET_VALUE_SGIX': 0x8199, + 'INTERLACE_SGIX': 0x8094, + 'PACK_RESAMPLE_SGIX': 0x842E, + 'UNPACK_RESAMPLE_SGIX': 0x842F, + 'RESAMPLE_DECIMATE_SGIX': 0x8430, + 'RESAMPLE_REPLICATE_SGIX': 0x8433, + 'RESAMPLE_ZERO_FILL_SGIX': 0x8434, + 'TEXTURE_COMPARE_SGIX': 0x819A, + 'TEXTURE_COMPARE_OPERATOR_SGIX': 0x819B, + 'TEXTURE_LEQUAL_R_SGIX': 0x819C, + 'TEXTURE_GEQUAL_R_SGIX': 0x819D, + 'SHADOW_AMBIENT_SGIX': 0x80BF, + 'TEXTURE_MAX_CLAMP_S_SGIX': 0x8369, + 'TEXTURE_MAX_CLAMP_T_SGIX': 0x836A, + 'TEXTURE_MAX_CLAMP_R_SGIX': 0x836B, + 'TEXTURE_MULTI_BUFFER_HINT_SGIX': 0x812E, + 'RGB_SIGNED_SGIX': 0x85E0, + 'RGBA_SIGNED_SGIX': 0x85E1, + 'ALPHA_SIGNED_SGIX': 0x85E2, + 'LUMINANCE_SIGNED_SGIX': 0x85E3, + 'INTENSITY_SIGNED_SGIX': 0x85E4, + 'LUMINANCE_ALPHA_SIGNED_SGIX': 0x85E5, + 'RGB16_SIGNED_SGIX': 0x85E6, + 'RGBA16_SIGNED_SGIX': 0x85E7, + 'ALPHA16_SIGNED_SGIX': 0x85E8, + 'LUMINANCE16_SIGNED_SGIX': 0x85E9, + 'INTENSITY16_SIGNED_SGIX': 0x85EA, + 'LUMINANCE16_ALPHA16_SIGNED_SGIX': 0x85EB, + 'RGB_EXTENDED_RANGE_SGIX': 0x85EC, + 'RGBA_EXTENDED_RANGE_SGIX': 0x85ED, + 'ALPHA_EXTENDED_RANGE_SGIX': 0x85EE, + 'LUMINANCE_EXTENDED_RANGE_SGIX': 0x85EF, + 'INTENSITY_EXTENDED_RANGE_SGIX': 0x85F0, + 'LUMINANCE_ALPHA_EXTENDED_RANGE_SGIX': 0x85F1, + 'RGB16_EXTENDED_RANGE_SGIX': 0x85F2, + 'RGBA16_EXTENDED_RANGE_SGIX': 0x85F3, + 'ALPHA16_EXTENDED_RANGE_SGIX': 0x85F4, + 'LUMINANCE16_EXTENDED_RANGE_SGIX': 0x85F5, + 'INTENSITY16_EXTENDED_RANGE_SGIX': 0x85F6, + 'LUMINANCE16_ALPHA16_EXTENDED_RANGE_SGIX': 0x85F7, + 'MIN_LUMINANCE_SGIS': 0x85F8, + 'MAX_LUMINANCE_SGIS': 0x85F9, + 'MIN_INTENSITY_SGIS': 0x85FA, + 'MAX_INTENSITY_SGIS': 0x85FB, + 'POST_TEXTURE_FILTER_BIAS_SGIX': 0x8179, + 'POST_TEXTURE_FILTER_SCALE_SGIX': 0x817A, + 'POST_TEXTURE_FILTER_BIAS_RANGE_SGIX': 0x817B, + 'POST_TEXTURE_FILTER_SCALE_RANGE_SGIX': 0x817C, + 'VERTEX_PRECLIP_SGIX': 0x83EE, + 'VERTEX_PRECLIP_HINT_SGIX': 0x83EF, + 'VERTEX_PRECLIP_SGIX': 0x83EE, + 'VERTEX_PRECLIP_HINT_SGIX': 0x83EF, + 'COLOR_MATRIX_SGI': 0x80B1, + 'COLOR_MATRIX_STACK_DEPTH_SGI': 0x80B2, + 'MAX_COLOR_MATRIX_STACK_DEPTH_SGI': 0x80B3, + 'POST_COLOR_MATRIX_RED_SCALE_SGI': 0x80B4, + 'POST_COLOR_MATRIX_GREEN_SCALE_SGI': 0x80B5, + 'POST_COLOR_MATRIX_BLUE_SCALE_SGI': 0x80B6, + 'POST_COLOR_MATRIX_ALPHA_SCALE_SGI': 0x80B7, + 'POST_COLOR_MATRIX_RED_BIAS_SGI': 0x80B8, + 'POST_COLOR_MATRIX_GREEN_BIAS_SGI': 0x80B9, + 'POST_COLOR_MATRIX_BLUE_BIAS_SGI': 0x80BA, + 'POST_COLOR_MATRIX_ALPHA_BIAS_SGI': 0x80BB, + 'COLOR_TABLE_SGI': 0x80D0, + 'POST_CONVOLUTION_COLOR_TABLE_SGI': 0x80D1, + 'POST_COLOR_MATRIX_COLOR_TABLE_SGI': 0x80D2, + 'PROXY_COLOR_TABLE_SGI': 0x80D3, + 'PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI': 0x80D4, + 'PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI': 0x80D5, + 'COLOR_TABLE_SCALE_SGI': 0x80D6, + 'COLOR_TABLE_BIAS_SGI': 0x80D7, + 'COLOR_TABLE_FORMAT_SGI': 0x80D8, + 'COLOR_TABLE_WIDTH_SGI': 0x80D9, + 'COLOR_TABLE_RED_SIZE_SGI': 0x80DA, + 'COLOR_TABLE_GREEN_SIZE_SGI': 0x80DB, + 'COLOR_TABLE_BLUE_SIZE_SGI': 0x80DC, + 'COLOR_TABLE_ALPHA_SIZE_SGI': 0x80DD, + 'COLOR_TABLE_LUMINANCE_SIZE_SGI': 0x80DE, + 'COLOR_TABLE_INTENSITY_SIZE_SGI': 0x80DF, + 'TEXTURE_COLOR_TABLE_SGI': 0x80BC, + 'PROXY_TEXTURE_COLOR_TABLE_SGI': 0x80BD, + 'UNPACK_CONSTANT_DATA_SUNX': 0x81D5, + 'TEXTURE_CONSTANT_DATA_SUNX': 0x81D6, + 'WRAP_BORDER_SUN': 0x81D4, + 'GLOBAL_ALPHA_SUN': 0x81D9, + 'GLOBAL_ALPHA_FACTOR_SUN': 0x81DA, + 'QUAD_MESH_SUN': 0x8614, + 'TRIANGLE_MESH_SUN': 0x8615, + 'SLICE_ACCUM_SUN': 0x85CC, + 'RESTART_SUN': 0x01, + 'REPLACE_MIDDLE_SUN': 0x02, + 'REPLACE_OLDEST_SUN': 0x03, + 'TRIANGLE_LIST_SUN': 0x81D7, + 'REPLACEMENT_CODE_SUN': 0x81D8, + 'REPLACEMENT_CODE_ARRAY_SUN': 0x85C0, + 'REPLACEMENT_CODE_ARRAY_TYPE_SUN': 0x85C1, + 'REPLACEMENT_CODE_ARRAY_STRIDE_SUN': 0x85C2, + 'REPLACEMENT_CODE_ARRAY_POINTER_SUN': 0x85C3, + 'R1UI_V3F_SUN': 0x85C4, + 'R1UI_C4UB_V3F_SUN': 0x85C5, + 'R1UI_C3F_V3F_SUN': 0x85C6, + 'R1UI_N3F_V3F_SUN': 0x85C7, + 'R1UI_C4F_N3F_V3F_SUN': 0x85C8, + 'R1UI_T2F_V3F_SUN': 0x85C9, + 'R1UI_T2F_N3F_V3F_SUN': 0x85CA, + 'R1UI_T2F_C4F_N3F_V3F_SUN': 0x85CB, + 'PHONG_WIN': 0x80EA, + 'PHONG_HINT_WIN': 0x80EB, + 'PROGRAM_BINARY_RETRIEVABLE_HINT': 0x8257, + 'PROGRAM_BINARY_LENGTH': 0x8741, + 'PROGRAM_BINARY_FORMATS': 0x87FE, + 'NUM_PROGRAM_BINARY_FORMATS': 0x87FF, + 'PRIMITIVE_RESTART_FIXED_INDEX': 0x8D69, + 'FOG_SPECULAR_TEXTURE_WIN': 0x80EC +}; diff --git a/dom/canvas/test/webgl-conf/checkout/js/glsl-conformance-test.js b/dom/canvas/test/webgl-conf/checkout/js/glsl-conformance-test.js new file mode 100644 index 0000000000..12a056ce21 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/glsl-conformance-test.js @@ -0,0 +1,424 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +GLSLConformanceTester = (function(){ + +var wtu = WebGLTestUtils; +var defaultVertexShader = [ + "attribute vec4 vPosition;", + "void main()", + "{", + " gl_Position = vPosition;", + "}" +].join('\n'); + +var defaultFragmentShader = [ + "precision mediump float;", + "void main()", + "{", + " gl_FragColor = vec4(1.0,0.0,0.0,1.0);", + "}" +].join('\n'); + +var defaultESSL3VertexShader = [ + "#version 300 es", + "in vec4 vPosition;", + "void main()", + "{", + " gl_Position = vPosition;", + "}" +].join('\n'); + +var defaultESSL3FragmentShader = [ + "#version 300 es", + "precision mediump float;", + "out vec4 my_FragColor;", + "void main()", + "{", + " my_FragColor = vec4(1.0,0.0,0.0,1.0);", + "}" +].join('\n'); + +function log(msg) { + bufferedLogToConsole(msg); +} + +var vShaderDB = {}; +var fShaderDB = {}; + +/** + * The info parameter should contain the following keys. Note that you may leave + * the parameters for one shader out, in which case the default shader will be + * used. + * vShaderSource: the source code for vertex shader + * vShaderId: id of an element containing vertex shader source code. Used if + * vShaderSource is not specified. + * vShaderSuccess: true if vertex shader compilation should + * succeed. + * fShaderSource: the source code for fragment shader + * fShaderId: id of an element containing fragment shader source code. Used if + * fShaderSource is not specified. + * fShaderSuccess: true if fragment shader compilation should + * succeed. + * linkSuccess: true if link should succeed + * passMsg: msg to describe success condition. + * render: if true render to unit quad. Green = success + * uniforms: an array of objects specifying uniforms to set prior to rendering. + * Each object should have the following keys: + * name: uniform variable name in the shader source. Uniform location will + * be queried based on its name. + * functionName: name of the function used to set the uniform. For example: + * 'uniform1i' + * value: value of the uniform to set. + */ +function runOneTest(gl, info) { + var passMsg = info.passMsg + debug(""); + debug("test: " + passMsg); + + var consoleDiv = document.getElementById("console"); + + var vIsDefault = false; + var fIsDefault = false; + + if (info.vShaderSource === undefined) { + if (info.vShaderId) { + info.vShaderSource = document.getElementById(info.vShaderId).text; + } else { + vIsDefault = true; + } + } + if (info.fShaderSource === undefined) { + if (info.fShaderId) { + info.fShaderSource = document.getElementById(info.fShaderId).text; + } else { + fIsDefault = true; + } + } + + var vLabel = (vIsDefault ? "default" : "test") + " vertex shader"; + var fLabel = (fIsDefault ? "default" : "test") + " fragment shader"; + if (vIsDefault) { + info.vShaderSource = defaultVertexShader; + info.vShaderSuccess = true; + } + if (fIsDefault) { + info.fShaderSource = defaultFragmentShader; + info.fShaderSuccess = true; + } + + if (vIsDefault != fIsDefault) { + // The language version of the default shader is chosen + // according to the language version of the other shader. + // We rely on "#version 300 es" being in this usual format. + // It must be on the first line of the shader according to the spec. + if (fIsDefault) { + // If we're using the default fragment shader, we need to make sure that + // it's language version matches with the vertex shader. + if (info.vShaderSource.split('\n')[0] == '#version 300 es') { + info.fShaderSource = defaultESSL3FragmentShader; + } + } else { + // If we're using the default vertex shader, we need to make sure that + // it's language version matches with the fragment shader. + if (info.fShaderSource.split('\n')[0] == '#version 300 es') { + info.vShaderSource = defaultESSL3VertexShader; + } + } + } + + var vSource = info.vShaderPrep ? info.vShaderPrep(info.vShaderSource) : + info.vShaderSource; + + if (!quietMode()) { + wtu.addShaderSource(consoleDiv, vLabel, vSource); + } + + // Reuse identical shaders so we test shared shader. + var vShader = vShaderDB[vSource]; + if (!vShader) { + // loadShader, with opt_skipCompileStatus: true. + vShader = wtu.loadShader(gl, vSource, gl.VERTEX_SHADER, null, null, null, null, true); + let compiledVShader = vShader; + if (vShader && !gl.getShaderParameter(vShader, gl.COMPILE_STATUS)) { + compiledVShader = null; + } + if (info.vShaderTest) { + if (!info.vShaderTest(compiledVShader)) { + testFailed("[vertex shader test] " + passMsg); + return; + } + } + // As per GLSL 1.0.17 10.27 we can only check for success on + // compileShader, not failure. + if (!info.ignoreResults && info.vShaderSuccess && !compiledVShader) { + testFailed("[unexpected vertex shader compile status] (expected: " + + info.vShaderSuccess + ") " + passMsg); + if (!quietMode() && vShader) { + const info = gl.getShaderInfoLog(vShader); + wtu.addShaderSource(consoleDiv, vLabel + " info log", info); + } + } + // Save the shaders so we test shared shader. + if (compiledVShader) { + vShaderDB[vSource] = compiledVShader; + } else { + vShader = null; + } + } + + var debugShaders = gl.getExtension('WEBGL_debug_shaders'); + if (debugShaders && vShader && !quietMode()) { + wtu.addShaderSource(consoleDiv, vLabel + " translated for driver", + debugShaders.getTranslatedShaderSource(vShader)); + } + + var fSource = info.fShaderPrep ? info.fShaderPrep(info.fShaderSource) : + info.fShaderSource; + + if (!quietMode()) { + wtu.addShaderSource(consoleDiv, fLabel, fSource); + } + + // Reuse identical shaders so we test shared shader. + var fShader = fShaderDB[fSource]; + if (!fShader) { + // loadShader, with opt_skipCompileStatus: true. + fShader = wtu.loadShader(gl, fSource, gl.FRAGMENT_SHADER, null, null, null, null, true); + let compiledFShader = fShader; + if (fShader && !gl.getShaderParameter(fShader, gl.COMPILE_STATUS)) { + compiledFShader = null; + } + if (info.fShaderTest) { + if (!info.fShaderTest(compiledFShader)) { + testFailed("[fragment shader test] " + passMsg); + return; + } + } + //debug(fShader == null ? "fail" : "succeed"); + // As per GLSL 1.0.17 10.27 we can only check for success on + // compileShader, not failure. + if (!info.ignoreResults && info.fShaderSuccess && !compiledFShader) { + testFailed("[unexpected fragment shader compile status] (expected: " + + info.fShaderSuccess + ") " + passMsg); + if (!quietMode() && fShader) { + const info = gl.getShaderInfoLog(fShader); + wtu.addShaderSource(consoleDiv, fLabel + " info log", info); + } + return; + } + + // Safe the shaders so we test shared shader. + if (compiledFShader) { + fShaderDB[fSource] = compiledFShader; + } else { + fShader = null; + } + } + + if (debugShaders && fShader && !quietMode()) { + wtu.addShaderSource(consoleDiv, fLabel + " translated for driver", + debugShaders.getTranslatedShaderSource(fShader)); + } + + if (vShader && fShader) { + var program = gl.createProgram(); + gl.attachShader(program, vShader); + gl.attachShader(program, fShader); + + if (vSource.indexOf("vPosition") >= 0) { + gl.bindAttribLocation(program, 0, "vPosition"); + } + if (vSource.indexOf("texCoord0") >= 0) { + gl.bindAttribLocation(program, 1, "texCoord0"); + } + gl.linkProgram(program); + var linked = (gl.getProgramParameter(program, gl.LINK_STATUS) != 0); + if (!linked) { + var error = gl.getProgramInfoLog(program); + log("*** Error linking program '"+program+"':"+error); + } + if (!info.ignoreResults && linked != info.linkSuccess) { + testFailed("[unexpected link status] (expected: " + + info.linkSuccess + ") " + passMsg); + return; + } + } else { + if (!info.ignoreResults && info.linkSuccess) { + testFailed("[link failed] " + passMsg); + return; + } + } + + if (parseInt(wtu.getUrlOptions().dumpShaders)) { + var vInfo = { + shader: vShader, + shaderSuccess: info.vShaderSuccess, + label: vLabel, + source: vSource + }; + var fInfo = { + shader: fShader, + shaderSuccess: info.fShaderSuccess, + label: fLabel, + source: fSource + }; + wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vInfo, fInfo); + } + + if (!info.render) { + testPassed(passMsg); + return; + } + + gl.useProgram(program); + + if (info.uniforms !== undefined) { + for (var i = 0; i < info.uniforms.length; ++i) { + var uniform = info.uniforms[i]; + var uniformLocation = gl.getUniformLocation(program, uniform.name); + if (uniformLocation !== null) { + if (uniform.functionName.includes("Matrix")) { + gl[uniform.functionName](uniformLocation, false, uniform.value); + } else { + gl[uniform.functionName](uniformLocation, uniform.value); + } + debug(uniform.name + ' set to ' + uniform.value); + } else { + debug('uniform ' + uniform.name + ' had null location and was not set'); + } + } + } + + if (info.uniformBlocks !== undefined) { + for (var i = 0; i < info.uniformBlocks.length; ++i) { + var uniformBlockIndex = gl.getUniformBlockIndex(program, info.uniformBlocks[i].name); + if (uniformBlockIndex !== null) { + gl.uniformBlockBinding(program, uniformBlockIndex, i); + debug(info.uniformBlocks[i].name + ' (index ' + uniformBlockIndex + ') bound to slot ' + i); + + var uboValueBuffer = gl.createBuffer(); + gl.bindBufferBase(gl.UNIFORM_BUFFER, i, uboValueBuffer); + gl.bufferData(gl.UNIFORM_BUFFER, info.uniformBlocks[i].value, info.uniformBlocks[i].usage || gl.STATIC_DRAW); + } else { + debug('uniform block' + info.uniformBlocks[i].name + ' had null block index and was not set'); + } + } + } + + wtu.setupUnitQuad(gl); + wtu.clearAndDrawUnitQuad(gl); + + var div = document.createElement("div"); + div.className = "testimages"; + wtu.insertImage(div, "result", wtu.makeImageFromCanvas(gl.canvas)); + div.appendChild(document.createElement('br')); + consoleDiv.appendChild(div); + + var tolerance = 0; + if (info.renderTolerance !== undefined) { + tolerance = info.renderTolerance; + } + if (info.renderColor !== undefined) { + wtu.checkCanvas(gl, info.renderColor, "should be expected color " + info.renderColor, tolerance); + } else { + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green", tolerance); + } +} + +function runTests(shaderInfos, opt_contextVersion) { + var wtu = WebGLTestUtils; + var canvas = document.createElement('canvas'); + canvas.width = 32; + canvas.height = 32; + var gl = wtu.create3DContext(canvas, undefined, opt_contextVersion); + if (!gl) { + testFailed("context does not exist"); + finishTest(); + return; + } + + for (var i = 0; i < shaderInfos.length; i++) { + runOneTest(gl, shaderInfos[i]); + } + + finishTest(); +}; + +function getSource(elem) { + var str = elem.text; + return str.replace(/^\s*/, '').replace(/\s*$/, ''); +} + +function getPassMessage(source) { + var lines = source.split('\n'); + return lines[0].substring(3); +} + +function getSuccess(msg) { + if (msg.indexOf("fail") >= 0) { + return false; + } + if (msg.indexOf("succeed") >= 0) { + return true; + } + testFailed("bad test description. Must have 'fail' or 'succeed'"); +} + +function setupTest() { + var info = {}; + + var vShaderElem = document.getElementById('vertexShader'); + if (vShaderElem) { + info.vShaderSource = getSource(vShaderElem); + info.passMsg = getPassMessage(info.vShaderSource); + info.vShaderSuccess = getSuccess(info.passMsg); + } + + var fShaderElem = document.getElementById('fragmentShader'); + if (fShaderElem) { + info.fShaderSource = getSource(fShaderElem); + info.passMsg = getPassMessage(info.fShaderSource); + info.fShaderSuccess = getSuccess(info.passMsg); + } + + // linkSuccess should be true if shader success value is undefined or true for both shaders. + info.linkSuccess = info.vShaderSuccess !== false && info.fShaderSuccess !== false; + + if (info.passMsg === undefined) { + testFailed("no test shader found."); + finishTest(); + return; + } + + return info; +} + +function runTest() { + var info = setupTest(); + description(info.passMsg); + runTests([info]); +} + +function runRenderTests(tests, opt_contextVersion) { + for (var ii = 0; ii < tests.length; ++ii) { + tests[ii].render = true + } + runTests(tests, opt_contextVersion); +} + +function runRenderTest() { + var info = setupTest(); + description(info.passMsg); + runRenderTests([info]); +} + +return { + runTest: runTest, + runTests: runTests, + runRenderTest: runRenderTest, + runRenderTests: runRenderTests +}; +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/glsl-constructor-tests-generator.js b/dom/canvas/test/webgl-conf/checkout/js/glsl-constructor-tests-generator.js new file mode 100644 index 0000000000..b5c1602980 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/glsl-constructor-tests-generator.js @@ -0,0 +1,919 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + + +var GLSLConstructorTestsGenerator = (function() { + +var wtu = WebGLTestUtils; + +// Shader code templates +var constructorVertexTemplate = [ + "attribute vec4 vPosition;", + + "precision mediump int;", + "precision mediump float;", + + // Colors used to signal correctness of component values comparison + "const vec4 green = vec4(0.0, 1.0, 0.0, 1.0);", + "const vec4 red = vec4(1.0, 0.0, 0.0, 1.0);", + + // Error bound used in comparison of floating point values + "$(errorBound)", + + "varying vec4 vColor;", + + "void main() {", + " $(argsList)", + + " $(type) v = $(type)($(argsConstr));", + + " if ($(checkCompVals))", + " vColor = green;", + " else", + " vColor = red;", + + " gl_Position = vPosition;", + "}" +].join("\n"); + + +var passThroughColorFragmentShader = [ + "precision mediump float;", + + "varying vec4 vColor;", + + "void main() {", + " gl_FragColor = vColor;", + "}" +].join('\n'); + + +var constructorFragmentTemplate = [ + "precision mediump int;", + "precision mediump float;", + + // Colors used to signal correctness of component values comparison + "const vec4 green = vec4(0.0, 1.0, 0.0, 1.0); ", + "const vec4 red = vec4(1.0, 0.0, 0.0, 1.0); ", + + // Error bound used in comparison of floating point values + "$(errorBound)", + + "void main() {", + " $(argsList)", + + " $(type) v = $(type)($(argsConstr));", + + " if ($(checkCompVals))", + " gl_FragColor = green;", + " else", + " gl_FragColor = red;", + "}" +].join("\n"); + + +// Coding of the different argument types +// s : scalar +// v2 : vec2 +// v3 : vec3 +// v4 : vec4 +// m2 : mat2 +// m3 : mat3 +// m4 : mat4 + +// Returns the dimensions of the type +// Count of columns, count of rows +function getTypeCodeDimensions(typeCode) { + switch (typeCode) { + case "s": return [1, 1]; + case "v2": return [1, 2]; + case "v3": return [1, 3]; + case "v4": return [1, 4]; + case "m2": return [2, 2]; + case "m3": return [3, 3]; + case "m4": return [4, 4]; + + default: + wtu.error("GLSLConstructorTestsGenerator.getTypeCodeDimensions(), unknown type code"); + debugger; + } +}; + + +// Returns the component count for the type code +function getTypeCodeComponentCount(typeCode) { + var dim = getTypeCodeDimensions(typeCode); + + return dim[0] * dim[1]; +} + + +// Returns glsl name of type code +function getGLSLBaseTypeName(typeCode) { + switch(typeCode) { + case "s": return ""; + case "v2": return "vec2"; + case "v3": return "vec3"; + case "v4": return "vec4"; + case "m2": return "mat2"; + case "m3": return "mat3"; + case "m4": return "mat4"; + + default: + wtu.error("GLSLConstructorTestsGenerator.getGLSLBaseTypeName(), unknown type code"); + debugger; + } +} + + +// Returns the scalar glsl type name related to the structured type +function getGLSLScalarType(targetType) { + switch(targetType[0]) { + case 'i': return "int"; + case 'b': return "bool"; + + case 'v': + case 'm': + return "float"; + + default: + wtu.error("GLSLConstructorTestsGenerator.getGLSLScalarType(), unknown target type"); + debugger; + } +} + + +// Returns the scalar prefix for the associated scalar type +function getGLSLScalarPrefix(targetType) { + switch(targetType[0]) { + case 'i': + case 'b': + return targetType[0]; + + case 'v': + case 'm': + return ''; + + default: + wtu.error("GLSLConstructorTestsGenerator.getGLSLScalarPrefix(), unknown target type"); + debugger; + } +} + + +// Returns the type for a specified target type and argument type code +function getGLSLArgumentType(typeCode, targetType) { + var baseType = getGLSLBaseTypeName(typeCode); + if (baseType !== "") { + if (typeCode[0] === "v") { + // Vectors come in different flavours + return getGLSLScalarPrefix(targetType) + baseType; + } + else + return baseType; + } + else + return getGLSLScalarType(targetType); +} + + +// Returns the glsl type of the argument components +function getGLSLArgumentComponentType(argTypeCode, targetType) { + var scalarType; + + if (argTypeCode[0] === "m") { + // Matrices are always floats + scalarType = "float"; + } + else + scalarType = getGLSLScalarType(targetType); + + return scalarType; +} + + +function getGLSLColumnSize(targetType) { + colSize = parseInt(targetType.slice(-1)); + + if (!isNaN(colSize)) + return colSize; + + wtu.error("GLSLConstructorTestsGenerator.getGLSLColumnSize(), invalid target type"); + debugger; +} + + +// Returns correct string representation of scalar value +function getScalarTypeValStr(val, scalarType) { + if (val == null) + debugger; + + switch (scalarType) { + case "float": return val.toFixed(1); + case "int": return val; + case "bool": return (val === 0) ? "false" : "true"; + + default: + wtu.error("GLSLConstructorTestsGenerator.getScalarTypeValStr(), unknown scalar type"); + debugger; + } +} + + +// Returns true if the glsl type name is a matrix +function isGLSLTypeMatrix(type) { + return (type.indexOf("mat") !== -1); +} + + +// Returns true if the glsl type name is a vector +function isGLSLTypeVector(type) { + return (type.indexOf("vec") !== -1); +} + + +// Returns the count of components +function getGLSLTypeComponentCount(type) { + var colSize = getGLSLColumnSize(type); + + if (isGLSLTypeMatrix(type)) + return colSize * colSize; + else + return colSize; +} + + +// Returns the constructor expression with the components set to a sequence of scalar values +// Like vec3(1.0, 2.0, 3.0) +function getComponentSequenceConstructorExpression(typeCode, firstCompValue, targetType) { + var scalarType = getGLSLArgumentComponentType(typeCode, targetType); + + if (typeCode === "s") { + // Scalar + return getScalarTypeValStr(firstCompValue, scalarType) + ";"; + } + else { + // Structured typeargTypeCode[0] === "m" + compCount = getTypeCodeComponentCount(typeCode); + var constrExpParts = new Array(compCount); + for (var aa = 0; aa < compCount; ++aa) + constrExpParts[aa] = getScalarTypeValStr(firstCompValue + aa, scalarType); + + return getGLSLArgumentType(typeCode, targetType) + "(" + constrExpParts.join(", ") + ");"; + } +} + + +// Returns the expression to select a component of the structured type +function getComponentSelectorExpStr(targetType, compIx) { + if (isGLSLTypeMatrix(targetType)) { + var colRowIx = getColRowIndexFromLinearIndex(compIx, getGLSLColumnSize(targetType)); + return "v[" + colRowIx.colIx + "][" + colRowIx.rowIx + "]"; + } + else + return "v[" + compIx + "]"; +} + + +// Returns expression which validates the components set by the constructor expression +function getComponentValidationExpression(refCompVals, targetType) { + // Early out for invalid arguments + if (refCompVals.length === 0) + return "false"; + + var scalarType = getGLSLScalarType(targetType); + var checkComponentValueParts = new Array(refCompVals.length); + for (var cc = 0; cc < refCompVals.length; ++cc) { + var val_str = getScalarTypeValStr(refCompVals[cc], scalarType); + var comp_sel_exp = getComponentSelectorExpStr(targetType, cc); + if (scalarType === "float") { + // Comparison of floating point values with error bound + checkComponentValueParts[cc] = "abs(" + comp_sel_exp + " - " + val_str + ") <= errorBound"; + } + else { + // Simple comparison to expected value + checkComponentValueParts[cc] = comp_sel_exp + " == " + val_str; + } + } + + return checkComponentValueParts.join(" && "); +} + + +// Returns substitution parts to turn the shader template into testable shader code +function getTestShaderParts(targetType, argExp, firstCompValue) { + // glsl code of declarations of arguments + var argsListParts = new Array(argExp.length); + + // glsl code of constructor expression + var argsConstrParts = new Array(argExp.length); + + // glsl type expression + var typeExpParts = new Array(argExp.length); + for (var aa = 0; aa < argExp.length; ++aa) { + var typeCode = argExp[aa]; + var argCompCount = getTypeCodeComponentCount(typeCode); + var argName = "a" + aa; + var argType = getGLSLArgumentType(typeCode, targetType); + var argConstrExp = argType + " " + argName + " = " + getComponentSequenceConstructorExpression(typeCode, firstCompValue, targetType); + + // Add construction of one argument + // Indent if not first argument + argsListParts[aa] = ((aa > 0) ? " " : "") + argConstrExp; + + // Add argument name to target type argument list + argsConstrParts[aa] = argName; + + // Add type name to type expression + typeExpParts[aa] = argType; + + // Increment argument component value so all argument component arguments have a unique value + firstCompValue += argCompCount; + } + + return { + argsList: argsListParts.join("\n") + "\n", + argsConstr: argsConstrParts.join(", "), + typeExp: targetType + "(" + typeExpParts.join(", ") + ")" + }; +} + + +// Utility functions to manipulate the array of reference values + +// Returns array filled with identical values +function getArrayWithIdenticalValues(size, val) { + var matArray = new Array(size); + for (var aa = 0; aa < size; ++aa) + matArray[aa] = val; + + return matArray; +} + + +// Returns array filled with increasing values from a specified start value +function getArrayWithIncreasingValues(size, start) { + var matArray = new Array(size); + for (var aa = 0; aa < size; ++aa) + matArray[aa] = start + aa; + + return matArray; +} + + +// Utility functions to manipulate the array of reference values if the target type is a matrix + +// Returns an array which is the column order layout of a square matrix where the diagonal is set to a specified value +function matCompArraySetDiagonal(matArray, diagVal) { + // The entries for the diagonal start at array index 0 and increase + // by column size + 1 + var colSize = Math.round(Math.sqrt(matArray.length)); + var dIx = 0; + do { + matArray[dIx] = diagVal; + dIx += (colSize + 1); + } + while (dIx < colSize * colSize); + + return matArray; +} + + +// Returns an array which contains the values of an identity matrix read out in column order +function matCompArrayCreateDiagonalMatrix(colSize, diagVal) { + var size = colSize * colSize; + var matArray = new Array(size); + for (var aa = 0; aa < size; ++aa) + matArray[aa] = 0; + + return matCompArraySetDiagonal(matArray, diagVal); +} + + +// Returns the column and row index from the linear index if the components of the matrix are stored in column order in an array +// in a one dimensional array in column order +function getColRowIndexFromLinearIndex(linIx, colSize) { + return { + colIx: Math.floor(linIx / colSize), + rowIx: linIx % colSize + }; +} + + +// Returns the linear index for matrix column and row index for a specified matrix size +function getLinearIndexFromColRowIndex(rowColIx, colSize) { + return rowColIx.colIx * colSize + rowColIx.rowIx; +} + + +// Returns a matrix set from another matrix +function matCompArraySetMatrixFromMatrix(dstColSize, srcMatArray) { + // Overwrite components from destination with the source component values at the same col, row coordinates + var dstMatArray = matCompArrayCreateDiagonalMatrix(dstColSize, 1); + + var srcColSize = Math.round(Math.sqrt(srcMatArray.length)); + + for (var c_ix = 0; c_ix < srcMatArray.length; ++c_ix) { + var srcMatIx = getColRowIndexFromLinearIndex(c_ix, srcColSize); + if (srcMatIx.colIx < dstColSize && srcMatIx.rowIx < dstColSize) { + // Source matrix coordinates are valid destination matrix coordinates + dstMatArray[getLinearIndexFromColRowIndex(srcMatIx, dstColSize)] = srcMatArray[c_ix]; + } + } + + return dstMatArray; +} + + +// Returns the glsl code to verify if the components are set correctly +// and the message to display for the test +function getConstructorExpressionInfo(targetType, argExp, firstCompValue) { + var argCompCountsSum = 0; + var argCompCounts = new Array(argExp.length); + for (var aa = 0; aa < argExp.length; ++aa) { + argCompCounts[aa] = getTypeCodeComponentCount(argExp[aa]); + argCompCountsSum += argCompCounts[aa]; + } + + var targetCompCount = getGLSLTypeComponentCount(targetType); + + var refCompVals; + var testMsg; + var valid; + + if (argCompCountsSum === 0) { + // A constructor needs at least one argument + refCompVals = []; + testMsg = "invalid (no arguments)"; + valid = false; + } + else { + if (isGLSLTypeVector(targetType)) { + if (argCompCountsSum === 1) { + // One scalar argument + // Vector constructor with one scalar argument set all components to the same value + refCompVals = getArrayWithIdenticalValues(targetCompCount, firstCompValue); + testMsg = "valid (all components set to the same value)"; + valid = true; + } + else { + // Not one scalar argument + if (argCompCountsSum < targetCompCount) { + // Not all components set + refCompVals = []; + testMsg = "invalid (not enough arguments)"; + valid = false; + } + else { + // argCompCountsSum >= targetCompCount + // All components set + var lastArgFirstCompIx = argCompCountsSum - argCompCounts[argCompCounts.length - 1]; + + if (lastArgFirstCompIx < targetCompCount) { + // First component of last argument is used + refCompVals = getArrayWithIncreasingValues(targetCompCount, firstCompValue); + testMsg = "valid"; + valid = true; + } + else { + // First component of last argument is not used + refCompVals = []; + testMsg = "invalid (unused argument)"; + valid = false; + } + } + } + } + else { + // Matrix target type + if (argCompCountsSum === 1) { + // One scalar argument + // Matrix constructors with one scalar set all components on the diagonal to the same value + // All other components are set to zero + refCompVals = matCompArrayCreateDiagonalMatrix(Math.round(Math.sqrt(targetCompCount)), firstCompValue); + testMsg = "valid (diagonal components set to the same value, off-diagonal components set to zero)"; + valid = true; + } + else { + // Not one scalar argument + if (argExp.length === 1 && argExp[0][0] === "m") { + // One single matrix argument + var dstColSize = getGLSLColumnSize(targetType); + refCompVals = matCompArraySetMatrixFromMatrix(dstColSize, getArrayWithIncreasingValues(getTypeCodeComponentCount(argExp[0]), firstCompValue)); + testMsg = "valid, components at corresponding col, row indices are set from argument, other components are set from identity matrix"; + valid = true; + } + else { + // More than one argument or one argument not of type matrix + // Can be treated in the same manner + // Arguments can not be of type matrix + var matFound = false; + for (var aa = 0; aa < argExp.length; ++aa) + if (argExp[aa][0] === "m") + matFound = true; + + if (matFound) { + refCompVals = []; + testMsg = "invalid, argument list greater than one contains matrix type"; + valid = false; + } + else { + if (argCompCountsSum < targetCompCount) { + refCompVals = []; + testMsg = "invalid (not enough arguments)"; + valid = false; + } + else { + // argCompCountsSum >= targetCompCount + // All components set + var lastArgFirstCompIx = argCompCountsSum - argCompCounts[argCompCounts.length - 1]; + + if (lastArgFirstCompIx < targetCompCount) { + // First component of last argument is used + refCompVals = getArrayWithIncreasingValues(targetCompCount, firstCompValue); + testMsg = "valid"; + valid = true; + } + else { + // First component of last argument is not used + refCompVals = []; + testMsg = "invalid (unused argument)"; + valid = false; + } + } + } + } + } + } + } + + // Check if no case is missed + if (testMsg == null || valid == null) { + wtu.error("GLSLConstructorTestsGenerator.getConstructorExpressionInfo(), info not set"); + debugger; + } + + return { + refCompVals: refCompVals, + testMsg: testMsg, + valid: valid + }; +} + + +// Returns a vertex shader testcase and a fragment shader testcase +function getVertexAndFragmentShaderTestCase(targetType, argExp) { + var firstCompValue = 0; + if (isGLSLTypeMatrix(targetType)) { + // Use value different from 0 and 1 + // 0 and 1 are values used by matrix constructed from a matrix or a single scalar + firstCompValue = 2; + } + + var argCode = getTestShaderParts (targetType, argExp, firstCompValue); + var expInfo = getConstructorExpressionInfo(targetType, argExp, firstCompValue); + + var substitutions = { + type: targetType, + errorBound: (getGLSLScalarType(targetType) === "float") ? "const float errorBound = 1.0E-5;" : "", + argsList: argCode.argsList, + argsConstr: argCode.argsConstr, + checkCompVals: getComponentValidationExpression(expInfo.refCompVals, targetType) + }; + + return [ { + // Test constructor argument list in vertex shader + vShaderSource: wtu.replaceParams(constructorVertexTemplate, substitutions), + vShaderSuccess: expInfo.valid, + fShaderSource: passThroughColorFragmentShader, + fShaderSuccess: true, + linkSuccess: expInfo.valid, + passMsg: "Vertex shader : " + argCode.typeExp + ", " + expInfo.testMsg, + render: expInfo.valid + }, { + // Test constructor argument list in fragment shader + fShaderSource: wtu.replaceParams(constructorFragmentTemplate, substitutions), + fShaderSuccess: expInfo.valid, + linkSuccess: expInfo.valid, + passMsg: "Fragment shader : " + argCode.typeExp + ", " + expInfo.testMsg, + render: expInfo.valid + } + ]; +} + + +// Incrementing the argument expressions +// Utility object which defines the order of incrementing the argument types +var typeCodeIncrementer = { + s: { typeCode: "v2", order: 0 }, + v2: { typeCode: "v3", order: 1 }, + v3: { typeCode: "v4", order: 2 }, + v4: { typeCode: "m2", order: 3 }, + m2: { typeCode: "m3", order: 4 }, + m3: { typeCode: "m4", order: 5 }, + m4: { typeCode: "s", order: 6 }, + first: "s" +} + + +// Returns the next argument sequence +function getNextArgumentSequence(inSeq) { + var nextSeq; + if (inSeq.length === 0) { + // Current argument sequence is empty, add first argument + nextSeq = [typeCodeIncrementer.first]; + } + else { + nextSeq = new Array(inSeq.length); + var overflow = true; + for (var aa = 0; aa < inSeq.length; ++aa) { + var currArg = inSeq[aa]; + if (overflow) { + // Increment the current argument type + var nextArg = typeCodeIncrementer[currArg].typeCode; + nextSeq[aa] = nextArg; + overflow = (nextArg === typeCodeIncrementer.first); + } + else { + // Copy remainder of sequence + nextSeq[aa] = currArg; + } + } + + if (overflow) { + nextSeq.push(typeCodeIncrementer.first); + } + } + + return nextSeq; +} + + +// Returns true if two argument expressions are equal +function areArgExpEqual(expA, expB) { + if (expA.length !== expB.length) + return false; + + for (var aa = 0; aa < expA.length; ++aa) + if (expA[aa] !== expB[aa]) + return false; + + return true; +} + + +// Returns true if first argument expression is smaller +// (comes before the second one in iterating order) +// compared to the second argument expression +function isArgExpSmallerOrEqual(argExpA, argExpB) { + var aLen = argExpA.length; + var bLen = argExpB.length; + if (aLen !== bLen) + return (aLen < bLen); + + // Argument type expression lengths are equal + for (var aa = aLen - 1; aa >= 0; --aa) { + var argA = argExpA[aa]; + var argB = argExpB[aa]; + + if (argA !== argB) { + var aOrder = typeCodeIncrementer[argA].order; + var bOrder = typeCodeIncrementer[argB].order; + if (aOrder !== bOrder) + return (aOrder < bOrder); + } + } + + // Argument type expressions are equal + return true; +} + + +// Returns the next argument expression from sequence set +// Returns null if end is reached +function getNextArgumentExpression(testExp, testSet) { + var testInterval = testSet[testExp.ix]; + + if (areArgExpEqual(testExp.argExp, testInterval[1])) { + // End of current interval reached + if (testExp.ix === testSet.length - 1) { + // End of set reached + return null; + } + else { + // Return first argument expression of next interval + var nextIx = testExp.ix + 1; + return { ix: nextIx, argExp: testSet[nextIx][0] }; + } + } + else { + // Return next expression in current interval + return { ix: testExp.ix, argExp: getNextArgumentSequence(testExp.argExp) }; + } +} + + +// Returns an array of the parts in the string separated by commas and with the white space trimmed +function convertCsvToArray(str) { + // Checks type codes in input + function checkInput(el, ix, arr) { + var typeCode = el.trim(); + if (!(typeCode in typeCodeIncrementer) && typeCode !== "first") { + wtu.error("GLSLConstructorTestsGenerator.convertCsvToArray(), unknown type code" + typeCode); + debugger; + } + + arr[ix] = typeCode; + } + + var spArr = str.split(","); + + // Convert empty string to empty array + if (spArr.length === 1 && spArr[0].trim() === "") + spArr = []; + + spArr.forEach(checkInput); + + return spArr; +} + + +// Processes the set of specified test sequences +function processInputs(testSequences) { + var testSet = new Array(testSequences.length); + for (var tt = 0; tt < testSequences.length; ++tt) { + var interval = testSequences[tt]; + var bounds = interval.split("-"); + var begin = convertCsvToArray(bounds[0]); + var end = convertCsvToArray(bounds[bounds.length - 1]); + + // Check if interval is valid + if (!isArgExpSmallerOrEqual(begin, end)) { + wtu.error("GLSLConstructorTestsGenerator.processInputs(), interval not valid"); + debugger; + } + + testSet[tt] = [ begin, end ]; + } + + return testSet; +} + + +/** + * Returns list of test cases for vector types + * All combinations of arguments up to one unused argument of one component are tested + * @param {targetType} Name of target type to test the constructor expressions on + * @param {testSet} Set of intervals of argument sequences to test + */ +function getConstructorTests(targetType, testSequences) { + // List of tests to return + var testInfos = []; + + // List of argument types + var testSet = processInputs(testSequences); + var testExp = { ix: 0, argExp: testSet[0][0] }; + + do { + // Add one vertex shader test case and one fragment shader test case + testInfos = testInfos.concat(getVertexAndFragmentShaderTestCase(targetType, testExp.argExp)); + + // Generate next argument expression + testExp = getNextArgumentExpression(testExp, testSet); + } + while (testExp != null); + + return testInfos; +} + + +// Returns default test argument expression set +// For details on input format : see bottom of file +function getDefaultTestSet(targetType) { + switch(targetType) { + case "vec2": + case "ivec2": + case "bvec2": + return [ + // No arguments and all single argument expressions + " - m4", + + // All two argument expressions with a scalar as second argument + "s, s - m4, s", + + // All two arguments expressions with a scalar as first argument + "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4", + + // Three argument expression + "s, s, s" + ]; + + case "vec3": + case "ivec3": + case "bvec3": + return [ + // No arguments and all single argument expressions + " - m4", + + // All two argument expressions with a scalar as second argument + "s, s - m4, s", + + // All two argument expressions with a scalar as first argument + "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4", + + // All three argument expressions with two scalars as second and third argument + "s, s, s - m4, s, s", + + // All three argument expressions with two scalars as first and second argument + "s, s, v2", "s, s, v3", "s, s, v4", "s, s, m2", "s, s, m3", "s, s, m4", + + // Four argument expression + "s, s, s, s" + ]; + + case "vec4": + case "ivec4": + case "bvec4": + case "mat2": + return [ + // No arguments and all single argument expressions + " - m4", + + // All two argument expressions with a scalar as second argument + "s, s - m4, s", + + // All two argument expressions with a scalar as first argument + "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4", + + // All three argument expressions with two scalars as second and third argument + "s, s, s - m4, s, s", + + // All three argument expressions with two scalars as first and second argument + "s, s, v2", "s, s, v3", "s, s, v4", "s, s, m2", "s, s, m3", "s, s, m4", + + // All four argument expressions with three scalars as second, third and fourth argument + "s, s, s, s - m4, s, s, s", + + // All four argument expressions with three scalars as first, second and third argument + "s, s, s, v2", "s, s, s, v3", "s, s, s, v4", "s, s, s, m2", "s, s, s, m3", "s, s, s, m4", + + // Five argument expression + "s, s, s, s, s" + ]; + + case "mat3": + case "mat4": + return [ + // No arguments and all single argument expressions + " - m4", + + // All two argument expressions with a scalar as second argument + "s, s - m4, s", + + // All two argument expressions with a scalar as first argument + "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4", + + // Several argument sequences + "v4, s, v4", "v4, s, v3, v2", "v4, v4, v3, v2", "v4, v4, v4, v4", "v2, v2, v2, v2, v2", "v2, v2, v2, v2, v2, v2, v2, v2", + "v3, v3, v3", "v3, v3, v3, s", "v3, v3, v3, v3, v3, s", "v3, v3, v3, v3, v3, s, s", + ]; + } +} + + +// Return publics +return { + getConstructorTests: getConstructorTests, + getDefaultTestSet: getDefaultTestSet +}; + +}()); + + +// Input is an array of intervals of argument types +// The generated test argument sequences are from (including) the lower interval boundary +// until (including) the upper boundary +// Coding and order of the different argument types : +// s : scalar +// v2 : vec2 +// v3 : vec3 +// v4 : vec4 +// m2 : mat2 +// m3 : mat3 +// m4 : mat4 + +// One interval is put in one string +// Low and high bound are separated by a dash. +// If there is no dash it is regarded as an interval of one expression +// The individual argument codes are separated by commas +// The individual arguments are incremented from left to right +// The left most argument is the one which is incremented first +// Once the left most arguments wraps the second argument is increased +// Examples : +// "s - m4" : All single arguments from scalar up to (including) mat4 +// "m2, s - m4, s" : All two argument expressions with a matrix argument as first argument and a scalar as second argument +// " - m4, m4" : The empty argument, all one arguments and all two argument expressions +// "m2, s, v3, m4" : One 4 argument expression : mat2, scalar, vec3, mat4 diff --git a/dom/canvas/test/webgl-conf/checkout/js/glsl-generator.js b/dom/canvas/test/webgl-conf/checkout/js/glsl-generator.js new file mode 100644 index 0000000000..d0b65bcb4b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/glsl-generator.js @@ -0,0 +1,1234 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +GLSLGenerator = (function() { + +var vertexShaderTemplate = [ + "attribute vec4 aPosition;", + "", + "varying vec4 vColor;", + "", + "$(extra)", + "$(emu)", + "", + "void main()", + "{", + " gl_Position = aPosition;", + " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));", + " vec4 color = vec4(", + " texcoord,", + " texcoord.x * texcoord.y,", + " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);", + " $(test)", + "}" +].join("\n"); + +var fragmentShaderTemplate = [ + "precision mediump float;", + "", + "varying vec4 vColor;", + "", + "$(extra)", + "$(emu)", + "", + "void main()", + "{", + " $(test)", + "}" +].join("\n"); + +var baseVertexShader = [ + "attribute vec4 aPosition;", + "", + "varying vec4 vColor;", + "", + "void main()", + "{", + " gl_Position = aPosition;", + " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));", + " vColor = vec4(", + " texcoord,", + " texcoord.x * texcoord.y,", + " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);", + "}" +].join("\n"); + +var baseVertexShaderWithColor = [ + "attribute vec4 aPosition;", + "attribute vec4 aColor;", + "", + "varying vec4 vColor;", + "", + "void main()", + "{", + " gl_Position = aPosition;", + " vColor = aColor;", + "}" +].join("\n"); + +var baseFragmentShader = [ + "precision mediump float;", + "varying vec4 vColor;", + "", + "void main()", + "{", + " gl_FragColor = vColor;", + "}" +].join("\n"); + +var types = [ + { type: "float", + code: [ + "float $(func)_emu($(args)) {", + " return $(func)_base($(baseArgs));", + "}"].join("\n") + }, + { type: "vec2", + code: [ + "vec2 $(func)_emu($(args)) {", + " return vec2(", + " $(func)_base($(baseArgsX)),", + " $(func)_base($(baseArgsY)));", + "}"].join("\n") + }, + { type: "vec3", + code: [ + "vec3 $(func)_emu($(args)) {", + " return vec3(", + " $(func)_base($(baseArgsX)),", + " $(func)_base($(baseArgsY)),", + " $(func)_base($(baseArgsZ)));", + "}"].join("\n") + }, + { type: "vec4", + code: [ + "vec4 $(func)_emu($(args)) {", + " return vec4(", + " $(func)_base($(baseArgsX)),", + " $(func)_base($(baseArgsY)),", + " $(func)_base($(baseArgsZ)),", + " $(func)_base($(baseArgsW)));", + "}"].join("\n") + } +]; + +var bvecTypes = [ + { type: "bvec2", + code: [ + "bvec2 $(func)_emu($(args)) {", + " return bvec2(", + " $(func)_base($(baseArgsX)),", + " $(func)_base($(baseArgsY)));", + "}"].join("\n") + }, + { type: "bvec3", + code: [ + "bvec3 $(func)_emu($(args)) {", + " return bvec3(", + " $(func)_base($(baseArgsX)),", + " $(func)_base($(baseArgsY)),", + " $(func)_base($(baseArgsZ)));", + "}"].join("\n") + }, + { type: "bvec4", + code: [ + "vec4 $(func)_emu($(args)) {", + " return bvec4(", + " $(func)_base($(baseArgsX)),", + " $(func)_base($(baseArgsY)),", + " $(func)_base($(baseArgsZ)),", + " $(func)_base($(baseArgsW)));", + "}"].join("\n") + } +]; + +var replaceRE = /\$\((\w+)\)/g; + +var replaceParams = function(str) { + var args = arguments; + return str.replace(replaceRE, function(str, p1, offset, s) { + for (var ii = 1; ii < args.length; ++ii) { + if (args[ii][p1] !== undefined) { + return args[ii][p1]; + } + } + throw "unknown string param '" + p1 + "'"; + }); +}; + +var generateReferenceShader = function( + shaderInfo, template, params, typeInfo, test) { + var input = shaderInfo.input; + var output = shaderInfo.output; + var feature = params.feature; + var testFunc = params.testFunc; + var emuFunc = params.emuFunc || ""; + var extra = params.extra || ''; + var args = params.args || "$(type) value"; + var type = typeInfo.type; + var typeCode = typeInfo.code; + + var baseArgs = params.baseArgs || "value$(field)"; + var baseArgsX = replaceParams(baseArgs, {field: ".x"}); + var baseArgsY = replaceParams(baseArgs, {field: ".y"}); + var baseArgsZ = replaceParams(baseArgs, {field: ".z"}); + var baseArgsW = replaceParams(baseArgs, {field: ".w"}); + var baseArgs = replaceParams(baseArgs, {field: ""}); + + test = replaceParams(test, { + input: input, + output: output, + func: feature + "_emu" + }); + emuFunc = replaceParams(emuFunc, { + func: feature + }); + args = replaceParams(args, { + type: type + }); + typeCode = replaceParams(typeCode, { + func: feature, + type: type, + args: args, + baseArgs: baseArgs, + baseArgsX: baseArgsX, + baseArgsY: baseArgsY, + baseArgsZ: baseArgsZ, + baseArgsW: baseArgsW + }); + var shader = replaceParams(template, { + extra: extra, + emu: emuFunc + "\n\n" + typeCode, + test: test + }); + return shader; +}; + +var generateTestShader = function( + shaderInfo, template, params, test) { + var input = shaderInfo.input; + var output = shaderInfo.output; + var feature = params.feature; + var testFunc = params.testFunc; + var extra = params.extra || ''; + + test = replaceParams(test, { + input: input, + output: output, + func: feature + }); + var shader = replaceParams(template, { + extra: extra, + emu: '', + test: test + }); + return shader; +}; + +function _reportResults(refData, refImg, testData, testImg, tolerance, + width, height, ctx, imgData, wtu, canvas2d, consoleDiv) { + var same = true; + var firstFailure = null; + for (var yy = 0; yy < height; ++yy) { + for (var xx = 0; xx < width; ++xx) { + var offset = (yy * width + xx) * 4; + var imgOffset = ((height - yy - 1) * width + xx) * 4; + imgData.data[imgOffset + 0] = 0; + imgData.data[imgOffset + 1] = 0; + imgData.data[imgOffset + 2] = 0; + imgData.data[imgOffset + 3] = 255; + if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance || + Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance || + Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance || + Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) { + var detail = 'at (' + xx + ',' + yy + '): ref=(' + + refData[offset + 0] + ',' + + refData[offset + 1] + ',' + + refData[offset + 2] + ',' + + refData[offset + 3] + ') test=(' + + testData[offset + 0] + ',' + + testData[offset + 1] + ',' + + testData[offset + 2] + ',' + + testData[offset + 3] + ') tolerance=' + tolerance; + consoleDiv.appendChild(document.createTextNode(detail)); + consoleDiv.appendChild(document.createElement('br')); + if (!firstFailure) { + firstFailure = ": " + detail; + } + imgData.data[imgOffset] = 255; + same = false; + } + } + } + + var diffImg = null; + if (!same) { + ctx.putImageData(imgData, 0, 0); + diffImg = wtu.makeImageFromCanvas(canvas2d); + } + + var div = document.createElement("div"); + div.className = "testimages"; + wtu.insertImage(div, "ref", refImg); + wtu.insertImage(div, "test", testImg); + if (diffImg) { + wtu.insertImage(div, "diff", diffImg); + } + div.appendChild(document.createElement('br')); + + consoleDiv.appendChild(div); + + if (!same) { + testFailed("images are different" + (firstFailure ? firstFailure : "")); + } else { + testPassed("images are the same"); + } + + consoleDiv.appendChild(document.createElement('hr')); +} + +var runFeatureTest = function(params) { + var wtu = WebGLTestUtils; + var gridRes = params.gridRes; + var vertexTolerance = params.tolerance || 0; + var fragmentTolerance = params.tolerance || 1; + if ('fragmentTolerance' in params) + fragmentTolerance = params.fragmentTolerance; + + description("Testing GLSL feature: " + params.feature); + + var width = 32; + var height = 32; + + var consoleDiv = document.getElementById("console"); + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var gl = wtu.create3DContext(canvas, { premultipliedAlpha: false }); + if (!gl) { + testFailed("context does not exist"); + finishTest(); + return; + } + + var canvas2d = document.createElement('canvas'); + canvas2d.width = width; + canvas2d.height = height; + var ctx = canvas2d.getContext("2d"); + var imgData = ctx.getImageData(0, 0, width, height); + + var shaderInfos = [ + { type: "vertex", + input: "color", + output: "vColor", + vertexShaderTemplate: vertexShaderTemplate, + fragmentShaderTemplate: baseFragmentShader, + tolerance: vertexTolerance + }, + { type: "fragment", + input: "vColor", + output: "gl_FragColor", + vertexShaderTemplate: baseVertexShader, + fragmentShaderTemplate: fragmentShaderTemplate, + tolerance: fragmentTolerance + } + ]; + for (var ss = 0; ss < shaderInfos.length; ++ss) { + var shaderInfo = shaderInfos[ss]; + var tests = params.tests; + var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types); + // Test vertex shaders + for (var ii = 0; ii < tests.length; ++ii) { + var type = testTypes[ii]; + if (params.simpleEmu) { + type = { + type: type.type, + code: params.simpleEmu + }; + } + debug(""); + var str = replaceParams(params.testFunc, { + func: params.feature, + type: type.type, + arg0: type.type + }); + var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader"; + debug(passMsg); + + var referenceVertexShaderSource = generateReferenceShader( + shaderInfo, + shaderInfo.vertexShaderTemplate, + params, + type, + tests[ii]); + var referenceFragmentShaderSource = generateReferenceShader( + shaderInfo, + shaderInfo.fragmentShaderTemplate, + params, + type, + tests[ii]); + var testVertexShaderSource = generateTestShader( + shaderInfo, + shaderInfo.vertexShaderTemplate, + params, + tests[ii]); + var testFragmentShaderSource = generateTestShader( + shaderInfo, + shaderInfo.fragmentShaderTemplate, + params, + tests[ii]); + + + debug(""); + var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference'); + var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference'); + var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test'); + var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test'); + debug(""); + + if (parseInt(wtu.getUrlOptions().dumpShaders)) { + var vRefInfo = { + shader: referenceVertexShader, + shaderSuccess: true, + label: "reference vertex shader", + source: referenceVertexShaderSource + }; + var fRefInfo = { + shader: referenceFragmentShader, + shaderSuccess: true, + label: "reference fragment shader", + source: referenceFragmentShaderSource + }; + wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo); + + var vTestInfo = { + shader: testVertexShader, + shaderSuccess: true, + label: "test vertex shader", + source: testVertexShaderSource + }; + var fTestInfo = { + shader: testFragmentShader, + shaderSuccess: true, + label: "test fragment shader", + source: testFragmentShaderSource + }; + wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo); + } + + var refData = draw( + referenceVertexShader, referenceFragmentShader); + var refImg = wtu.makeImageFromCanvas(canvas); + if (ss == 0) { + var testData = draw( + testVertexShader, referenceFragmentShader); + } else { + var testData = draw( + referenceVertexShader, testFragmentShader); + } + var testImg = wtu.makeImageFromCanvas(canvas); + + _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance, + width, height, ctx, imgData, wtu, canvas2d, consoleDiv); + } + } + + finishTest(); + + function draw(vertexShader, fragmentShader) { + var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed); + + var posLoc = gl.getAttribLocation(program, "aPosition"); + wtu.setupIndexedQuad(gl, gridRes, posLoc); + + gl.useProgram(program); + wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw"); + + var img = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img); + return img; + } + +}; + +var runBasicTest = function(params) { + var wtu = WebGLTestUtils; + var gridRes = params.gridRes; + var vertexTolerance = params.tolerance || 0; + var fragmentTolerance = vertexTolerance; + if ('fragmentTolerance' in params) + fragmentTolerance = params.fragmentTolerance || 0; + + description("Testing : " + document.getElementsByTagName("title")[0].innerText); + + var width = 32; + var height = 32; + + var consoleDiv = document.getElementById("console"); + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var gl = wtu.create3DContext(canvas); + if (!gl) { + testFailed("context does not exist"); + finishTest(); + return; + } + + var canvas2d = document.createElement('canvas'); + canvas2d.width = width; + canvas2d.height = height; + var ctx = canvas2d.getContext("2d"); + var imgData = ctx.getImageData(0, 0, width, height); + + var shaderInfos = [ + { type: "vertex", + input: "color", + output: "vColor", + vertexShaderTemplate: vertexShaderTemplate, + fragmentShaderTemplate: baseFragmentShader, + tolerance: vertexTolerance + }, + { type: "fragment", + input: "vColor", + output: "gl_FragColor", + vertexShaderTemplate: baseVertexShader, + fragmentShaderTemplate: fragmentShaderTemplate, + tolerance: fragmentTolerance + } + ]; + for (var ss = 0; ss < shaderInfos.length; ++ss) { + var shaderInfo = shaderInfos[ss]; + var tests = params.tests; +// var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types); + // Test vertex shaders + for (var ii = 0; ii < tests.length; ++ii) { + var test = tests[ii]; + debug(""); + var passMsg = "Testing: " + test.name + " in " + shaderInfo.type + " shader"; + debug(passMsg); + + function genShader(shaderInfo, template, shader, subs) { + shader = replaceParams(shader, subs, { + input: shaderInfo.input, + output: shaderInfo.output + }); + shader = replaceParams(template, subs, { + test: shader, + emu: "", + extra: "" + }); + return shader; + } + + var referenceVertexShaderSource = genShader( + shaderInfo, + shaderInfo.vertexShaderTemplate, + test.reference.shader, + test.reference.subs); + var referenceFragmentShaderSource = genShader( + shaderInfo, + shaderInfo.fragmentShaderTemplate, + test.reference.shader, + test.reference.subs); + var testVertexShaderSource = genShader( + shaderInfo, + shaderInfo.vertexShaderTemplate, + test.test.shader, + test.test.subs); + var testFragmentShaderSource = genShader( + shaderInfo, + shaderInfo.fragmentShaderTemplate, + test.test.shader, + test.test.subs); + + debug(""); + var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference'); + var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference'); + var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test'); + var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test'); + debug(""); + + if (parseInt(wtu.getUrlOptions().dumpShaders)) { + var vRefInfo = { + shader: referenceVertexShader, + shaderSuccess: true, + label: "reference vertex shader", + source: referenceVertexShaderSource + }; + var fRefInfo = { + shader: referenceFragmentShader, + shaderSuccess: true, + label: "reference fragment shader", + source: referenceFragmentShaderSource + }; + wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo); + + var vTestInfo = { + shader: testVertexShader, + shaderSuccess: true, + label: "test vertex shader", + source: testVertexShaderSource + }; + var fTestInfo = { + shader: testFragmentShader, + shaderSuccess: true, + label: "test fragment shader", + source: testFragmentShaderSource + }; + wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo); + } + + var refData = draw(referenceVertexShader, referenceFragmentShader); + var refImg = wtu.makeImageFromCanvas(canvas); + if (ss == 0) { + var testData = draw(testVertexShader, referenceFragmentShader); + } else { + var testData = draw(referenceVertexShader, testFragmentShader); + } + var testImg = wtu.makeImageFromCanvas(canvas); + + _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance, + width, height, ctx, imgData, wtu, canvas2d, consoleDiv); + } + } + + finishTest(); + + function draw(vertexShader, fragmentShader) { + var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed); + + var posLoc = gl.getAttribLocation(program, "aPosition"); + wtu.setupIndexedQuad(gl, gridRes, posLoc); + + gl.useProgram(program); + wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw"); + + var img = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img); + return img; + } + +}; + +var runReferenceImageTest = function(params) { + var wtu = WebGLTestUtils; + var gridRes = params.gridRes; + var vertexTolerance = params.tolerance || 0; + var fragmentTolerance = vertexTolerance; + if ('fragmentTolerance' in params) + fragmentTolerance = params.fragmentTolerance || 0; + + description("Testing GLSL feature: " + params.feature); + + var width = 32; + var height = 32; + + var consoleDiv = document.getElementById("console"); + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var gl = wtu.create3DContext(canvas, { antialias: false, premultipliedAlpha: false }); + if (!gl) { + testFailed("context does not exist"); + finishTest(); + return; + } + + var canvas2d = document.createElement('canvas'); + canvas2d.width = width; + canvas2d.height = height; + var ctx = canvas2d.getContext("2d"); + var imgData = ctx.getImageData(0, 0, width, height); + + // State for reference images for vertex shader tests. + // These are drawn with the same tessellated grid as the test vertex + // shader so that the interpolation is identical. The grid is reused + // from test to test; the colors are changed. + + var indexedQuadForReferenceVertexShader = + wtu.setupIndexedQuad(gl, gridRes, 0); + var referenceVertexShaderProgram = + wtu.setupProgram(gl, [ baseVertexShaderWithColor, baseFragmentShader ], + ["aPosition", "aColor"]); + var referenceVertexShaderColorBuffer = gl.createBuffer(); + + var shaderInfos = [ + { type: "vertex", + input: "color", + output: "vColor", + vertexShaderTemplate: vertexShaderTemplate, + fragmentShaderTemplate: baseFragmentShader, + tolerance: vertexTolerance + }, + { type: "fragment", + input: "vColor", + output: "gl_FragColor", + vertexShaderTemplate: baseVertexShader, + fragmentShaderTemplate: fragmentShaderTemplate, + tolerance: fragmentTolerance + } + ]; + for (var ss = 0; ss < shaderInfos.length; ++ss) { + var shaderInfo = shaderInfos[ss]; + var tests = params.tests; + var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types); + // Test vertex shaders + for (var ii = 0; ii < tests.length; ++ii) { + var type = testTypes[ii]; + var isVertex = (ss == 0); + debug(""); + var str = replaceParams(params.testFunc, { + func: params.feature, + type: type.type, + arg0: type.type + }); + var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader"; + debug(passMsg); + + var referenceVertexShaderSource = generateReferenceShader( + shaderInfo, + shaderInfo.vertexShaderTemplate, + params, + type, + tests[ii].source); + var referenceFragmentShaderSource = generateReferenceShader( + shaderInfo, + shaderInfo.fragmentShaderTemplate, + params, + type, + tests[ii].source); + var testVertexShaderSource = generateTestShader( + shaderInfo, + shaderInfo.vertexShaderTemplate, + params, + tests[ii].source); + var testFragmentShaderSource = generateTestShader( + shaderInfo, + shaderInfo.fragmentShaderTemplate, + params, + tests[ii].source); + var referenceTextureOrArray = generateReferenceImage( + gl, + tests[ii].generator, + isVertex ? gridRes : width, + isVertex ? gridRes : height, + isVertex); + + debug(""); + var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true); + var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true); + debug(""); + + + if (parseInt(wtu.getUrlOptions().dumpShaders)) { + var vRefInfo = { + shader: referenceVertexShader, + shaderSuccess: true, + label: "reference vertex shader", + source: referenceVertexShaderSource + }; + var fRefInfo = { + shader: referenceFragmentShader, + shaderSuccess: true, + label: "reference fragment shader", + source: referenceFragmentShaderSource + }; + wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo); + + var vTestInfo = { + shader: testVertexShader, + shaderSuccess: true, + label: "test vertex shader", + source: testVertexShaderSource + }; + var fTestInfo = { + shader: testFragmentShader, + shaderSuccess: true, + label: "test fragment shader", + source: testFragmentShaderSource + }; + wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo); + } + + var refData; + if (isVertex) { + refData = drawVertexReferenceImage(referenceTextureOrArray); + } else { + refData = drawFragmentReferenceImage(referenceTextureOrArray); + } + var refImg = wtu.makeImageFromCanvas(canvas); + var testData; + if (isVertex) { + var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed); + testData = draw( + testVertexShader, referenceFragmentShader); + } else { + var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed); + testData = draw( + referenceVertexShader, testFragmentShader); + } + var testImg = wtu.makeImageFromCanvas(canvas); + var testTolerance = shaderInfo.tolerance; + // Provide per-test tolerance so that we can increase it only for those desired. + if ('tolerance' in tests[ii]) + testTolerance = tests[ii].tolerance || 0; + _reportResults(refData, refImg, testData, testImg, testTolerance, + width, height, ctx, imgData, wtu, canvas2d, consoleDiv); + } + } + + finishTest(); + + function draw(vertexShader, fragmentShader) { + var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed); + + var posLoc = gl.getAttribLocation(program, "aPosition"); + wtu.setupIndexedQuad(gl, gridRes, posLoc); + + gl.useProgram(program); + wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw"); + + var img = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img); + return img; + } + + function drawVertexReferenceImage(colors) { + gl.bindBuffer(gl.ARRAY_BUFFER, indexedQuadForReferenceVertexShader[0]); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + gl.bindBuffer(gl.ARRAY_BUFFER, referenceVertexShaderColorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexedQuadForReferenceVertexShader[1]); + gl.useProgram(referenceVertexShaderProgram); + wtu.clearAndDrawIndexedQuad(gl, gridRes); + gl.disableVertexAttribArray(0); + gl.disableVertexAttribArray(1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw"); + + var img = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img); + return img; + } + + function drawFragmentReferenceImage(texture) { + var program = wtu.setupTexturedQuad(gl); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture); + var texLoc = gl.getUniformLocation(program, "tex"); + gl.uniform1i(texLoc, 0); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw"); + + var img = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img); + return img; + } + + /** + * Creates and returns either a Uint8Array (for vertex shaders) or + * WebGLTexture (for fragment shaders) containing the reference + * image for the function being tested. Exactly how the function is + * evaluated, and the size of the returned texture or array, depends on + * whether we are testing a vertex or fragment shader. If a fragment + * shader, the function is evaluated at the pixel centers. If a + * vertex shader, the function is evaluated at the triangle's + * vertices. + * + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use to generate texture objects. + * @param {!function(number,number,number,number): !Array.<number>} generator The reference image generator function. + * @param {number} width The width of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader. + * @param {number} height The height of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader. + * @param {boolean} isVertex True if generating a reference image for a vertex shader; false if for a fragment shader. + * @return {!WebGLTexture|!Uint8Array} The texture object or array that was generated. + */ + function generateReferenceImage( + gl, + generator, + width, + height, + isVertex) { + + // Note: the math in this function must match that in the vertex and + // fragment shader templates above. + function computeTexCoord(x) { + return x * 0.5 + 0.5; + } + + function computeVertexColor(texCoordX, texCoordY) { + return [ texCoordX, + texCoordY, + texCoordX * texCoordY, + (1.0 - texCoordX) * texCoordY * 0.5 + 0.5 ]; + } + + /** + * Computes fragment color according to the algorithm used for interpolation + * in OpenGL (GLES 2.0 spec 3.5.1, OpenGL 4.3 spec 14.6.1). + */ + function computeInterpolatedColor(texCoordX, texCoordY) { + // Calculate grid line indexes below and to the left from texCoord. + var gridBottom = Math.floor(texCoordY * gridRes); + if (gridBottom == gridRes) { + --gridBottom; + } + var gridLeft = Math.floor(texCoordX * gridRes); + if (gridLeft == gridRes) { + --gridLeft; + } + + // Calculate coordinates relative to the grid cell. + var cellX = texCoordX * gridRes - gridLeft; + var cellY = texCoordY * gridRes - gridBottom; + + // Barycentric coordinates inside either triangle ACD or ABC + // are used as weights for the vertex colors in the corners: + // A--B + // |\ | + // | \| + // D--C + + var aColor = computeVertexColor(gridLeft / gridRes, (gridBottom + 1) / gridRes); + var bColor = computeVertexColor((gridLeft + 1) / gridRes, (gridBottom + 1) / gridRes); + var cColor = computeVertexColor((gridLeft + 1) / gridRes, gridBottom / gridRes); + var dColor = computeVertexColor(gridLeft / gridRes, gridBottom / gridRes); + + // Calculate weights. + var a, b, c, d; + + if (cellX + cellY < 1) { + // In bottom triangle ACD. + a = cellY; // area of triangle C-D-(cellX, cellY) relative to ACD + c = cellX; // area of triangle D-A-(cellX, cellY) relative to ACD + d = 1 - a - c; + b = 0; + } else { + // In top triangle ABC. + a = 1 - cellX; // area of the triangle B-C-(cellX, cellY) relative to ABC + c = 1 - cellY; // area of the triangle A-B-(cellX, cellY) relative to ABC + b = 1 - a - c; + d = 0; + } + + var interpolated = []; + for (var ii = 0; ii < aColor.length; ++ii) { + interpolated.push(a * aColor[ii] + b * bColor[ii] + c * cColor[ii] + d * dColor[ii]); + } + return interpolated; + } + + function clamp(value, minVal, maxVal) { + return Math.max(minVal, Math.min(value, maxVal)); + } + + // Evaluates the function at clip coordinates (px,py), storing the + // result in the array "pixel". Each channel's result is clamped + // between 0 and 255. + function evaluateAtClipCoords(px, py, pixel, colorFunc) { + var tcx = computeTexCoord(px); + var tcy = computeTexCoord(py); + + var color = colorFunc(tcx, tcy); + + var output = generator(color[0], color[1], color[2], color[3]); + + // Multiply by 256 to get even distribution for all values between 0 and 1. + // Use rounding rather than truncation to more closely match the GPU's behavior. + pixel[0] = clamp(Math.round(256 * output[0]), 0, 255); + pixel[1] = clamp(Math.round(256 * output[1]), 0, 255); + pixel[2] = clamp(Math.round(256 * output[2]), 0, 255); + pixel[3] = clamp(Math.round(256 * output[3]), 0, 255); + } + + function generateFragmentReference() { + var data = new Uint8Array(4 * width * height); + + var horizTexel = 1.0 / width; + var vertTexel = 1.0 / height; + var halfHorizTexel = 0.5 * horizTexel; + var halfVertTexel = 0.5 * vertTexel; + + var pixel = new Array(4); + + for (var yi = 0; yi < height; ++yi) { + for (var xi = 0; xi < width; ++xi) { + // The function must be evaluated at pixel centers. + + // Compute desired position in clip space + var px = -1.0 + 2.0 * (halfHorizTexel + xi * horizTexel); + var py = -1.0 + 2.0 * (halfVertTexel + yi * vertTexel); + + evaluateAtClipCoords(px, py, pixel, computeInterpolatedColor); + var index = 4 * (width * yi + xi); + data[index + 0] = pixel[0]; + data[index + 1] = pixel[1]; + data[index + 2] = pixel[2]; + data[index + 3] = pixel[3]; + } + } + + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, + gl.RGBA, gl.UNSIGNED_BYTE, data); + return texture; + } + + function generateVertexReference() { + // We generate a Uint8Array which contains the evaluation of the + // function at the vertices of the triangle mesh. It is expected + // that the width and the height are identical, and equivalent + // to the grid resolution. + if (width != height) { + throw "width and height must be equal"; + } + + var texSize = 1 + width; + var data = new Uint8Array(4 * texSize * texSize); + + var step = 2.0 / width; + + var pixel = new Array(4); + + for (var yi = 0; yi < texSize; ++yi) { + for (var xi = 0; xi < texSize; ++xi) { + // The function is evaluated at the triangles' vertices. + + // Compute desired position in clip space + var px = -1.0 + (xi * step); + var py = -1.0 + (yi * step); + + evaluateAtClipCoords(px, py, pixel, computeVertexColor); + var index = 4 * (texSize * yi + xi); + data[index + 0] = pixel[0]; + data[index + 1] = pixel[1]; + data[index + 2] = pixel[2]; + data[index + 3] = pixel[3]; + } + } + + return data; + } + + //---------------------------------------------------------------------- + // Body of generateReferenceImage + // + + if (isVertex) { + return generateVertexReference(); + } else { + return generateFragmentReference(); + } + } +}; + +return { + /** + * runs a bunch of GLSL tests using the passed in parameters + * The parameters are: + * + * feature: + * the name of the function being tested (eg, sin, dot, + * normalize) + * + * testFunc: + * The prototype of function to be tested not including the + * return type. + * + * emuFunc: + * A base function that can be used to generate emulation + * functions. Example for 'ceil' + * + * float $(func)_base(float value) { + * float m = mod(value, 1.0); + * return m != 0.0 ? (value + 1.0 - m) : value; + * } + * + * args: + * The arguments to the function + * + * baseArgs: (optional) + * The arguments when a base function is used to create an + * emulation function. For example 'float sign_base(float v)' + * is used to implemenent vec2 sign_emu(vec2 v). + * + * simpleEmu: + * if supplied, the code that can be used to generate all + * functions for all types. + * + * Example for 'normalize': + * + * $(type) $(func)_emu($(args)) { + * return value / length(value); + * } + * + * gridRes: (optional) + * The resolution of the mesh to generate. The default is a + * 1x1 grid but many vertex shaders need a higher resolution + * otherwise the only values passed in are the 4 corners + * which often have the same value. + * + * tests: + * The code for each test. It is assumed the tests are for + * float, vec2, vec3, vec4 in that order. + * + * tolerance: (optional) + * Allow some tolerance in the comparisons. The tolerance is applied to + * both vertex and fragment shaders. The default tolerance is 0, meaning + * the values have to be identical. + * + * fragmentTolerance: (optional) + * Specify a tolerance which only applies to fragment shaders. The + * fragment-only tolerance will override the shared tolerance for + * fragment shaders if both are specified. Fragment shaders usually + * use mediump float precision so they sometimes require higher tolerance + * than vertex shaders which use highp by default. + */ + runFeatureTest: runFeatureTest, + + /* + * Runs a bunch of GLSL tests using the passed in parameters + * + * The parameters are: + * + * tests: + * Array of tests. For each test the following parameters are expected + * + * name: + * some description of the test + * reference: + * parameters for the reference shader (see below) + * test: + * parameters for the test shader (see below) + * + * The parameter for the reference and test shaders are + * + * shader: the GLSL for the shader + * subs: any substitutions you wish to define for the shader. + * + * Each shader is created from a basic template that + * defines an input and an output. You can see the + * templates at the top of this file. The input and output + * change depending on whether or not we are generating + * a vertex or fragment shader. + * + * All this code function does is a bunch of string substitutions. + * A substitution is defined by $(name). If name is found in + * the 'subs' parameter it is replaced. 4 special names exist. + * + * 'input' the input to your GLSL. Always a vec4. All change + * from 0 to 1 over the quad to be drawn. + * + * 'output' the output color. Also a vec4 + * + * 'emu' a place to insert extra stuff + * 'extra' a place to insert extra stuff. + * + * You can think of the templates like this + * + * $(extra) + * $(emu) + * + * void main() { + * // do math to calculate input + * ... + * + * $(shader) + * } + * + * Your shader first has any subs you provided applied as well + * as 'input' and 'output' + * + * It is then inserted into the template which is also provided + * with your subs. + * + * gridRes: (optional) + * The resolution of the mesh to generate. The default is a + * 1x1 grid but many vertex shaders need a higher resolution + * otherwise the only values passed in are the 4 corners + * which often have the same value. + * + * tolerance: (optional) + * Allow some tolerance in the comparisons. The tolerance is applied to + * both vertex and fragment shaders. The default tolerance is 0, meaning + * the values have to be identical. + * + * fragmentTolerance: (optional) + * Specify a tolerance which only applies to fragment shaders. The + * fragment-only tolerance will override the shared tolerance for + * fragment shaders if both are specified. Fragment shaders usually + * use mediump float precision so they sometimes require higher tolerance + * than vertex shaders which use highp. + */ + runBasicTest: runBasicTest, + + /** + * Runs a bunch of GLSL tests using the passed in parameters. The + * expected results are computed as a reference image in JavaScript + * instead of on the GPU. The parameters are: + * + * feature: + * the name of the function being tested (eg, sin, dot, + * normalize) + * + * testFunc: + * The prototype of function to be tested not including the + * return type. + * + * args: + * The arguments to the function + * + * gridRes: (optional) + * The resolution of the mesh to generate. The default is a + * 1x1 grid but many vertex shaders need a higher resolution + * otherwise the only values passed in are the 4 corners + * which often have the same value. + * + * tests: + * Array of tests. It is assumed the tests are for float, vec2, + * vec3, vec4 in that order. For each test the following + * parameters are expected: + * + * source: the GLSL source code for the tests + * + * generator: a JavaScript function taking four parameters + * which evaluates the same function as the GLSL source, + * returning its result as a newly allocated array. + * + * tolerance: (optional) a per-test tolerance. + * + * extra: (optional) + * Extra GLSL code inserted at the top of each test's shader. + * + * tolerance: (optional) + * Allow some tolerance in the comparisons. The tolerance is applied to + * both vertex and fragment shaders. The default tolerance is 0, meaning + * the values have to be identical. + * + * fragmentTolerance: (optional) + * Specify a tolerance which only applies to fragment shaders. The + * fragment-only tolerance will override the shared tolerance for + * fragment shaders if both are specified. Fragment shaders usually + * use mediump float precision so they sometimes require higher tolerance + * than vertex shaders which use highp. + */ + runReferenceImageTest: runReferenceImageTest, + + none: false +}; + +}()); + diff --git a/dom/canvas/test/webgl-conf/checkout/js/js-test-post.js b/dom/canvas/test/webgl-conf/checkout/js/js-test-post.js new file mode 100644 index 0000000000..2b19b3b98f --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/js-test-post.js @@ -0,0 +1,38 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +shouldBeTrue("successfullyParsed"); +_addSpan('<br /><span class="pass">TEST COMPLETE</span>'); +if (_jsTestPreVerboseLogging) { + _bufferedLogToConsole('TEST COMPLETE'); +} + +{ + const e_results = document.createElement('div'); + let fails_class = 'pass'; + if (RESULTS.fail) { + fails_class = 'fail'; + } else { + const parseBoolean = v => v.toLowerCase().startsWith('t') || parseFloat(v) > 0; + const params = new URLSearchParams(window.location.search); + if (parseBoolean(params.get('runUntilFail') || '')) { + setTimeout(() => { + params.set('runCount', parseInt(params.get('runCount') || '0') + 1); + const url = new URL(window.location.href); + url.search = params.toString(); + window.location.href = url.toString(); + }, 100); + } + } + e_results.classList.add('pass'); + e_results.innerHTML = `<p>TEST COMPLETE: ${RESULTS.pass} PASS, ` + + `<span class="${fails_class}">${RESULTS.fail} FAIL</span></p>`; + + const e_desc = document.getElementById("description"); + e_desc.appendChild(e_results); +} + +notifyFinishedToHarness() diff --git a/dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js b/dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js new file mode 100644 index 0000000000..e1cb9f749c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js @@ -0,0 +1,801 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +(function() { + var testHarnessInitialized = false; + + var initNonKhronosFramework = function() { + if (testHarnessInitialized) { + return; + } + testHarnessInitialized = true; + + /* -- plaform specific code -- */ + + // WebKit Specific code. Add your code here. + if (window.testRunner && !window.layoutTestController) { + window.layoutTestController = window.testRunner; + } + + if (window.layoutTestController) { + window.layoutTestController.dumpAsText(); + window.layoutTestController.waitUntilDone(); + } + if (window.internals) { + // The WebKit testing system compares console output. + // Because the output of the WebGL Tests is GPU dependent + // we turn off console messages. + window.console.log = function() { }; + window.console.error = function() { }; + window.internals.settings.setWebGLErrorsToConsoleEnabled(false); + } + + /* -- end platform specific code --*/ + } + + this.initTestingHarness = function() { + initNonKhronosFramework(); + } +}()); + +var getUrlOptions = (function() { + var _urlOptionsParsed = false; + var _urlOptions = {}; + return function() { + if (!_urlOptionsParsed) { + var s = window.location.href; + var q = s.indexOf("?"); + var e = s.indexOf("#"); + if (e < 0) { + e = s.length; + } + var query = s.substring(q + 1, e); + var pairs = query.split("&"); + for (var ii = 0; ii < pairs.length; ++ii) { + var keyValue = pairs[ii].split("="); + var key = keyValue[0]; + var value = decodeURIComponent(keyValue[1]); + _urlOptions[key] = value; + } + _urlOptionsParsed = true; + } + + return _urlOptions; + } +})(); + +if (typeof quietMode == 'undefined') { + var quietMode = (function() { + var _quietModeChecked = false; + var _isQuiet = false; + return function() { + if (!_quietModeChecked) { + _isQuiet = (getUrlOptions().quiet == 1); + _quietModeChecked = true; + } + return _isQuiet; + } + })(); +} + +function nonKhronosFrameworkNotifyDone() { + // WebKit Specific code. Add your code here. + if (window.layoutTestController) { + window.layoutTestController.notifyDone(); + } +} + +const RESULTS = { + pass: 0, + fail: 0, +}; + +function reportTestResultsToHarness(success, msg) { + if (success) { + RESULTS.pass += 1; + } else { + RESULTS.fail += 1; + } + if (window.parent.webglTestHarness) { + window.parent.webglTestHarness.reportResults(window.location.pathname, success, msg); + } +} + +function reportSkippedTestResultsToHarness(success, msg) { + if (window.parent.webglTestHarness) { + window.parent.webglTestHarness.reportResults(window.location.pathname, success, msg, true); + } +} + +function notifyFinishedToHarness() { + if (window._didNotifyFinishedToHarness) { + testFailed("Duplicate notifyFinishedToHarness()"); + } + window._didNotifyFinishedToHarness = true; + + if (window.parent.webglTestHarness) { + window.parent.webglTestHarness.notifyFinished(window.location.pathname); + } + if (window.nonKhronosFrameworkNotifyDone) { + window.nonKhronosFrameworkNotifyDone(); + } +} + +var _bufferedConsoleLogs = []; + +function _bufferedLogToConsole(msg) +{ + if (_bufferedConsoleLogs) { + _bufferedConsoleLogs.push(msg); + } else if (window.console) { + window.console.log(msg); + } +} + +// Public entry point exposed to many other files. +function bufferedLogToConsole(msg) +{ + _bufferedLogToConsole(msg); +} + +// Called implicitly by testFailed(). +function _flushBufferedLogsToConsole() +{ + if (_bufferedConsoleLogs) { + if (window.console) { + for (var ii = 0; ii < _bufferedConsoleLogs.length; ++ii) { + window.console.log(_bufferedConsoleLogs[ii]); + } + } + _bufferedConsoleLogs = null; + } +} + +var _jsTestPreVerboseLogging = false; + +function enableJSTestPreVerboseLogging() +{ + _jsTestPreVerboseLogging = true; +} + +function description(msg) +{ + initTestingHarness(); + if (msg === undefined) { + msg = document.title; + } + // For MSIE 6 compatibility + var span = document.createElement("span"); + span.innerHTML = '<p>' + msg + '</p><p>On success, you will see a series of "<span class="pass">PASS</span>" messages, followed by "<span class="pass">TEST COMPLETE</span>".</p>'; + var description = document.getElementById("description"); + if (description.firstChild) + description.replaceChild(span, description.firstChild); + else + description.appendChild(span); + if (_jsTestPreVerboseLogging) { + _bufferedLogToConsole(msg); + } +} + +function _addSpan(contents) +{ + var span = document.createElement("span"); + document.getElementById("console").appendChild(span); // insert it first so XHTML knows the namespace + span.innerHTML = contents + '<br />'; +} + +function debug(msg) +{ + if (!quietMode()) + _addSpan(msg); + if (_jsTestPreVerboseLogging) { + _bufferedLogToConsole(msg); + } +} + +function escapeHTML(text) +{ + return text.replace(/&/g, "&").replace(/</g, "<"); +} +/** + * Defines the exception type for a test failure. + * @constructor + * @param {string} message The error message. + */ +var TestFailedException = function (message) { + this.message = message; + this.name = "TestFailedException"; +}; + +/** + * @param {string=} msg + */ +function testPassed(msg) { + msg = msg || 'Passed'; + if (_currentTestName) + msg = _currentTestName + ': ' + msg; + + reportTestResultsToHarness(true, msg); + + if (!quietMode()) + _addSpan('<span><span class="pass">PASS</span> ' + escapeHTML(msg) + '</span>'); + if (_jsTestPreVerboseLogging) { + _bufferedLogToConsole('PASS ' + msg); + } +} + +/** + * @param {string=} msg + */ +function testFailed(msg) { + msg = msg || 'Failed'; + if (_currentTestName) + msg = _currentTestName + ': ' + msg; + + reportTestResultsToHarness(false, msg); + _addSpan('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>'); + _bufferedLogToConsole('FAIL ' + msg); + _flushBufferedLogsToConsole(); +} + +var _currentTestName; + +/** + * Sets the current test name for usage within testPassedOptions/testFailedOptions. + * @param {string=} name The name to set as the current test name. + */ +function setCurrentTestName(name) +{ + _currentTestName = name; +} + +/** + * Gets the current test name in use within testPassedOptions/testFailedOptions. + * @return {string} The name of the current test. + */ +function getCurrentTestName() +{ + return _currentTestName; +} + +/** + * Variation of the testPassed function, with the option to not show (and thus not count) the test's pass result. + * @param {string} msg The message to be shown in the pass result. + * @param {boolean} addSpan Indicates whether the message will be visible (thus counted in the results) or not. + */ +function testPassedOptions(msg, addSpan) +{ + if (addSpan && !quietMode()) + { + reportTestResultsToHarness(true, _currentTestName + ": " + msg); + _addSpan('<span><span class="pass">PASS</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(msg) + '</span>'); + } + if (_jsTestPreVerboseLogging) { + _bufferedLogToConsole('PASS ' + msg); + } +} + +/** + * Report skipped tests. + * @param {string} msg The message to be shown in the skip result. + * @param {boolean} addSpan Indicates whether the message will be visible (thus counted in the results) or not. + */ +function testSkippedOptions(msg, addSpan) +{ + if (addSpan && !quietMode()) + { + reportSkippedTestResultsToHarness(true, _currentTestName + ": " + msg); + _addSpan('<span><span class="warn">SKIP</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(msg) + '</span>'); + } + if (_jsTestPreVerboseLogging) { + _bufferedLogToConsole('SKIP' + msg); + } +} + +/** + * Variation of the testFailed function, with the option to throw an exception or not. + * @param {string} msg The message to be shown in the fail result. + * @param {boolean} exthrow Indicates whether the function will throw a TestFailedException or not. + */ +function testFailedOptions(msg, exthrow) +{ + reportTestResultsToHarness(false, _currentTestName + ": " + msg); + _addSpan('<span><span class="fail">FAIL</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(msg) + '</span>'); + _bufferedLogToConsole('FAIL ' + msg); + _flushBufferedLogsToConsole(); + if (exthrow) { + _currentTestName = ""; //Remembering to set the name of current testcase to empty string. + throw new TestFailedException(msg); + } +} + +function areArraysEqual(_a, _b) +{ + try { + if (_a.length !== _b.length) + return false; + for (var i = 0; i < _a.length; i++) + if (_a[i] !== _b[i]) + return false; + } catch (ex) { + return false; + } + return true; +} + +function isMinusZero(n) +{ + // the only way to tell 0 from -0 in JS is the fact that 1/-0 is + // -Infinity instead of Infinity + return n === 0 && 1/n < 0; +} + +function isResultCorrect(_actual, _expected) +{ + if (_expected === 0) + return _actual === _expected && (1/_actual) === (1/_expected); + if (_actual === _expected) + return true; + if (typeof(_expected) == "number" && isNaN(_expected)) + return typeof(_actual) == "number" && isNaN(_actual); + if (Object.prototype.toString.call(_expected) == Object.prototype.toString.call([])) + return areArraysEqual(_actual, _expected); + return false; +} + +function stringify(v) +{ + if (v === 0 && 1/v < 0) + return "-0"; + else return "" + v; +} + +function evalAndLog(_a) +{ + if (typeof _a != "string") + debug("WARN: tryAndLog() expects a string argument"); + + // Log first in case things go horribly wrong or this causes a sync event. + debug(_a); + + var _av; + try { + _av = eval(_a); + } catch (e) { + testFailed(_a + " threw exception " + e); + } + return _av; +} + +function shouldBeString(evalable, expected) { + const val = eval(evalable); + const text = evalable + " should be " + expected + "."; + if (val == expected) { + testPassed(text); + } else { + testFailed(text + " (was " + val + ")"); + } +} + +function shouldBe(_a, _b, quiet) +{ + if (typeof _a != "string" || typeof _b != "string") + debug("WARN: shouldBe() expects string arguments"); + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + var _bv = eval(_b); + + if (exception) + testFailed(_a + " should be " + _bv + ". Threw exception " + exception); + else if (isResultCorrect(_av, _bv)) { + if (!quiet) { + testPassed(_a + " is " + _b); + } + } else if (typeof(_av) == typeof(_bv)) + testFailed(_a + " should be " + _bv + ". Was " + stringify(_av) + "."); + else + testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ")."); +} + +function shouldNotBe(_a, _b, quiet) +{ + if (typeof _a != "string" || typeof _b != "string") + debug("WARN: shouldNotBe() expects string arguments"); + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + var _bv = eval(_b); + + if (exception) + testFailed(_a + " should not be " + _bv + ". Threw exception " + exception); + else if (!isResultCorrect(_av, _bv)) { + if (!quiet) { + testPassed(_a + " is not " + _b); + } + } else + testFailed(_a + " should not be " + _bv + "."); +} + +function shouldBeTrue(_a) { shouldBe(_a, "true"); } +function shouldBeFalse(_a) { shouldBe(_a, "false"); } +function shouldBeNaN(_a) { shouldBe(_a, "NaN"); } +function shouldBeNull(_a) { shouldBe(_a, "null"); } + +function shouldBeEqualToString(a, b) +{ + var unevaledString = '"' + b.replace(/"/g, "\"") + '"'; + shouldBe(a, unevaledString); +} + +function shouldEvaluateTo(actual, expected) { + // A general-purpose comparator. 'actual' should be a string to be + // evaluated, as for shouldBe(). 'expected' may be any type and will be + // used without being eval'ed. + if (expected == null) { + // Do this before the object test, since null is of type 'object'. + shouldBeNull(actual); + } else if (typeof expected == "undefined") { + shouldBeUndefined(actual); + } else if (typeof expected == "function") { + // All this fuss is to avoid the string-arg warning from shouldBe(). + try { + var actualValue = eval(actual); + } catch (e) { + testFailed("Evaluating " + actual + ": Threw exception " + e); + return; + } + shouldBe("'" + actualValue.toString().replace(/\n/g, "") + "'", + "'" + expected.toString().replace(/\n/g, "") + "'"); + } else if (typeof expected == "object") { + shouldBeTrue(actual + " == '" + expected + "'"); + } else if (typeof expected == "string") { + shouldBe(actual, expected); + } else if (typeof expected == "boolean") { + shouldBe("typeof " + actual, "'boolean'"); + if (expected) + shouldBeTrue(actual); + else + shouldBeFalse(actual); + } else if (typeof expected == "number") { + shouldBe(actual, stringify(expected)); + } else { + debug(expected + " is unknown type " + typeof expected); + shouldBeTrue(actual, "'" +expected.toString() + "'"); + } +} + +function shouldBeNonZero(_a) +{ + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + if (exception) + testFailed(_a + " should be non-zero. Threw exception " + exception); + else if (_av != 0) + testPassed(_a + " is non-zero."); + else + testFailed(_a + " should be non-zero. Was " + _av); +} + +function shouldBeNonNull(_a) +{ + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + if (exception) + testFailed(_a + " should be non-null. Threw exception " + exception); + else if (_av != null) + testPassed(_a + " is non-null."); + else + testFailed(_a + " should be non-null. Was " + _av); +} + +function shouldBeUndefined(_a) +{ + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + if (exception) + testFailed(_a + " should be undefined. Threw exception " + exception); + else if (typeof _av == "undefined") + testPassed(_a + " is undefined."); + else + testFailed(_a + " should be undefined. Was " + _av); +} + +function shouldBeDefined(_a) +{ + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + if (exception) + testFailed(_a + " should be defined. Threw exception " + exception); + else if (_av !== undefined) + testPassed(_a + " is defined."); + else + testFailed(_a + " should be defined. Was " + _av); +} + +function shouldBeLessThanOrEqual(_a, _b) { + if (typeof _a != "string" || typeof _b != "string") + debug("WARN: shouldBeLessThanOrEqual expects string arguments"); + + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + var _bv = eval(_b); + + if (exception) + testFailed(_a + " should be <= " + _b + ". Threw exception " + exception); + else if (typeof _av == "undefined" || _av > _bv) + testFailed(_a + " should be >= " + _b + ". Was " + _av + " (of type " + typeof _av + ")."); + else + testPassed(_a + " is <= " + _b); +} + +function shouldBeGreaterThanOrEqual(_a, _b) { + if (typeof _a != "string" || typeof _b != "string") + debug("WARN: shouldBeGreaterThanOrEqual expects string arguments"); + + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + var _bv = eval(_b); + + if (exception) + testFailed(_a + " should be >= " + _b + ". Threw exception " + exception); + else if (typeof _av == "undefined" || _av < _bv) + testFailed(_a + " should be >= " + _b + ". Was " + _av + " (of type " + typeof _av + ")."); + else + testPassed(_a + " is >= " + _b); +} + +function expectTrue(v, msg) { + if (v) { + testPassed(msg); + } else { + testFailed(msg); + } +} + +function maxArrayDiff(a, b) { + if (a.length != b.length) + throw new Error(`a and b have different lengths: ${a.length} vs ${b.length}`); + + let diff = 0; + for (const i in a) { + diff = Math.max(diff, Math.abs(a[i] - b[i])); + } + return diff; +} + +function expectArray(was, expected, maxDiff=0) { + const diff = maxArrayDiff(expected, was); + let str = `Expected [${expected.toString()}]`; + let fn = testPassed; + if (maxDiff) { + str += ' +/- ' + maxDiff; + } + if (diff > maxDiff) { + fn = testFailed; + str += `, was [${was.toString()}]`; + } + fn(str); +} + +function shouldThrow(_a, _e) +{ + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + var _ev; + if (_e) + _ev = eval(_e); + + if (exception) { + if (typeof _e == "undefined" || exception == _ev) + testPassed(_a + " threw exception " + exception + "."); + else + testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + exception + "."); + } else if (typeof _av == "undefined") + testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined."); + else + testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + "."); +} + +function shouldNotThrow(evalStr, desc) { + desc = desc || `\`${evalStr}\``; + try { + eval(evalStr); + testPassed(`${desc} should not throw.`); + } catch (e) { + testFailed(`${desc} should not throw, but threw exception ${e}.`); + } +} + + +function shouldBeType(_a, _type) { + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + var _typev = eval(_type); + + if(_typev === Number){ + if(_av instanceof Number){ + testPassed(_a + " is an instance of Number"); + } + else if(typeof(_av) === 'number'){ + testPassed(_a + " is an instance of Number"); + } + else{ + testFailed(_a + " is not an instance of Number"); + } + } + else if (_av instanceof _typev) { + testPassed(_a + " is an instance of " + _type); + } else { + testFailed(_a + " is not an instance of " + _type); + } +} + +/** + * Shows a message in case expression test fails. + * @param {boolean} exp + * @param {straing} message + */ +function checkMessage(exp, message) { + if ( !exp ) + _addSpan('<span><span class="warn">WARNING</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(message) + '</span>'); +} + +function assertMsg(assertion, msg) { + if (assertion) { + testPassed(msg); + } else { + testFailed(msg); + } +} + +/** + * Variation of the assertMsg function, with the option to not show (and thus not count) the test's pass result, + * and throw or not a TestFailedException in case of failure. + * @param {boolean} assertion If this is true, means success, else failure. + * @param {?string} msg The message to be shown in the result. + * @param {boolean} verbose In case of success, determines if the test will show it's result and count in the results. + * @param {boolean} exthrow In case of failure, determines if the function will throw a TestFailedException. + */ +function assertMsgOptions(assertion, msg, verbose, exthrow) { + if (assertion) { + testPassedOptions(msg, verbose); + } else { + testFailedOptions(msg, exthrow); + } +} + + +function webglHarnessCollectGarbage() { + if (window.GCController) { + window.GCController.collect(); + return; + } + + if (window.opera && window.opera.collect) { + window.opera.collect(); + return; + } + + try { + window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils) + .garbageCollect(); + return; + } catch(e) {} + + if (window.gc) { + window.gc(); + return; + } + + if (window.CollectGarbage) { + CollectGarbage(); + return; + } + + // WebKit's MiniBrowser. + if (window.$vm) { + window.$vm.gc(); + return; + } + + function gcRec(n) { + if (n < 1) + return {}; + var temp = {i: "ab" + i + (i / 100000)}; + temp += "foo"; + gcRec(n-1); + } + for (var i = 0; i < 1000; i++) + gcRec(10); +} + +function finishTest() { + successfullyParsed = true; + var epilogue = document.createElement("script"); + var basePath = ""; + var expectedBase = "js-test-pre.js"; + var scripts = document.getElementsByTagName('script'); + for (var script, i = 0; script = scripts[i]; i++) { + var src = script.src; + var l = src.length; + if (src.substr(l - expectedBase.length) == expectedBase) { + basePath = src.substr(0, l - expectedBase.length); + break; + } + } + epilogue.src = basePath + "js-test-post.js"; + document.body.appendChild(epilogue); +} + +/// Prefer `call(() => { ... })` to `(() => { ... })()`\ +/// This way, it's clear up-front that we're calling not just defining. +function call(fn) { + return fn(); +} + +/// `for (const i of range(3))` => 0, 1, 2 +/// Don't use `for...in range(n)`, it will not work. +function* range(n) { + for (let i = 0; i < n; i++) { + yield i; + } +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/pnglib.js b/dom/canvas/test/webgl-conf/checkout/js/pnglib.js new file mode 100644 index 0000000000..d2a9b99e08 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/pnglib.js @@ -0,0 +1,207 @@ +/** +* A handy class to calculate color values. +* +* @version 1.0 +* @author Robert Eisele <robert@xarg.org> +* @copyright Copyright (c) 2010, Robert Eisele +* @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ +* @license http://www.opensource.org/licenses/bsd-license.php BSD License +* +*/ + +(function() { + + // helper functions for that ctx + function write(buffer, offs) { + for (var i = 2; i < arguments.length; i++) { + for (var j = 0; j < arguments[i].length; j++) { + buffer[offs++] = arguments[i].charAt(j); + } + } + } + + function byte2(w) { + return String.fromCharCode((w >> 8) & 255, w & 255); + } + + function byte4(w) { + return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255); + } + + function byte2lsb(w) { + return String.fromCharCode(w & 255, (w >> 8) & 255); + } + + window.PNGlib = function(width,height,depth) { + + this.width = width; + this.height = height; + this.depth = depth; + + // pixel data and row filter identifier size + this.pix_size = height * (width + 1); + + // deflate header, pix_size, block headers, adler32 checksum + this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4; + + // offsets and sizes of Png chunks + this.ihdr_offs = 0; // IHDR offset and size + this.ihdr_size = 4 + 4 + 13 + 4; + this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size + this.plte_size = 4 + 4 + 3 * depth + 4; + this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size + this.trns_size = 4 + 4 + depth + 4; + this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size + this.idat_size = 4 + 4 + this.data_size + 4; + this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size + this.iend_size = 4 + 4 + 4; + this.buffer_size = this.iend_offs + this.iend_size; // total PNG size + + this.buffer = new Array(); + this.palette = new Object(); + this.pindex = 0; + + var _crc32 = new Array(); + + // initialize buffer with zero bytes + for (var i = 0; i < this.buffer_size; i++) { + this.buffer[i] = "\x00"; + } + + // initialize non-zero elements + write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03"); + write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE'); + write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS'); + write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT'); + write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND'); + + // initialize deflate header + var header = ((8 + (7 << 4)) << 8) | (3 << 6); + header+= 31 - (header % 31); + + write(this.buffer, this.idat_offs + 8, byte2(header)); + + // initialize deflate block headers + for (var i = 0; (i << 16) - 1 < this.pix_size; i++) { + var size, bits; + if (i + 0xffff < this.pix_size) { + size = 0xffff; + bits = "\x00"; + } else { + size = this.pix_size - (i << 16) - i; + bits = "\x01"; + } + write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size)); + } + + /* Create crc32 lookup table */ + for (var i = 0; i < 256; i++) { + var c = i; + for (var j = 0; j < 8; j++) { + if (c & 1) { + c = -306674912 ^ ((c >> 1) & 0x7fffffff); + } else { + c = (c >> 1) & 0x7fffffff; + } + } + _crc32[i] = c; + } + + // compute the index into a png for a given pixel + this.index = function(x,y) { + var i = y * (this.width + 1) + x + 1; + var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i; + return j; + } + + // convert a color and build up the palette + this.color = function(red, green, blue, alpha) { + + alpha = alpha >= 0 ? alpha : 255; + var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue; + + if (typeof this.palette[color] == "undefined") { + if (this.pindex == this.depth) return "\x00"; + + var ndx = this.plte_offs + 8 + 3 * this.pindex; + + this.buffer[ndx + 0] = String.fromCharCode(red); + this.buffer[ndx + 1] = String.fromCharCode(green); + this.buffer[ndx + 2] = String.fromCharCode(blue); + this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha); + + this.palette[color] = String.fromCharCode(this.pindex++); + } + return this.palette[color]; + } + + // output a PNG string, Base64 encoded + this.getBase64 = function() { + + var s = this.getDump(); + + var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var c1, c2, c3, e1, e2, e3, e4; + var l = s.length; + var i = 0; + var r = ""; + + do { + c1 = s.charCodeAt(i); + e1 = c1 >> 2; + c2 = s.charCodeAt(i+1); + e2 = ((c1 & 3) << 4) | (c2 >> 4); + c3 = s.charCodeAt(i+2); + if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); } + if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; } + r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4); + } while ((i+= 3) < l); + return r; + } + + // output a PNG string + this.getDump = function() { + + // compute adler32 of output pixels + row filter bytes + var BASE = 65521; /* largest prime smaller than 65536 */ + var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + var s1 = 1; + var s2 = 0; + var n = NMAX; + + for (var y = 0; y < this.height; y++) { + for (var x = -1; x < this.width; x++) { + s1+= this.buffer[this.index(x, y)].charCodeAt(0); + s2+= s1; + if ((n-= 1) == 0) { + s1%= BASE; + s2%= BASE; + n = NMAX; + } + } + } + s1%= BASE; + s2%= BASE; + write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1)); + + // compute crc32 of the PNG chunks + function crc32(png, offs, size) { + var crc = -1; + for (var i = 4; i < size-4; i += 1) { + crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff); + } + write(png, offs+size-4, byte4(crc ^ -1)); + } + + crc32(this.buffer, this.ihdr_offs, this.ihdr_size); + crc32(this.buffer, this.plte_offs, this.plte_size); + crc32(this.buffer, this.trns_offs, this.trns_size); + crc32(this.buffer, this.idat_offs, this.idat_size); + crc32(this.buffer, this.iend_offs, this.iend_size); + + // convert PNG to string + return "\211PNG\r\n\032\n"+this.buffer.join(''); + } + } + +})(); diff --git a/dom/canvas/test/webgl-conf/checkout/js/test-eval.js b/dom/canvas/test/webgl-conf/checkout/js/test-eval.js new file mode 100644 index 0000000000..113e9c6008 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/test-eval.js @@ -0,0 +1,15 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +/** + * Calls eval. + * + * This is here so other modules can use "use strict": + */ +TestEval = function(str) { + return eval(str); +}; + + diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/canvas-tests-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/canvas-tests-utils.js new file mode 100644 index 0000000000..7c30104379 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/canvas-tests-utils.js @@ -0,0 +1,825 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// Some variables that will be used in this file +var canvas; +var gl; +var OES_vertex_array_object; +var uniformLocation; +var extension; +var buffer; +var framebuffer; +var program; +var renderbuffer; +var shader; +var texture; +var arrayBuffer; +var arrayBufferView; +var vertexArrayObject; +var imageData; +var float32array; +var int32array; + +var OES_texture_float; +var new_WEBGL_lose_context; +var allowRestore; +var contextLostEventFired; +var contextRestoredEventFired; +var newExtension; + +function compareGLError(glError, evalStr) { + var exception; + try { + eval(evalStr); + } catch (e) { + exception = e; + } + if (exception) { + return false; + } else { + if (gl.getError() == glError) + return true; + return false; + } +} + +function contextCreation(contextType) { + canvas = new OffscreenCanvas(10, 10); + gl = canvas.getContext(contextType); + + if (contextType == 'webgl') { + if (gl instanceof WebGLRenderingContext) + return true; + return false; + } else if (contextType == 'webgl2') { + if (gl instanceof WebGL2RenderingContext) + return true; + return false; + } else { + return false; + } +} + +function transferredOffscreenCanvasCreation(placeholder, width, height) { + placeholder.width = width; + placeholder.height = height; + return placeholder.transferControlToOffscreen(); +} + +function assertWidthAndHeight(entity, entityName, width, height) { + if (entity.width == width && entity.height == height) { + testPassed("The width and height of " + entityName + " are correct."); + return; + } + var errMsg = ""; + if (entity.width != width) { + errMsg += "The width of " + entityName + " is " + entity.width + " while expected value is " + width + ". "; + } + if (entity.height != height) { + errMsg += "The height of " + entityName + " is " + entity.height + " while expected value is " + height + ". "; + } + testFailed(errMsg); +} + +var webgl1Methods = [ + "getContextAttributes", + "activeTexture", + "attachShader", + "bindAttribLocation", + "bindBuffer", + "bindFramebuffer", + "bindRenderbuffer", + "bindTexture", + "blendColor", + "blendEquation", + "blendEquationSeparate", + "blendFunc", + "blendFuncSeparate", + "bufferData", + "bufferSubData", + "checkFramebufferStatus", + "clear", + "clearColor", + "clearDepth", + "clearStencil", + "colorMask", + "compileShader", + "compressedTexImage2D", + "compressedTexSubImage2D", + "copyTexImage2D", + "copyTexSubImage2D", + "createBuffer", + "createFramebuffer", + "createProgram", + "createRenderbuffer", + "createShader", + "createTexture", + "cullFace", + "deleteBuffer", + "deleteFramebuffer", + "deleteProgram", + "deleteRenderbuffer", + "deleteShader", + "deleteTexture", + "depthFunc", + "depthMask", + "depthRange", + "detachShader", + "disable", + "disableVertexAttribArray", + "drawArrays", + "drawElements", + "enable", + "enableVertexAttribArray", + "finish", + "flush", + "framebufferRenderbuffer", + "framebufferTexture2D", + "frontFace", + "generateMipmap", + "getActiveAttrib", + "getActiveUniform", + "getAttachedShaders", + "getAttribLocation", + "getParameter", + "getBufferParameter", + "getError", + "getExtension", + "getFramebufferAttachmentParameter", + "getProgramParameter", + "getProgramInfoLog", + "getRenderbufferParameter", + "getShaderParameter", + "getShaderInfoLog", + "getShaderPrecisionFormat", + "getShaderSource", + "getSupportedExtensions", + "getTexParameter", + "getUniform", + "getUniformLocation", + "getVertexAttrib", + "getVertexAttribOffset", + "hint", + "isBuffer", + "isContextLost", + "isEnabled", + "isFramebuffer", + "isProgram", + "isRenderbuffer", + "isShader", + "isTexture", + "lineWidth", + "linkProgram", + "pixelStorei", + "polygonOffset", + "readPixels", + "renderbufferStorage", + "sampleCoverage", + "scissor", + "shaderSource", + "stencilFunc", + "stencilFuncSeparate", + "stencilMask", + "stencilMaskSeparate", + "stencilOp", + "stencilOpSeparate", + "texImage2D", + "texParameterf", + "texParameteri", + "texSubImage2D", + "uniform1f", + "uniform1fv", + "uniform1i", + "uniform1iv", + "uniform2f", + "uniform2fv", + "uniform2i", + "uniform2iv", + "uniform3f", + "uniform3fv", + "uniform3i", + "uniform3iv", + "uniform4f", + "uniform4fv", + "uniform4i", + "uniform4iv", + "uniformMatrix2fv", + "uniformMatrix3fv", + "uniformMatrix4fv", + "useProgram", + "validateProgram", + "vertexAttrib1f", + "vertexAttrib1fv", + "vertexAttrib2f", + "vertexAttrib2fv", + "vertexAttrib3f", + "vertexAttrib3fv", + "vertexAttrib4f", + "vertexAttrib4fv", + "vertexAttribPointer", + "viewport", +]; + +var webgl2Methods = [ + "getBufferSubData", + "copyBufferSubData", + "blitFramebuffer", + "framebufferTextureLayer", + "getInternalformatParameter", + "invalidateFramebuffer", + "invalidateSubFramebuffer", + "readBuffer", + "renderbufferStorageMultisample", + "texImage3D", + "texStorage2D", + "texStorage3D", + "texSubImage3D", + "copyTexSubImage3D", + "compressedTexImage3D", + "compressedTexSubImage3D", + "getFragDataLocation", + "uniform1ui", + "uniform2ui", + "uniform3ui", + "uniform4ui", + "uniform1uiv", + "uniform2uiv", + "uniform3uiv", + "uniform4uiv", + "uniformMatrix2x3fv", + "uniformMatrix3x2fv", + "uniformMatrix2x4fv", + "uniformMatrix4x2fv", + "uniformMatrix3x4fv", + "uniformMatrix4x3fv", + "vertexAttribI4i", + "vertexAttribI4iv", + "vertexAttribI4ui", + "vertexAttribI4uiv", + "vertexAttribIPointer", + "vertexAttribDivisor", + "drawArraysInstanced", + "drawElementsInstanced", + "drawRangeElements", + "drawBuffers", + "clearBufferiv", + "clearBufferuiv", + "clearBufferfv", + "clearBufferfi", + "createQuery", + "deleteQuery", + "isQuery", + "beginQuery", + "endQuery", + "getQuery", + "getQueryParameter", + "createSampler", + "deleteSampler", + "isSampler", + "bindSampler", + "samplerParameteri", + "samplerParameterf", + "getSamplerParameter", + "fenceSync", + "isSync", + "deleteSync", + "clientWaitSync", + "waitSync", + "getSyncParameter", + "createTransformFeedback", + "deleteTransformFeedback", + "isTransformFeedback", + "bindTransformFeedback", + "beginTransformFeedback", + "endTransformFeedback", + "transformFeedbackVaryings", + "getTransformFeedbackVarying", + "pauseTransformFeedback", + "resumeTransformFeedback", + "bindBufferBase", + "bindBufferRange", + "getIndexedParameter", + "getUniformIndices", + "getActiveUniforms", + "getUniformBlockIndex", + "getActiveUniformBlockParameter", + "getActiveUniformBlockName", + "uniformBlockBinding", + "createVertexArray", + "deleteVertexArray", + "isVertexArray", + "bindVertexArray", +]; + +function assertFunction(v, f) { + try { + if (typeof v[f] != "function") { + return false; + } else { + return true; + } + } catch(e) { + return false; + } +} + +function testAPIs(contextType) { + canvas = new OffscreenCanvas(10, 10); + gl = canvas.getContext(contextType); + var passed = true; + var methods; + if (contextType == 'webgl') + methods = webgl1Methods; + else + methods = webgl1Methods.concat(webgl2Methods); + for (var i=0; i<methods.length; i++) { + var r = assertFunction(gl, methods[i]); + passed = passed && r; + } + + methods.push(...["makeXRCompatible"]); + var extended = false; + for (var i in gl) { + if (typeof gl[i] == "function" && methods.indexOf(i) == -1) { + if (!extended) { + extended = true; + } + } + } + + if (!passed || extended) + return false; + return true; +} + +var simpleTextureVertexShader = [ + 'attribute vec4 vPosition;', + 'attribute vec2 texCoord0;', + 'varying vec2 texCoord;', + 'void main() {', + ' gl_Position = vPosition;', + ' texCoord = texCoord0;', + '}'].join('\n'); + +var simpleTextureFragmentShader = [ + 'precision mediump float;', + 'uniform sampler2D tex;', + 'varying vec2 texCoord;', + 'void main() {', + ' gl_FragData[0] = texture2D(tex, texCoord);', + '}'].join('\n'); + +function getShader(gl, shaderStr, type) +{ + var shader = gl.createShader(type); + gl.shaderSource(shader, shaderStr); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) + return null; + return shader; +} + +function setupProgram(gl, shaders, opt_attribs, opt_locations) +{ + var vertexShader = getShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER); + var fragmentShader = getShader(gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER); + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + + if (opt_attribs) { + for (var ii = 0; ii < opt_attribs.length; ++ii) { + gl.bindAttribLocation( + program, + opt_locations ? opt_locations[ii] : ii, + opt_attribs[ii]); + } + } + gl.linkProgram(program); + + // Check the link status + var linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + // something went wrong with the link + gl.deleteProgram(program); + return null; + } + gl.useProgram(program); + return program; +} + +function setupSimpleTextureProgram(gl, opt_positionLocation, opt_texcoordLocation) +{ + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + return setupProgram(gl, + [simpleTextureVertexShader, simpleTextureFragmentShader], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); +} + +function testLostContextWithoutRestore() +{ + // Functions with special return values. + if (!gl.isContextLost()) + return false; + + if (gl.getError() != gl.CONTEXT_LOST_WEBGL) + return false; + if (gl.getError() != gl.NO_ERROR) + return false; + + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_UNSUPPORTED || + gl.getAttribLocation(program, 'u_modelViewProjMatrix') != -1 || + gl.getVertexAttribOffset(0, gl.VERTEX_ATTRIB_ARRAY_POINTER) != 0) + return false; + + // Test the extension itself. + if (!compareGLError(gl.INVALID_OPERATION, "extension.loseContext()")) + return false; + + imageData = new ImageData(1, 1); + float32array = new Float32Array(1); + int32array = new Int32Array(1); + + // Functions returning void should return immediately. + // This is untestable, but we can at least be sure they cause no errors + // and the codepaths are exercised. + if (!compareGLError(gl.NO_ERROR, "gl.activeTexture(gl.TEXTURE0)") || + !compareGLError(gl.NO_ERROR, "gl.attachShader(program, shader)") || + !compareGLError(gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, buffer)") || + !compareGLError(gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)") || + !compareGLError(gl.NO_ERROR, "gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer)") || + !compareGLError(gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, texture)") || + !compareGLError(gl.NO_ERROR, "gl.blendColor(1.0, 1.0, 1.0, 1.0)") || + !compareGLError(gl.NO_ERROR, "gl.blendEquation(gl.FUNC_ADD)") || + !compareGLError(gl.NO_ERROR, "gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD)") || + !compareGLError(gl.NO_ERROR, "gl.blendFunc(gl.ONE, gl.ONE)") || + !compareGLError(gl.NO_ERROR, "gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE)") || + !compareGLError(gl.NO_ERROR, "gl.bufferData(gl.ARRAY_BUFFER, 0, gl.STATIC_DRAW)") || + !compareGLError(gl.NO_ERROR, "gl.bufferData(gl.ARRAY_BUFFER, arrayBufferView, gl.STATIC_DRAW)") || + !compareGLError(gl.NO_ERROR, "gl.bufferData(gl.ARRAY_BUFFER, arrayBuffer, gl.STATIC_DRAW)") || + !compareGLError(gl.NO_ERROR, "gl.bufferSubData(gl.ARRAY_BUFFRE, 0, arrayBufferView)") || + !compareGLError(gl.NO_ERROR, "gl.bufferSubData(gl.ARRAY_BUFFRE, 0, arrayBuffer)") || + !compareGLError(gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)") || + !compareGLError(gl.NO_ERROR, "gl.clearColor(1, 1, 1, 1)") || + !compareGLError(gl.NO_ERROR, "gl.clearDepth(1)") || + !compareGLError(gl.NO_ERROR, "gl.clearStencil(0)") || + !compareGLError(gl.NO_ERROR, "gl.colorMask(1, 1, 1, 1)") || + !compareGLError(gl.NO_ERROR, "gl.compileShader(shader)") || + !compareGLError(gl.NO_ERROR, "gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.cullFace(gl.FRONT)") || + !compareGLError(gl.NO_ERROR, "gl.deleteBuffer(buffer)") || + !compareGLError(gl.NO_ERROR, "gl.deleteFramebuffer(framebuffer)") || + !compareGLError(gl.NO_ERROR, "gl.deleteProgram(program)") || + !compareGLError(gl.NO_ERROR, "gl.deleteRenderbuffer(renderbuffer)") || + !compareGLError(gl.NO_ERROR, "gl.deleteShader(shader)") || + !compareGLError(gl.NO_ERROR, "gl.deleteTexture(texture)") || + !compareGLError(gl.NO_ERROR, "gl.depthFunc(gl.NEVER)") || + !compareGLError(gl.NO_ERROR, "gl.depthMask(0)") || + !compareGLError(gl.NO_ERROR, "gl.depthRange(0, 1)") || + !compareGLError(gl.NO_ERROR, "gl.detachShader(program, shader)") || + !compareGLError(gl.NO_ERROR, "gl.disable(gl.BLEND)") || + !compareGLError(gl.NO_ERROR, "gl.disableVertexAttribArray(0)") || + !compareGLError(gl.NO_ERROR, "gl.drawArrays(gl.POINTS, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.drawElements(gl.POINTS, 0, gl.UNSIGNED_SHORT, 0)") || + !compareGLError(gl.NO_ERROR, "gl.enable(gl.BLEND)") || + !compareGLError(gl.NO_ERROR, "gl.enableVertexAttribArray(0)") || + !compareGLError(gl.NO_ERROR, "gl.finish()") || + !compareGLError(gl.NO_ERROR, "gl.flush()") || + !compareGLError(gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer)") || + !compareGLError(gl.NO_ERROR, "gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)") || + !compareGLError(gl.NO_ERROR, "gl.frontFace(gl.CW)") || + !compareGLError(gl.NO_ERROR, "gl.generateMipmap(gl.TEXTURE_2D)") || + !compareGLError(gl.NO_ERROR, "gl.hint(gl.GENERATE_MIPMAP_HINT, gl.FASTEST)") || + !compareGLError(gl.NO_ERROR, "gl.lineWidth(0)") || + !compareGLError(gl.NO_ERROR, "gl.linkProgram(program)") || + !compareGLError(gl.NO_ERROR, "gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0)") || + !compareGLError(gl.NO_ERROR, "gl.polygonOffset(0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)") || + !compareGLError(gl.NO_ERROR, "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.sampleCoverage(0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.scissor(0, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.shaderSource(shader, '')") || + !compareGLError(gl.NO_ERROR, "gl.stencilFunc(gl.NEVER, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.stencilFuncSeparate(gl.FRONT, gl.NEVER, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.stencilMask(0)") || + !compareGLError(gl.NO_ERROR, "gl.stencilMaskSeparate(gl.FRONT, 0)") || + !compareGLError(gl.NO_ERROR, "gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP)") || + !compareGLError(gl.NO_ERROR, "gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.KEEP)") || + !compareGLError(gl.NO_ERROR, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)") || + !compareGLError(gl.NO_ERROR, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageData)") || + !compareGLError(gl.NO_ERROR, "gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)") || + !compareGLError(gl.NO_ERROR, "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)") || + !compareGLError(gl.NO_ERROR, "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)") || + !compareGLError(gl.NO_ERROR, "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, imageData)") || + !compareGLError(gl.NO_ERROR, "gl.uniform1f(uniformLocation, 0)") || + !compareGLError(gl.NO_ERROR, "gl.uniform1fv(uniformLocation, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniform1fv(uniformLocation, [0])") || + !compareGLError(gl.NO_ERROR, "gl.uniform1i(uniformLocation, 0)") || + !compareGLError(gl.NO_ERROR, "gl.uniform1iv(uniformLocation, int32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniform1iv(uniformLocation, [0])") || + !compareGLError(gl.NO_ERROR, "gl.uniform2f(uniformLocation, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.uniform2fv(uniformLocation, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniform2fv(uniformLocation, [0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.uniform2i(uniformLocation, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.uniform2iv(uniformLocation, int32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniform2iv(uniformLocation, [0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.uniform3f(uniformLocation, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.uniform3fv(uniformLocation, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniform3fv(uniformLocation, [0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.uniform3i(uniformLocation, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.uniform3iv(uniformLocation, int32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniform3iv(uniformLocation, [0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.uniform4f(uniformLocation, 0, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.uniform4fv(uniformLocation, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniform4fv(uniformLocation, [0, 0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.uniform4i(uniformLocation, 0, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.uniform4iv(uniformLocation, int32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniform4iv(uniformLocation, [0, 0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.uniformMatrix2fv(uniformLocation, false, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniformMatrix2fv(uniformLocation, false, [0, 0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.uniformMatrix3fv(uniformLocation, false, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniformMatrix3fv(uniformLocation, false, [0, 0, 0, 0, 0, 0, 0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.uniformMatrix4fv(uniformLocation, false, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.uniformMatrix4fv(uniformLocation, false, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.useProgram(program)") || + !compareGLError(gl.NO_ERROR, "gl.validateProgram(program)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib1f(0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib1fv(0, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib1fv(0, [0])") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib2f(0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib2fv(0, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib2fv(0, [0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib3f(0, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib3fv(0, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib3fv(0, [0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib4f(0, 0, 0, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib4fv(0, float32array)") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttrib4fv(0, [0, 0, 0, 0])") || + !compareGLError(gl.NO_ERROR, "gl.vertexAttribPointer(0, 0, gl.FLOAT, false, 0, 0)") || + !compareGLError(gl.NO_ERROR, "gl.viewport(0, 0, 0, 0)")) + return false; + + // Functions return nullable values should all return null. + if (gl.createBuffer() != null || + gl.createFramebuffer() != null || + gl.createProgram() != null || + gl.createRenderbuffer() != null || + gl.createShader(gl.GL_VERTEX_SHADER) != null || + gl.createTexture() != null || + gl.getActiveAttrib(program, 0) != null || + gl.getActiveUniform(program, 0) != null || + gl.getAttachedShaders(program) != null || + gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE) != null || + gl.getContextAttributes() != null || + gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) != null || + gl.getParameter(gl.CURRENT_PROGRAM) != null || + gl.getProgramInfoLog(program) != null || + gl.getProgramParameter(program, gl.LINK_STATUS) != null || + gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH) != null || + gl.getShaderInfoLog(shader) != null || + gl.getShaderParameter(shader, gl.SHADER_TYPE) != null || + gl.getShaderSource(shader) != null || + gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S) != null || + gl.getUniform(program, uniformLocation) != null || + gl.getUniformLocation(program, 'vPosition') != null || + gl.getVertexAttrib(0, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING) != null || + gl.getSupportedExtensions() != null || + gl.getExtension("WEBGL_lose_context") != null) + return false; + + // "Is" queries should all return false. + if (gl.isBuffer(buffer) || gl.isEnabled(gl.BLEND) || gl.isFramebuffer(framebuffer) || + gl.isProgram(program) || gl.isRenderbuffer(renderbuffer) || gl.isShader(shader) || + gl.isTexture(texture)) + return false; + + if (gl.getError() != gl.NO_ERROR) + return false; + + // test extensions + if (OES_vertex_array_object) { + if (!compareGLError(gl.NO_ERROR, "OES_vertex_array_object.bindVertexArrayOES(vertexArrayObject)") || + !compareGLError(gl.NO_ERROR, "OES_vertex_array_object.isVertexArrayOES(vertexArrayObject)") || + !compareGLError(gl.NO_ERROR, "OES_vertex_array_object.deleteVertexArrayOES(vertexArrayObject)")) + return false; + if (OES_vertex_array_object.createVertexArrayOES() != null) + return false; + } + return true; +} +function testValidContext() +{ + if (gl.isContextLost()) + return false; + + arrayBuffer = new ArrayBuffer(4); + arrayBufferView = new Int8Array(arrayBuffer); + + // Generate resources for testing. + buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + program = setupSimpleTextureProgram(gl); + renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + shader = gl.createShader(gl.VERTEX_SHADER); + texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + if (gl.getError() != gl.NO_ERROR) + return false; + + // Test is queries that will later be false + if (!compareGLError(gl.NO_ERROR, "gl.enable(gl.BLEND)")) + return false; + if (!gl.isBuffer(buffer) || !gl.isEnabled(gl.BLEND) || !gl.isFramebuffer(framebuffer) || + !gl.isProgram(program) || !gl.isRenderbuffer(renderbuffer) || !gl.isShader(shader) || + !gl.isTexture(texture)) + return false; + + if (OES_vertex_array_object) { + vertexArrayObject = OES_vertex_array_object.createVertexArrayOES(); + if (gl.getError() != gl.NO_ERROR) + return false; + if (!OES_vertex_array_object.isVertexArrayOES(vertexArrayObject)) + return false; + } + return true; +} + +function setupTest() +{ + canvas = new OffscreenCanvas(10, 10); + gl = canvas.getContext('webgl'); + WEBGL_lose_context = gl.getExtension("WEBGL_lose_context"); + if (!WEBGL_lose_context) + return false; + + // Try to get a few extensions + OES_vertex_array_object = gl.getExtension("OES_vertex_array_object"); + OES_texture_float = gl.getExtension("OES_texture_float"); + + return true; +} + +function testOriginalContext() +{ + if (gl.isContextLost()) + return false; + if (gl.getError() != gl.NO_ERROR) + return false; + return true; +} + +function testLostContext(e) +{ + if (contextLostEventFired) + return false; + contextLostEventFired = true; + if (!gl.isContextLost()) + return false; + if (gl.getError() != gl.NO_ERROR) + return false; + if (allowRestore) + e.preventDefault(); + return true; +} + +function testLosingAndRestoringContext() +{ + return new Promise(function(resolve, reject) { + if (!setupTest()) + reject("Test failed"); + + canvas.addEventListener("webglcontextlost", function(e) { + if (!testLostContext(e)) + reject("Test failed"); + // restore the context after this event has exited. + setTimeout(function() { + if (!compareGLError(gl.NO_ERROR, "WEBGL_lose_context.restoreContext()")) + reject("Test failed"); + // The context should still be lost. It will not get restored until the + // webglrestorecontext event is fired. + if (!gl.isContextLost()) + reject("Test failed"); + if (gl.getError() != gl.NO_ERROR) + reject("Test failed"); + // gl methods should still be no-ops + if (!compareGLError(gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)")) + reject("Test failed"); + }, 0); + }); + canvas.addEventListener("webglcontextrestored", function() { + if (!testRestoredContext()) + reject("Test failed"); + else + resolve("Test passed"); + }); + allowRestore = true; + contextLostEventFired = false; + contextRestoredEventFired = false; + + if (!testOriginalContext()) + reject("Test failed"); + WEBGL_lose_context.loseContext(); + // The context should be lost immediately. + if (!gl.isContextLost()) + reject("Test failed"); + if (gl.getError() != gl.CONTEXT_LOST_WEBGL) + reject("Test failed"); + if (gl.getError() != gl.NO_ERROR) + reject("Test failed"); + // gl methods should be no-ops + if (!compareGLError(gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)")) + reject("Test failed"); + // but the event should not have been fired. + if (contextLostEventFired) + reject("Test failed"); + }); +} + +function reGetExtensionAndTestForProperty(gl, name, expectProperty) { + var newExtension = gl.getExtension(name); + // NOTE: while getting a extension after context lost/restored is allowed to fail + // for the purpose the conformance tests it is not. + // + // Hypothetically the user can switch GPUs live. For example on Windows, install 2 GPUs, + // then in the control panen enable 1, disable the others and visa versa. Since the GPUs + // have different capabilities one or the other may not support a particlar extension. + // + // But, for the purpose of the conformance tests the context is expected to restore + // on the same GPU and therefore the extensions that succeeded previously should + // succeed on restore. + if (newExtension == null) + return false; + if (expectProperty) { + if (!(newExtension.webglTestProperty === true)) + return false; + } else { + if (!(newExtension.webglTestProperty === undefined)) + return false; + } + return newExtension; +} + + +function testOESTextureFloat() { + if (OES_texture_float) { + // Extension must still be lost. + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + if (!compareGLError(gl.INVALID_ENUM, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.FLOAT, null)")) + return false; + // Try re-enabling extension + OES_texture_float = reGetExtensionAndTestForProperty(gl, "OES_texture_float", false); + if (!compareGLError(gl.NO_ERROR, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.FLOAT, null)")) + return false; + return true; + } +} + +function testOESVertexArrayObject() { + if (OES_vertex_array_object) { + // Extension must still be lost. + if (OES_vertex_array_object.createVertexArrayOES() != null) + return false; + // Try re-enabling extension + + var old_OES_vertex_array_object = OES_vertex_array_object; + OES_vertex_array_object = reGetExtensionAndTestForProperty(gl, "OES_vertex_array_object", false); + if (OES_vertex_array_object.createVertexArrayOES() == null) + return false; + if (old_OES_vertex_array_object.createVertexArrayOES() != null) + return false; + return true; + } +} + +function testExtensions() { + if (!testOESTextureFloat() || !testOESVertexArrayObject()) + return false; + return true; +} + +function testRestoredContext() +{ + if (contextRestoredEventFired) + return false; + contextRestoredEventFired = true; + if (gl.isContextLost()) + return false; + if (gl.getError() != gl.NO_ERROR) + return false; + + if (!testExtensions()) + return false; + return true; +} + diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/clipping-wide-points.js b/dom/canvas/test/webgl-conf/checkout/js/tests/clipping-wide-points.js new file mode 100644 index 0000000000..6b8fc00599 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/clipping-wide-points.js @@ -0,0 +1,92 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; +description("This test ensures clipping works with wide points whose centers are out of the viewport"); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("testbed", undefined, contextVersion); + +var pointSize; + +function setupProgram() { + var vs = "attribute vec4 pos;" + + "uniform float pointSize; " + + "void main() {" + + " gl_PointSize = pointSize;" + + " gl_Position = pos;" + + "}"; + var fs = "precision mediump float;" + + "void main() {" + + " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);" + + "}"; + var program = wtu.setupProgram(gl, [vs, fs], ['pos']); + if (program) { + var loc = gl.getUniformLocation(program, 'pointSize'); + gl.uniform1f(loc, pointSize); + gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors after setting up program"); + } + return program; +} + +function runOneTestCase(vertex) { + debug(""); + debug("testing point at (" + vertex[0] + ", " + vertex[1] + ", " + vertex[2] + ")"); + var data = new Float32Array(vertex); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, data); + + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.checkCanvasRect(gl, 0, 0, 1, 1, [0, 255, 0]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors after running one test case"); +} + +function runTests() { + if (!gl) { + testFailed("context does not exist"); + return; + } + + var range = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE); + if (range[1] < 2.0) { + testPassed("ALIASDED_POINT_SIZE_RANGE less than 2"); + return; + } + pointSize = 2.0; + + var data = new Float32Array(4); + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); + + var program = setupProgram(); + if (!program) { + testFailed("fail to set up program"); + return; + } + + gl.disable(gl.BLEND); + gl.disable(gl.DITHER); + gl.disable(gl.DEPTH_TEST); + + gl.clearColor(1.0, 0.0, 0.0, 1.0); + + var vertices = [ + [ 0.99, 0.5, 0.0, 1.0 ], + [ 1.01, 0.5, 0.0, 1.0 ], + [ 0.5, 0.99, 0.0, 1.0 ], + [ 0.5, 1.01, 0.0, 1.0 ], + ]; + for (var idx = 0; idx < vertices.length; ++idx) { + runOneTestCase(vertices[idx]); + } +} + +runTests(); +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/compositing-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/compositing-test.js new file mode 100644 index 0000000000..ceccbc406e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/compositing-test.js @@ -0,0 +1,136 @@ +var createCompositingTestFn = (function() { + +const width = 20; +const height = 20; + +function waitForComposite() { + debug('wait for composite'); + return new Promise(resolve => wtu.waitForComposite(resolve)); +} + +async function testPreserveDrawingBufferFalse(gl, drawFn, clear) { + debug(''); + debug(`test preserveDrawingBuffer: false with ${drawFn.name} ${clear ? 'with' : 'without'} clear`); + + if (clear) { + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + if (drawFn(gl)) { + debug('skipped: extension does not exist'); + return; + } + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + + // enable scissor here, before compositing, to make sure it's correctly + // ignored and restored + const halfWidth = gl.canvas.width / 2; + const halfHeight = gl.canvas.height / 2; + gl.scissor(0, halfHeight, halfWidth, halfHeight); + gl.enable(gl.SCISSOR_TEST); + + await waitForComposite(); + + // scissor was set earlier + gl.clearColor(0, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + wtu.checkCanvasRect(gl, 0, halfHeight, halfWidth, halfHeight, [0, 0, 255, 255], + "cleared corner should be blue, stencil should be preserved"); + wtu.checkCanvasRect(gl, 0, 0, halfWidth, halfHeight, [0, 0, 0, 0], + "remainder of buffer should be cleared"); + + gl.disable(gl.SCISSOR_TEST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +async function testPreserveDrawingBufferTrue(gl, drawFn, clear) { + debug(''); + debug(`test preserveDrawingBuffer: true with ${drawFn.name} ${clear ? 'with' : 'without'} clear`); + + if (clear) { + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + const skipTest = drawFn(gl); + if (skipTest) { + debug('skipped: extension does not exist'); + return; + } + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + + await waitForComposite(); + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function setupWebGL({ + webglVersion, + shadersFn, + attribs, +}) { + const existingCanvases = document.querySelectorAll('canvas'); + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + canvas.style.display = 'block'; + canvas.style.position = 'fixed'; + canvas.style.left = `${existingCanvases.length * 25}px`; + canvas.style.top = '0'; + // The canvas needs to be visible or the test will fail. + document.body.insertBefore(canvas, [...existingCanvases].pop()); + const gl = wtu.create3DContext(canvas, attribs, webglVersion); + if (!gl) { + testFailed('WebGL context creation failed'); + return gl; + } + + const shaders = shadersFn(gl); + const program = wtu.setupProgram(gl, shaders, ["position"]); + if (!program) { + debug(`program failed to compile: ${wtu.getLastError()}`); + } + const positionBuf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1, -1, + 1, -1, + -1, 1, + -1, 1, + 1, -1, + 1, 1, + ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + const indexBuf = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuf); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 3, 4, 5]), gl.STATIC_DRAW); + return gl; +} + +function createCompositingTestFn(options) { + const glPreserveDrawingBufferFalse = setupWebGL({ + ...options, + attribs: {antialias: false}, + }); + const glPreserveDrawingBufferTrue = setupWebGL({ + ...options, + attribs: {antialias: false, preserveDrawingBuffer: true}, + }); + return async function(drawFn) { + debug('---'); + await testPreserveDrawingBufferFalse(glPreserveDrawingBufferFalse, drawFn, false); + await testPreserveDrawingBufferFalse(glPreserveDrawingBufferFalse, drawFn, true); + + await testPreserveDrawingBufferTrue(glPreserveDrawingBufferTrue, drawFn, false); + await testPreserveDrawingBufferTrue(glPreserveDrawingBufferTrue, drawFn, true); + }; +} + +return createCompositingTestFn; +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/compound-assignment-type-combination.js b/dom/canvas/test/webgl-conf/checkout/js/tests/compound-assignment-type-combination.js new file mode 100644 index 0000000000..41a49bced4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/compound-assignment-type-combination.js @@ -0,0 +1,133 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; + +// ESSL 1.00 spec section 5.8 (also ESSL 3.00 spec section 5.8): +// "The l-value and the expression must satisfy the semantic requirements of both op and equals (=)" +// In the semantic requirements of assignment (=): +// "The lvalue-expression and rvalue-expression must have the same type" + +var runTest = function(contextVersion) { + var vertexTemplateESSL1 = [ + 'precision mediump float;', + + 'uniform $(rtype) ur;', + 'uniform $(ltype) ul;', + + 'void main() {', + ' $(ltype) a = ul;', + ' a $(op) ur;', + ' gl_Position = vec4(float(a$(ltypeToScalar)));', + '}' + ].join('\n'); + + var vertexTemplateESSL3 = [ + '#version 300 es', + vertexTemplateESSL1 + ].join('\n'); + + var fragmentTemplateESSL1 = [ + 'precision mediump float;', + + 'uniform $(rtype) ur;', + 'uniform $(ltype) ul;', + + 'void main() {', + ' $(ltype) a = ul;', + ' a $(op) ur;', + ' gl_FragColor = vec4(float(a$(ltypeToScalar)));', + '}' + ].join('\n'); + + var fragmentTemplateESSL3 = [ + '#version 300 es', + 'out mediump vec4 my_FragColor;', + fragmentTemplateESSL1 + ].join('\n').replace('gl_FragColor', 'my_FragColor'); + + var isNonSquareMatrix = function(typeStr) { + return typeStr.substring(0, 3) == 'mat' && + typeStr.length > 5 && + typeStr[3] != typeStr[5]; + } + + var vsTemplate = contextVersion < 2 ? vertexTemplateESSL1 : vertexTemplateESSL3; + var fsTemplate = contextVersion < 2 ? fragmentTemplateESSL1 : fragmentTemplateESSL3; + + var wtu = WebGLTestUtils; + + var tests = []; + + var baseTypes = ['float', 'int']; + var vecTypes = [['vec2', 'vec3', 'vec4', 'mat2', 'mat3', 'mat4'], ['ivec2', 'ivec3', 'ivec4']]; + if (contextVersion >= 2) { + vecTypes[0] = ['vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'mat2x3', 'mat2x4', 'mat3x2', 'mat3x4', 'mat4x2', 'mat4x3']; + } + var ops = ['+=', '-=', '*=', '/=']; + + var fs, vs; + for (var k = 0; k < ops.length; ++k) { + var op = ops[k]; + for (var i = 0; i < baseTypes.length; ++i) { + var baseType = baseTypes[i]; + for (var j = 0; j < vecTypes[i].length; ++j) { + var vecType = vecTypes[i][j]; + var vecTypeToScalar = vecType.substring(0, 3) == 'mat' ? '[0].x' : '.x'; + + var pushTest = function(ltype, rtype, ltypeToScalar, expectSuccess) { + vs = wtu.replaceParams(vsTemplate, {ltype: ltype, rtype: rtype, ltypeToScalar: ltypeToScalar, op: op}); + fs = wtu.replaceParams(fsTemplate, {ltype: ltype, rtype: rtype, ltypeToScalar: ltypeToScalar, op: op}); + tests.push({ + vShaderSource: vs, + vShaderSuccess: expectSuccess, + linkSuccess: expectSuccess, + passMsg: ltype + " " + op + " " + rtype + " in a vertex shader should " + (expectSuccess ? "succeed." : "fail.") + }); + tests.push({ + fShaderSource: fs, + fShaderSuccess: expectSuccess, + linkSuccess: expectSuccess, + passMsg: ltype + " " + op + " " + rtype + " in a fragment shader should " + (expectSuccess ? "succeed." : "fail.") + }); + } + + // "scalar op= vector" is not okay, since the result of op is a vector, + // which can't be assigned to a scalar. + pushTest(baseType, vecType, '', false); + + if (j > 0) { + var vecType2 = vecTypes[i][j - 1]; + // "vector1 op= vector2" is not okay when vector1 and vector2 have + // non-matching dimensions. + pushTest(vecType, vecType2, vecTypeToScalar, false); + } + + // "vector op= scalar" is okay. + pushTest(vecType, baseType, vecTypeToScalar, true); + + // vecX *= matX is okay (effectively, this treats vector as a row vector). + if (vecType.substring(0, 3) == 'vec' && op == '*=') { + pushTest(vecType, 'mat' + vecType[3], vecTypeToScalar, true); + } + + if (op != '*=' || !isNonSquareMatrix(vecType)) { + // "vector1 op= vector2" is okay when vector1 and vector2 have the same + // type (does a component-wise operation or matrix multiplication). + pushTest(vecType, vecType, vecTypeToScalar, true); + } else { + // non-square matrices can only be compound multiplied with a square matrix. + pushTest(vecType, vecType, vecTypeToScalar, false); + pushTest(vecType, 'mat' + vecType[3], vecTypeToScalar, true); + } + } + } + } + + GLSLConformanceTester.runTests(tests, contextVersion); +} + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-tex-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-tex-image.js new file mode 100644 index 0000000000..7886181f4c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-tex-image.js @@ -0,0 +1,138 @@ +"use strict"; +description("This test ensures WebGL implementations correctly implement querying for compressed textures when extensions are disabled."); + +debug(""); + +const wtu = WebGLTestUtils; +const gl = wtu.create3DContext(null, undefined, contextVersion); + +const COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; +const COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; + +let formats = null; +let ext; + +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + wtu.shouldGenerateGLError(gl, [gl.INVALID_ENUM, gl.INVALID_OPERATION], + "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 10, 10, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, new Uint8Array(8));"); + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "formats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)"); + shouldBeNonNull("formats"); + shouldBe("formats.length", "0"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 4, 4, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4*4*4));"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, + "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, new Uint8Array(8));"); + + // Check too-many and too-few args. + + wtu.shouldThrow(gl, false, "too many args", function() { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 4, 4, 0, new Uint8Array(8), null); + }); + wtu.shouldThrow(gl, TypeError, "too few args", function() { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 4, 4, 0); + }); + + wtu.shouldThrow(gl, false, "too many args", function() { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, new Uint8Array(8), null); + }); + wtu.shouldThrow(gl, TypeError, "too few args", function() { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, COMPRESSED_RGB_PVRTC_4BPPV1_IMG); + }); + + // - + + let pbo; + // WebGL 2.0 specific + if (gl.PIXEL_UNPACK_BUFFER) { + pbo = gl.createBuffer(); + } + + gl.bindTexture(gl.TEXTURE_2D, tex); + + function validateExt(extName, enumName, blockSize, blockByteSize, expectedSubImageError) { + debug('\n---------------------------'); + debug('\n' + extName); + ext = gl.getExtension(extName); + if (!ext) { + testPassed(`Optional ext ${extName} MAY be unsupported.`); + return; + } + testPassed(`Optional ext ${extName} is supported.`); + + const data = new Uint8Array(blockByteSize); + + const views = [ + data, + new Uint8ClampedArray(data.buffer), + new Int8Array(data.buffer), + new Uint16Array(data.buffer), + new Int16Array(data.buffer), + new Uint32Array(data.buffer), + new Int32Array(data.buffer), + new Float32Array(data.buffer), + new DataView(data.buffer), + ]; + if (window.SharedArrayBuffer) { + const sharedBuffer = new SharedArrayBuffer(blockByteSize); + views.push( + new Uint8Array(sharedBuffer), + new Uint8ClampedArray(sharedBuffer), + new DataView(sharedBuffer) + ); + } + + for (const view of views) { + window.g_view = view; + debug(`\nfrom ${view.constructor.name} of ${view.buffer.constructor.name}`); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, g_view)`); + + wtu.shouldGenerateGLError(gl, expectedSubImageError, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, g_view)`); + } + + if (pbo) { + debug('\nfrom PBO'); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.bufferData(gl.PIXEL_UNPACK_BUFFER, ${blockByteSize}*2, gl.STATIC_DRAW)`); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, ${blockByteSize}, 0)`); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, ${blockByteSize}, 1)`); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, ${blockByteSize}, ${blockByteSize})`); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, ${blockByteSize}, ${blockByteSize+1})`); + + wtu.shouldGenerateGLError(gl, expectedSubImageError, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, ${blockByteSize}, 0)`); + wtu.shouldGenerateGLError(gl, expectedSubImageError, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, ${blockByteSize}, 1)`); + wtu.shouldGenerateGLError(gl, expectedSubImageError, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, ${blockByteSize}, ${blockByteSize})`); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, ${blockByteSize}, ${blockByteSize+1})`); + + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + } + } + + validateExt('WEBGL_compressed_texture_s3tc', 'COMPRESSED_RGBA_S3TC_DXT5_EXT', 4, 16, gl.NO_ERROR); + validateExt('WEBGL_compressed_texture_etc1', 'COMPRESSED_RGB_ETC1_WEBGL', 4, 8, gl.INVALID_OPERATION); + validateExt('WEBGL_compressed_texture_etc', 'COMPRESSED_RGBA8_ETC2_EAC', 4, 16, gl.NO_ERROR); + validateExt('WEBGL_compressed_texture_astc', 'COMPRESSED_RGBA_ASTC_4x4_KHR', 4, 16, gl.NO_ERROR); +} + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js new file mode 100644 index 0000000000..46d155f5f1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js @@ -0,0 +1,258 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +"use strict"; + +let CompressedTextureUtils = (function() { + +let formatToString = function(ext, format) { + for (let p in ext) { + if (ext[p] == format) { + return p; + } + } + return "0x" + format.toString(16); +}; + +/** + * Make an image element from Uint8Array bitmap data. + * @param {number} imageHeight Height of the data in pixels. + * @param {number} imageWidth Width of the data in pixels. + * @param {number} dataWidth Width of each row in the data buffer, in pixels. + * @param {Uint8Array} data Image data buffer to display. Each pixel takes up 4 bytes in the array regardless of the alpha parameter. + * @param {boolean} alpha True if alpha data should be taken from data. Otherwise alpha channel is set to 255. + * @return {HTMLImageElement} The image element. + */ +let makeScaledImage = function(imageWidth, imageHeight, dataWidth, data, alpha, opt_scale) { + let scale = opt_scale ? opt_scale : 8; + let c = document.createElement("canvas"); + c.width = imageWidth * scale; + c.height = imageHeight * scale; + let ctx = c.getContext("2d"); + for (let yy = 0; yy < imageHeight; ++yy) { + for (let xx = 0; xx < imageWidth; ++xx) { + let offset = (yy * dataWidth + xx) * 4; + ctx.fillStyle = "rgba(" + + data[offset + 0] + "," + + data[offset + 1] + "," + + data[offset + 2] + "," + + (alpha ? data[offset + 3] / 255 : 1) + ")"; + ctx.fillRect(xx * scale, yy * scale, scale, scale); + } + } + return wtu.makeImageFromCanvas(c); +}; + +let insertCaptionedImg = function(parent, caption, img) { + let div = document.createElement("div"); + div.appendChild(img); + let label = document.createElement("div"); + label.appendChild(document.createTextNode(caption)); + div.appendChild(label); + parent.appendChild(div); +}; + +/** + * @param {WebGLRenderingContextBase} gl + * @param {Object} compressedFormats Mapping from format names to format enum values. + * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. + */ +let testCompressedFormatsUnavailableWhenExtensionDisabled = function(gl, compressedFormats, expectedByteLength, testSize) { + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + for (let name in compressedFormats) { + if (compressedFormats.hasOwnProperty(name)) { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, compressedFormats[name], testSize, testSize, 0, new Uint8Array(expectedByteLength(testSize, testSize, compressedFormats[name]))); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Trying to use format " + name + " with extension disabled."); + } + } + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(tex); +}; + +/** + * @param {WebGLRenderingContextBase} gl + * @param {Object} expectedFormats Mapping from format names to format enum values. + */ +let testCompressedFormatsListed = function(gl, expectedFormats) { + debug(""); + debug("Testing that every format is listed by the compressed texture formats query"); + + let supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS); + + let failed; + let count = 0; + for (let name in expectedFormats) { + if (expectedFormats.hasOwnProperty(name)) { + ++count; + let format = expectedFormats[name]; + failed = true; + for (let ii = 0; ii < supportedFormats.length; ++ii) { + if (format == supportedFormats[ii]) { + testPassed("supported format " + name + " exists"); + failed = false; + break; + } + } + if (failed) { + testFailed("supported format " + name + " does not exist"); + } + } + } + if (supportedFormats.length != count) { + testFailed("Incorrect number of supported formats, was " + supportedFormats.length + " should be " + count); + } +}; + +/** + * @param {Object} ext Compressed texture extension object. + * @param {Object} expectedFormats Mapping from format names to format enum values. + */ +let testCorrectEnumValuesInExt = function(ext, expectedFormats) { + debug(""); + debug("Testing that format enum values in the extension object are correct"); + + for (name in expectedFormats) { + if (expectedFormats.hasOwnProperty(name)) { + if (isResultCorrect(ext[name], expectedFormats[name])) { + testPassed("Enum value for " + name + " matches 0x" + ext[name].toString(16)); + } else { + testFailed("Enum value for " + name + " mismatch: 0x" + ext[name].toString(16) + " should be 0x" + expectedFormats[name].toString(16)); + } + } + } +}; + +/** + * @param {WebGLRenderingContextBase} gl + * @param {Object} validFormats Mapping from format names to format enum values. + * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. + * @param getBlockDimensions A function that takes in a format and returns block size in pixels. + */ +let testFormatRestrictionsOnBufferSize = function(gl, validFormats, expectedByteLength, getBlockDimensions) { + debug(""); + debug("Testing format restrictions on texture upload buffer size"); + + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + for (let formatId in validFormats) { + if (validFormats.hasOwnProperty(formatId)) { + let format = validFormats[formatId]; + let blockSize = getBlockDimensions(format); + let expectedSize = expectedByteLength(blockSize.width * 4, blockSize.height * 4, format); + let data = new Uint8Array(expectedSize); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 3, blockSize.height * 4, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too small width)"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 5, blockSize.height * 4, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too large width)"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 4, blockSize.height * 3, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too small height)"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 4, blockSize.height * 5, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too large height)"); + } + } +}; + +/** + * @param {WebGLRenderingContextBase} gl + * @param {Object} validFormats Mapping from format names to format enum values. + * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. + * @param getBlockDimensions A function that takes in a format and returns block size in pixels. + * @param {number} width Width of the image in pixels. + * @param {number} height Height of the image in pixels. + * @param {Object} subImageConfigs configs for compressedTexSubImage calls + */ +let testTexSubImageDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, width, height, subImageConfigs) { + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + for (let formatId in validFormats) { + if (validFormats.hasOwnProperty(formatId)) { + let format = validFormats[formatId]; + let blockSize = getBlockDimensions(format); + debug("testing " + ctu.formatToString(ext, format)); + let expectedSize = expectedByteLength(width, height, format); + let data = new Uint8Array(expectedSize); + + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setting up compressed texture"); + + for (let i = 0, len = subImageConfigs.length; i < len; ++i) { + let c = subImageConfigs[i]; + let subData = new Uint8Array(expectedByteLength(c.width, c.height, format)); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, c.xoffset, c.yoffset, c.width, c.height, format, subData); + wtu.glErrorShouldBe(gl, c.expectation, c.message); + } + } + } + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(tex); +}; + +let testTexImageLevelDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, imageConfigs) { + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + for (let formatId in validFormats) { + if (validFormats.hasOwnProperty(formatId)) { + let format = validFormats[formatId]; + let blockSize = getBlockDimensions(format); + debug("testing " + ctu.formatToString(ext, format)); + + for (let i = 0, len = imageConfigs.length; i < len; ++i) { + let c = imageConfigs[i]; + let data = new Uint8Array(expectedByteLength(c.width, c.height, format)); + gl.compressedTexImage2D(gl.TEXTURE_2D, c.level, format, c.width, c.height, 0, data); + wtu.glErrorShouldBe(gl, c.expectation, c.message); + } + } + } + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(tex); +} + +let testTexStorageLevelDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, imageConfigs) { + for (let formatId in validFormats) { + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + if (validFormats.hasOwnProperty(formatId)) { + let format = validFormats[formatId]; + let blockSize = getBlockDimensions(format); + debug("testing " + ctu.formatToString(ext, format)); + + for (let i = 0, len = imageConfigs.length; i < len; ++i) { + let c = imageConfigs[i]; + let data = new Uint8Array(expectedByteLength(c.width, c.height, format)); + if (i == 0) { + gl.texStorage2D(gl.TEXTURE_2D, imageConfigs.length, format, c.width, c.height); + wtu.glErrorShouldBe(gl, c.expectation, c.message); + } + gl.compressedTexSubImage2D(gl.TEXTURE_2D, i, 0, 0, c.width, c.height, format, data); + wtu.glErrorShouldBe(gl, c.expectation, c.message); + } + } + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(tex); + } +} + +return { + formatToString: formatToString, + insertCaptionedImg: insertCaptionedImg, + makeScaledImage: makeScaledImage, + testCompressedFormatsListed: testCompressedFormatsListed, + testCompressedFormatsUnavailableWhenExtensionDisabled: testCompressedFormatsUnavailableWhenExtensionDisabled, + testCorrectEnumValuesInExt: testCorrectEnumValuesInExt, + testFormatRestrictionsOnBufferSize: testFormatRestrictionsOnBufferSize, + testTexSubImageDimensions: testTexSubImageDimensions, + testTexImageLevelDimensions: testTexImageLevelDimensions, + testTexStorageLevelDimensions: testTexStorageLevelDimensions, +}; + +})();
\ No newline at end of file diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/context-methods.js b/dom/canvas/test/webgl-conf/checkout/js/tests/context-methods.js new file mode 100644 index 0000000000..f6476463d2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/context-methods.js @@ -0,0 +1,52 @@ +"use strict"; + +// Properties to be ignored because they were added in versions of the +// spec that are backward-compatible with this version +const IGNORED_METHODS = [ + // There is no official spec for the commit API yet, the proposal link is: + // https://wiki.whatwg.org/wiki/OffscreenCanvas + "commit", + + // For WebXR integration: + "makeXRCompatible", +]; + +function assertFunction(v, f) { + try { + if (typeof v[f] != "function") { + testFailed(`Property either does not exist or is not a function: ${f}`); + return false; + } else { + return true; + } + } catch(e) { + testFailed(`Trying to access the property '${f}' threw an error: ${e.toString()}`); + } +} + +function testContextMethods(gl, requiredContextMethods) { + const acceptableMethods = [].concat(requiredContextMethods, IGNORED_METHODS); + + let passed = true; + requiredContextMethods.forEach(method => { + const r = assertFunction(gl, method); + passed = passed && r; + }); + if (passed) { + testPassed("All WebGL methods found."); + } + let extended = false; + for (let propertyName of Object.getOwnPropertyNames(gl)) { + if (typeof gl[propertyName] == "function" && !acceptableMethods.includes(propertyName)) { + if (!extended) { + extended = true; + testFailed("Also found the following extra methods:"); + } + testFailed(propertyName); + } + } + + if (!extended) { + testPassed("No extra methods found on WebGL context."); + } +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js new file mode 100644 index 0000000000..51509e8a6e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js @@ -0,0 +1,473 @@ +"use strict"; + +function allocateTexture() +{ + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture parameter setup should succeed"); + return texture; +} + +function checkRenderingResults() +{ + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); +} + +function arrayToString(arr, size) { + var mySize; + if (!size) + mySize = arr.length; + else + mySize = size; + var out = "["; + for (var ii = 0; ii < mySize; ++ii) { + if (ii > 0) { + out += ", "; + } + out += arr[ii]; + } + return out + "]"; +} + +function runReadbackTest(testProgram, subtractor) +{ + // Verify floating point readback + debug("Checking readback of floating-point values"); + var buf = new Float32Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, buf); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels from floating-point framebuffer should succeed"); + var ok = true; + var tolerance = 8.0; // TODO: factor this out from both this test and the subtractor shader above. + for (var ii = 0; ii < buf.length; ++ii) { + if (Math.abs(buf[ii] - subtractor[ii]) > tolerance) { + ok = false; + break; + } + } + if (ok) { + testPassed("readPixels of float-type data from floating-point framebuffer succeeded"); + } else { + testFailed("readPixels of float-type data from floating-point framebuffer failed: expected " + + arrayToString(subtractor, 4) + ", got " + arrayToString(buf)); + } +} + +function runFloatTextureRenderTargetTest(enabled, internalFormatString, format, type, testProgram, numberOfChannels, subtractor, texSubImageCover) +{ + let internalFormat = eval(internalFormatString); + debug(""); + debug("testing floating-point " + internalFormatString + " texture render target" + (texSubImageCover > 0 ? " after calling texSubImage" : "")); + + var texture = allocateTexture(); + var width = 2; + var height = 2; + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "floating-point texture allocation should succeed"); + + // Try to use this texture as a render target. + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.bindTexture(gl.TEXTURE_2D, null); + + var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (!enabled) { + if (completeStatus == gl.FRAMEBUFFER_COMPLETE && !enabled) + testFailed("floating-point " + internalFormatString + " render target should not be supported"); + else + testPassed("floating-point " + internalFormatString + " render target should not be supported"); + return; + } + + if (completeStatus != gl.FRAMEBUFFER_COMPLETE) { + if (version == 1 && format == gl.RGB) + testPassed("floating-point " + internalFormatString + " render target not supported; this is allowed.") + else + testFailed("floating-point " + internalFormatString + " render target not supported"); + return; + } + + if (texSubImageCover > 0) { + // Ensure that replacing the whole texture or a part of it with texSubImage2D doesn't affect renderability + gl.bindTexture(gl.TEXTURE_2D, texture); + var data = new Float32Array(width * height * numberOfChannels * texSubImageCover); + gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height * texSubImageCover, format, type, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texSubImage2D should succeed if EXT_color_buffer_half_float is enabled"); + gl.bindTexture(gl.TEXTURE_2D, null); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("render target support changed after calling texSubImage2D"); + return; + } + } + + var renderProgram = + wtu.setupProgram(gl, + [wtu.simpleVertexShader, `void main() + { + gl_FragColor = vec4(1000.0, 1000.0, 1000.0, 1000.0); + }`], + ['vPosition'], + [0]); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering to floating-point texture should succeed"); + + // Now sample from the floating-point texture and verify we got the correct values. + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.useProgram(testProgram); + gl.uniform1i(gl.getUniformLocation(testProgram, "tex"), 0); + gl.uniform4fv(gl.getUniformLocation(testProgram, "subtractor"), subtractor); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering from floating-point texture should succeed"); + checkRenderingResults(); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + runReadbackTest(testProgram, subtractor); +} + +function runFloatRenderbufferRenderTargetTest(enabled, internalFormatString, testProgram, numberOfChannels, subtractor) +{ + var internalFormat = eval(internalFormatString); + var samples = [0]; + if (enabled && version > 1) { + samples = Array.prototype.slice.call(gl.getInternalformatParameter(gl.RENDERBUFFER, internalFormat, gl.SAMPLES)); + samples.push(0); + } + for (var ndx = 0; ndx < samples.length; ++ndx) { + debug(""); + debug("testing floating-point " + internalFormatString + " renderbuffer render target with number of samples " + samples[ndx]); + + var colorbuffer = gl.createRenderbuffer(); + var width = 2; + var height = 2; + gl.bindRenderbuffer(gl.RENDERBUFFER, colorbuffer); + if (samples[ndx] == 0) + gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height); + else + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[ndx], internalFormat, width, height); + if (!enabled) { + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "floating-point renderbuffer allocation should fail if EXT_color_buffer_half_float is not enabled or this is a 32 bit format"); + return; + } else { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "floating-point renderbuffer allocation should succeed if EXT_color_buffer_half_float is enabled"); + } + + // Try to use this renderbuffer as a render target. + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorbuffer); + + var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (completeStatus != gl.FRAMEBUFFER_COMPLETE) { + testFailed("floating-point " + internalFormatString + " render target not supported"); + return; + } + var resolveColorRbo = null; + var resolveFbo = null; + if (samples[ndx] > 0) { + resolveColorRbo = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, resolveColorRbo); + gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height); + resolveFbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, resolveFbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, resolveColorRbo); + completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (completeStatus != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Failed to create resolve framebuffer"); + return; + } + } + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.clearColor(1000.0, 1000.0, 1000.0, 1000.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + if (samples[ndx] > 0) { + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, resolveFbo); + gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, resolveFbo); + } + runReadbackTest(testProgram, subtractor); + } +} + +function runRGB16FNegativeTest() +{ + debug(""); + debug("testing RGB16F isn't color renderable"); + + var texture = allocateTexture(); + var width = 2; + var height = 2; + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB16F, width, height, 0, gl.RGB, gl.FLOAT, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "RGB16F texture allocation should succeed"); + + // Try to use this texture as a render target. + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.bindTexture(gl.TEXTURE_2D, null); + + var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (completeStatus == gl.FRAMEBUFFER_COMPLETE) + testFailed("RGB16F render target should not be supported with or without enabling EXT_color_buffer_half_float"); + else + testPassed("RGB16F render target should not be supported with or without enabling EXT_color_buffer_half_float"); + gl.deleteTexture(texture); + + var colorbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, colorbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB16F, width, height); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "RGB16F renderbuffer allocation should fail with or without enabling EXT_color_buffer_half_float"); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.deleteRenderbuffer(colorbuffer); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.deleteFramebuffer(fbo); +} + +function runUniqueObjectTest() +{ + debug(""); + debug("Testing that getExtension() returns the same object each time"); + gl.getExtension("EXT_color_buffer_half_float").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("EXT_color_buffer_half_float").myProperty', '2'); +} + +function runInternalFormatQueryTest() +{ + debug(""); + debug("testing the internal format query"); + + var maxSamples = gl.getParameter(gl.MAX_SAMPLES); + const formats = [gl.RGBA16F, gl.R16F, gl.RG16F]; + var firstMultiOnlyFormat = 4; + for (var fmt = 0; fmt < formats.length; ++fmt) { + var samples = gl.getInternalformatParameter(gl.RENDERBUFFER, formats[fmt], gl.SAMPLES); + if (fmt >= firstMultiOnlyFormat && (samples.length == 0 || samples[0] < maxSamples)) { + testFailed("the maximum value in SAMPLES should be at least " + maxSamples); + return; + } + + var prevSampleCount = 0; + var sampleCount; + for (var ndx = 0; ndx < samples.length; ++ndx, prevSampleCount = sampleCount) { + sampleCount = samples[ndx]; + // sample count must be > 0 + if (sampleCount <= 0) { + testFailed("Expected sample count to be at least one; got " + sampleCount); + return; + } + + // samples must be ordered descending + if (ndx > 0 && sampleCount >= prevSampleCount) { + testFailed("Expected sample count to be ordered in descending order; got " + prevSampleCount + " at index " + (ndx - 1) + ", and " + sampleCount + " at index " + ndx); + return; + } + } + } + testPassed("Internal format query succeeded"); +} + +function runCopyTexImageTest(enabled) +{ + var width = 16; + var height = 16; + var level = 0; + var cases = [ + { internalformat: "RGBA16F", format: "RGBA", destFormat: "R16F", valid: true, renderable: true, }, + { internalformat: "RGBA16F", format: "RGBA", destFormat: "RG16F", valid: true, renderable: true, }, + { internalformat: "RGBA16F", format: "RGBA", destFormat: "RGB16F", valid: true, renderable: true, }, + { internalformat: "RGBA16F", format: "RGBA", destFormat: "RGBA16F", valid: true, renderable: true, }, + { internalformat: "RGB16F", format: "RGB", destFormat: "R16F", valid: true, renderable: false, }, + { internalformat: "RGB16F", format: "RGB", destFormat: "RG16F", valid: true, renderable: false, }, + { internalformat: "RGB16F", format: "RGB", destFormat: "RGB16F", valid: true, renderable: false, }, + { internalformat: "RGB16F", format: "RGB", destFormat: "RGBA16F", valid: false, renderable: false, }, + { internalformat: "RG16F", format: "RG", destFormat: "R16F", valid: true, renderable: true, }, + { internalformat: "RG16F", format: "RG", destFormat: "RG16F", valid: true, renderable: true, }, + { internalformat: "RG16F", format: "RG", destFormat: "RGB16F", valid: false, renderable: true, }, + { internalformat: "RG16F", format: "RG", destFormat: "RGBA16F", valid: false, renderable: true, }, + { internalformat: "R16F", format: "RED", destFormat: "R16F", valid: true, renderable: true, }, + { internalformat: "R16F", format: "RED", destFormat: "RG16F", valid: false, renderable: true, }, + { internalformat: "R16F", format: "RED", destFormat: "RGB16F", valid: false, renderable: true, }, + { internalformat: "R16F", format: "RED", destFormat: "RGBA16F", valid: false, renderable: true, }, + ]; + if (version == 1) { + cases = [ + { valid: true, renderable: true, format: "RGBA", destFormat: "LUMINANCE", }, + { valid: true, renderable: true, format: "RGBA", destFormat: "ALPHA", }, + { valid: true, renderable: true, format: "RGBA", destFormat: "LUMINANCE_ALPHA", }, + { valid: true, renderable: true, format: "RGBA", destFormat: "RGB", }, + { valid: true, renderable: true, format: "RGBA", destFormat: "RGBA", }, + { valid: true, renderable: true, format: "RGB", destFormat: "LUMINANCE", }, + { valid: false, renderable: true, format: "RGB", destFormat: "ALPHA", }, + { valid: false, renderable: true, format: "RGB", destFormat: "LUMINANCE_ALPHA", }, + { valid: true, renderable: true, format: "RGB", destFormat: "RGB", }, + { valid: false, renderable: true, format: "RGB", destFormat: "RGBA", }, + { valid: true, renderable: false, format: "ALPHA", destFormat: "ALPHA", }, + { valid: true, renderable: false, format: "LUMINANCE", destFormat: "LUMINANCE", }, + { valid: true, renderable: false, format: "LUMINANCE_ALPHA", destFormat: "LUMINANCE_ALPHA", }, + ]; + } + cases.forEach(function(testcase) { + debug(""); + debug(`Testing CopyTexImage2D for format: ${testcase.format}, internalformat: ${testcase.internalformat}, destformat: ${testcase.destFormat}`); + + var format = gl[testcase.format]; + var internalformat = version > 1 ? gl[testcase.internalformat] : format; + var type = version > 1 ? gl.HALF_FLOAT : 0x8D61 /* HALF_FLOAT_OES */; + var destFormat = gl[testcase.destFormat]; + var texSrc = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texSrc); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + var data = new Uint16Array(width * height * 4); + gl.texImage2D(gl.TEXTURE_2D, level, internalformat, width, height, 0, format, type, data); + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texSrc, level); + var texDest = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texDest); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup framebuffer with texture should succeed."); + if (enabled && testcase.renderable) { + if (version == 1 && format == gl.RGB && gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT) { + testPassed("RGB framebuffer attachment not supported. This is allowed.") + } else { + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + gl.copyTexImage2D(gl.TEXTURE_2D, level, destFormat, 0, 0, width, height, 0); + wtu.glErrorShouldBe(gl, testcase.valid ? gl.NO_ERROR : [gl.INVALID_ENUM, gl.INVALID_OPERATION], "CopyTexImage2D"); + } + } else { + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); + gl.copyTexImage2D(gl.TEXTURE_2D, level, destFormat, 0, 0, width, height, 0); + wtu.glErrorShouldBe(gl, [gl.INVALID_ENUM, gl.INVALID_FRAMEBUFFER_OPERATION], "CopyTexImage2D should fail."); + } + + gl.deleteTexture(texDest); + gl.deleteTexture(texSrc); + gl.deleteFramebuffer(fbo); + }); +} + +description("This test verifies the functionality of the EXT_color_buffer_half_float extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, version); + +if (version < 2) { + // These are exposed on the extension, but we need them before the extension has been requested so we can + // make sure they don't work. + gl.R16F = 0x822D; + gl.RG16F = 0x822F; + gl.RGB16F = 0x881B; + gl.RGBA16F = 0x881A; +} + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + var texturedShaders = [ + wtu.simpleTextureVertexShader, + `precision mediump float; + uniform sampler2D tex; + uniform vec4 subtractor; + varying vec2 texCoord; + void main() + { + vec4 color = texture2D(tex, texCoord); + if (abs(color.r - subtractor.r) + + abs(color.g - subtractor.g) + + abs(color.b - subtractor.b) + + abs(color.a - subtractor.a) < 16.0) { + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } + }`, + ]; + var testProgram = + wtu.setupProgram(gl, + texturedShaders, + ['vPosition', 'texCoord0'], + [0, 1]); + var quadParameters = wtu.setupUnitQuad(gl, 0, 1); + + if (version > 1) { + // Ensure these formats can't be used for rendering if the extension is disabled + runFloatTextureRenderTargetTest(false, "gl.R16F", gl.RED, gl.FLOAT); + runFloatTextureRenderTargetTest(false, "gl.RG16F", gl.RG, gl.FLOAT); + runFloatTextureRenderTargetTest(false, "gl.RGBA16F", gl.RGBA, gl.FLOAT); + } + + runFloatRenderbufferRenderTargetTest(false, "gl.R16F"); + runFloatRenderbufferRenderTargetTest(false, "gl.RG16F"); + runFloatRenderbufferRenderTargetTest(false, "gl.RGBA16F"); + runFloatRenderbufferRenderTargetTest(false, "gl.R32F"); + runFloatRenderbufferRenderTargetTest(false, "gl.RG32F"); + runFloatRenderbufferRenderTargetTest(false, "gl.RGBA32F"); + runFloatRenderbufferRenderTargetTest(false, "gl.R11F_G11F_B10F"); + + if (version > 1) { + runCopyTexImageTest(false); + // Ensure RGB16F can't be used for rendering. + runRGB16FNegativeTest(); + } + + let oesTextureHalfFloat = null; + if (version == 1) { + // oesTextureHalfFloat implicitly enables EXT_color_buffer_half_float if supported + oesTextureHalfFloat = gl.getExtension("OES_texture_half_float"); + if (oesTextureHalfFloat && gl.getSupportedExtensions().includes("EXT_color_buffer_half_float")) { + runFloatTextureRenderTargetTest(true, "gl.RGBA", gl.RGBA, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 4, [1000, 1000, 1000, 1000], 0); + runFloatTextureRenderTargetTest(true, "gl.RGB", gl.RGB, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 3, [1000, 1000, 1000, 1], 0); + } + } + + var ext = null; + if (!(ext = gl.getExtension("EXT_color_buffer_half_float"))) { + testPassed("No EXT_color_buffer_half_float support -- this is legal"); + } else { + testPassed("Successfully enabled EXT_color_buffer_half_float extension"); + + shouldBe("ext.RGB16F_EXT", "gl.RGB16F"); + shouldBe("ext.RGBA16F_EXT", "gl.RGBA16F"); + shouldBe("ext.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT", "0x8211"); + shouldBe("ext.UNSIGNED_NORMALIZED_EXT", "0x8C17"); + + if (version > 1) { + runInternalFormatQueryTest(); + runFloatTextureRenderTargetTest(true, "gl.R16F", gl.RED, gl.FLOAT, testProgram, 1, [1000, 1, 1, 1], 0); + runFloatTextureRenderTargetTest(true, "gl.RG16F", gl.RG, gl.FLOAT, testProgram, 2, [1000, 1000, 1, 1], 0); + runFloatTextureRenderTargetTest(true, "gl.RGBA16F", gl.RGBA, gl.FLOAT, testProgram, 4, [1000, 1000, 1000, 1000], 0); + runFloatRenderbufferRenderTargetTest(true, "gl.R16F", testProgram, 1, [1000, 1, 1, 1]); + runFloatRenderbufferRenderTargetTest(true, "gl.RG16F", testProgram, 2, [1000, 1000, 1, 1]); + runFloatRenderbufferRenderTargetTest(true, "gl.RGBA16F", testProgram, 4, [1000, 1000, 1000, 1000]); + } + if (version == 1) { + shouldBeNonNull(oesTextureHalfFloat); // Required by spec + runFloatTextureRenderTargetTest(true, "gl.RGBA", gl.RGBA, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 4, [1000, 1000, 1000, 1000], 0); + runFloatTextureRenderTargetTest(true, "gl.RGB", gl.RGB, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 3, [1000, 1000, 1000, 1], 0); + runFloatTextureRenderTargetTest(false, "gl.LUMINANCE_ALPHA", gl.LUMINANCE_ALPHA, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 2, [1000, 1000, 1000, 1000], 0); + runFloatTextureRenderTargetTest(false, "gl.LUMINANCE", gl.LUMINANCE, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 1, [1000, 1, 1, 1], 0); + } + + if (version > 1) + runRGB16FNegativeTest(); // Ensure EXT_color_buffer_half_float does not enable RGB16F as color renderable. + + runCopyTexImageTest(true); + + runUniqueObjectTest(); + } +} + +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ext-float-blend.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-float-blend.js new file mode 100644 index 0000000000..ab2f9f9288 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-float-blend.js @@ -0,0 +1,237 @@ +'use strict'; + +const trivialVsSrc = ` +void main() +{ + gl_Position = vec4(0,0,0,1); +} +`; +const trivialFsSrc = ` +void main() +{ + gl_FragColor = vec4(0,1,0,1); +} +`; +const trivialVsMrtSrc100 = ` +void main() +{ + gl_Position = vec4(0,0,0,1); +} +`; +const trivialFsMrtSrc100 = ` +#extension GL_EXT_draw_buffers : require +precision mediump float; +void main() +{ + gl_FragData[0] = vec4(1, 0, 0, 1); + gl_FragData[1] = vec4(0, 1, 0, 1); +} +`; +const trivialVsMrtSrc300 = `#version 300 es +void main() +{ + gl_Position = vec4(0,0,0,1); +} +`; +const trivialFsMrtSrc300 = `#version 300 es +precision mediump float; +layout(location = 0) out vec4 o_color0; +layout(location = 1) out vec4 o_color1; +void main() +{ + o_color0 = vec4(1, 0, 0, 1); + o_color1 = vec4(0, 1, 0, 1); +} +`; + +function testExtFloatBlend(internalFormat) { + const shouldBlend = gl.getSupportedExtensions().indexOf('EXT_float_blend') != -1; + + const prog = wtu.setupProgram(gl, [trivialVsSrc, trivialFsSrc]); + gl.useProgram(prog); + + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.disable(gl.BLEND); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'Float32 draw target without blending'); + + gl.enable(gl.BLEND); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); +} + +function testExtFloatBlendMRTImpl(version, internalFormat, shaders, attachments, drawBuffers) { + const shouldBlend = gl.getSupportedExtensions().indexOf('EXT_float_blend') != -1; + + const prog = wtu.setupProgram(gl, shaders); + gl.useProgram(prog); + + const tex1 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + const tex2 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex2); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + const texF1 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texF1); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + const texF2 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texF2); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[0], gl.TEXTURE_2D, tex1, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[1], gl.TEXTURE_2D, tex2, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + drawBuffers(attachments); + + gl.enable(gl.BLEND); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'No Float32 color attachment'); + + if (version < 2) { + // EXT_draw_buffers require all color buffers having the same number of bitplanes + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[0], gl.TEXTURE_2D, texF1, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[1], gl.TEXTURE_2D, texF2, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + } else { + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[0], gl.TEXTURE_2D, texF1, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[1], gl.TEXTURE_2D, texF2, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[0], gl.TEXTURE_2D, tex1, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + drawBuffers([attachments[0]]); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'Float32 color attachment draw buffer is not enabled'); + + drawBuffers(attachments); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[1], gl.TEXTURE_2D, tex2, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'No Float32 color attachment'); + } + + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex1); + gl.deleteTexture(tex2); + gl.deleteTexture(texF1); + gl.deleteTexture(texF2); +} + +function testExtFloatBlendMRT(version, drawBuffersExt) { + if (version < 2) { + if (!drawBuffersExt) return; + testExtFloatBlendMRTImpl( + version, + gl.RGBA, + [trivialVsMrtSrc100, trivialFsMrtSrc100], + [drawBuffersExt.COLOR_ATTACHMENT0_WEBGL, drawBuffersExt.COLOR_ATTACHMENT1_WEBGL], + drawBuffersExt.drawBuffersWEBGL.bind(drawBuffersExt) + ); + } else { + testExtFloatBlendMRTImpl( + version, + gl.RGBA32F, + [trivialVsMrtSrc300, trivialFsMrtSrc300], + [gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1], + gl.drawBuffers.bind(gl) + ); + } +} + +function testExtFloatBlendNonFloat32TypeImpl(internalFormat, formats) { + const shouldBlend = gl.getSupportedExtensions().indexOf('EXT_float_blend') != -1; + + const prog = wtu.setupProgram(gl, [trivialVsSrc, trivialFsSrc]); + gl.useProgram(prog); + + gl.enable(gl.BLEND); + + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'UNSIGNED_BYTE should blend anyway'); + + for (let i = 0, len = formats.length; i < len; i++) { + gl.texImage2D(gl.TEXTURE_2D, 0, formats[i][0], 1, 1, 0, formats[i][1], formats[i][2], null); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'Any other float type which is not 32-bit-Float should blend anyway'); + } + + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); +} + +function testExtFloatBlendNonFloat32Type(version, oesTextureHalfFloat) { + if (version < 2) { + if (!oesTextureHalfFloat) return; + const formats = [ + [gl.RGBA, gl.RGBA, oesTextureHalfFloat.HALF_FLOAT_OES] + ]; + testExtFloatBlendNonFloat32TypeImpl(gl.RGBA, formats); + } else { + const formats = [ + [gl.RGBA16F, gl.RGBA, gl.HALF_FLOAT], + [gl.RGBA16F, gl.RGBA, gl.FLOAT], + [gl.RG16F, gl.RG, gl.FLOAT], + [gl.R16F, gl.RED, gl.FLOAT], + [gl.R11F_G11F_B10F, gl.RGB, gl.FLOAT] + ]; + testExtFloatBlendNonFloat32TypeImpl(gl.RGBA32F, formats); + } +} + +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ext-texture-filter-anisotropic.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-texture-filter-anisotropic.js new file mode 100644 index 0000000000..1e60a79717 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-texture-filter-anisotropic.js @@ -0,0 +1,169 @@ +"use strict"; +description("This test verifies the functionality of the EXT_texture_filter_anisotropic extension, if it is available."); + +debug(""); + +let wtu = WebGLTestUtils; +let canvas = document.getElementById("canvas"); +let gl = wtu.create3DContext(canvas, undefined, contextVersion); +let ext = null; +let sampler; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runHintTestDisabled(); + if (contextVersion >= 2) { + runSamplerTestDisabled(); + } + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "EXT_texture_filter_anisotropic"); + + if (!ext) { + testPassed("No EXT_texture_filter_anisotropic support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled EXT_texture_filter_anisotropic extension"); + + runSupportedTest(true); + runHintTestEnabled(); + if (contextVersion >= 2) { + runSamplerTestEnabled(); + } + } +} + +function runSupportedTest(extensionEnabled) { + if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "EXT_texture_filter_anisotropic") !== undefined) { + if (extensionEnabled) { + testPassed("EXT_texture_filter_anisotropic listed as supported and getExtension succeeded"); + } else { + testFailed("EXT_texture_filter_anisotropic listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("EXT_texture_filter_anisotropic not listed as supported but getExtension succeeded"); + } else { + testPassed("EXT_texture_filter_anisotropic not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runHintTestDisabled() { + debug("Testing MAX_TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + + const MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; + shouldBeNull(`gl.getParameter(${MAX_TEXTURE_MAX_ANISOTROPY_EXT})`); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "MAX_TEXTURE_MAX_ANISOTROPY_EXT should not be queryable if extension is disabled"); + + debug("Testing TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + const TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + shouldBeNull(`gl.getTexParameter(gl.TEXTURE_2D, ${TEXTURE_MAX_ANISOTROPY_EXT})`); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be queryable if extension is disabled"); + + gl.texParameterf(gl.TEXTURE_2D, TEXTURE_MAX_ANISOTROPY_EXT, 1); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be settable if extension is disabled"); + + gl.texParameteri(gl.TEXTURE_2D, TEXTURE_MAX_ANISOTROPY_EXT, 1); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be settable if extension is disabled"); + + gl.deleteTexture(texture); +} + +function runHintTestEnabled() { + debug("Testing MAX_TEXTURE_MAX_ANISOTROPY_EXT with extension enabled"); + + shouldBe("ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT", "0x84FF"); + + let max_anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "MAX_TEXTURE_MAX_ANISOTROPY_EXT query should succeed if extension is enabled"); + + if (max_anisotropy >= 2) { + testPassed("Minimum value of MAX_TEXTURE_MAX_ANISOTROPY_EXT is 2.0"); + } else { + testFailed("Minimum value of MAX_TEXTURE_MAX_ANISOTROPY_EXT is 2.0, returned values was: " + max_anisotropy); + } + + // TODO make a texture and verify initial value == 1 and setting to less than 1 is invalid value + + debug("Testing TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + shouldBe("ext.TEXTURE_MAX_ANISOTROPY_EXT", "0x84FE"); + + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + let queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "TEXTURE_MAX_ANISOTROPY_EXT query should succeed if extension is enabled"); + + if (queried_value == 1) { + testPassed("Initial value of TEXTURE_MAX_ANISOTROPY_EXT is 1.0"); + } else { + testFailed("Initial value of TEXTURE_MAX_ANISOTROPY_EXT should be 1.0, returned value was: " + queried_value); + } + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to < 1 should be an invalid value"); + + gl.texParameteri(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "texParameteri TEXTURE_MAX_ANISOTROPY_EXT set to < 1 should be an invalid value"); + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to >= 2 should succeed"); + + gl.texParameteri(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameteri TEXTURE_MAX_ANISOTROPY_EXT set to >= 2 should succeed"); + + queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + if (queried_value == max_anisotropy) { + testPassed("Set value of TEXTURE_MAX_ANISOTROPY_EXT matches expecation"); + } else { + testFailed("Set value of TEXTURE_MAX_ANISOTROPY_EXT should be: " + max_anisotropy + " , returned value was: " + queried_value); + } + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 1.5); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to 1.5 should succeed"); + + queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + if (queried_value == 1.5) { + testPassed("Set value of TEXTURE_MAX_ANISOTROPY_EXT matches expecation"); + } else { + testFailed("Set value of TEXTURE_MAX_ANISOTROPY_EXT should be: " + 1.5 + " , returned value was: " + queried_value); + } + + gl.deleteTexture(texture); +} + +function runSamplerTestDisabled() { + sampler = gl.createSampler(); + const TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; + gl.samplerParameterf(sampler, TEXTURE_MAX_ANISOTROPY_EXT, 1.0); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "setting TEXTURE_MAX_ANISOTROPY_EXT on sampler without extension enabled should fail"); + shouldBeNull(`gl.getSamplerParameter(sampler, ${TEXTURE_MAX_ANISOTROPY_EXT})`); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "querying TEXTURE_MAX_ANISOTROPY_EXT on sampler without extension enabled should fail"); + gl.deleteSampler(sampler); +} + +function runSamplerTestEnabled() { + let sampler = gl.createSampler(); + gl.samplerParameterf(sampler, ext.TEXTURE_MAX_ANISOTROPY_EXT, 1.5); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setting TEXTURE_MAX_ANISOTROPY_EXT on sampler should succeed"); + let queried_value = gl.getSamplerParameter(sampler, ext.TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "querying TEXTURE_MAX_ANISOTROPY_EXT on sampler should succeed"); + if (queried_value == 1.5) { + testPassed("Set value of TEXTURE_MAX_ANISOTROPY_EXT on sampler matches expecation"); + } else { + testFailed("Set value of TEXTURE_MAX_ANISOTROPY_EXT on sampler should be: " + 1.5 + " , returned value was: " + queried_value); + } + gl.deleteSampler(sampler); +} + +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-bindattriblocation-aliasing.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-bindattriblocation-aliasing.js new file mode 100644 index 0000000000..4c853fc4fa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-bindattriblocation-aliasing.js @@ -0,0 +1,44 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +"use strict"; + +var runBindAttribLocationAliasingTest = function(wtu, gl, glFragmentShader, vertexShaderTemplate) { + var typeInfo = [ + { type: 'float', asVec4: 'vec4(0.0, $(var), 0.0, 1.0)' }, + { type: 'vec2', asVec4: 'vec4($(var), 0.0, 1.0)' }, + { type: 'vec3', asVec4: 'vec4($(var), 1.0)' }, + { type: 'vec4', asVec4: '$(var)' }, + ]; + var maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + // Test all type combinations of a_1 and a_2. + typeInfo.forEach(function(typeInfo1) { + typeInfo.forEach(function(typeInfo2) { + debug('attribute_1: ' + typeInfo1.type + ' attribute_2: ' + typeInfo2.type); + var replaceParams = { + type_1: typeInfo1.type, + type_2: typeInfo2.type, + gl_Position_1: wtu.replaceParams(typeInfo1.asVec4, {var: 'a_1'}), + gl_Position_2: wtu.replaceParams(typeInfo2.asVec4, {var: 'a_2'}) + }; + var strVertexShader = wtu.replaceParams(vertexShaderTemplate, replaceParams); + var glVertexShader = wtu.loadShader(gl, strVertexShader, gl.VERTEX_SHADER); + assertMsg(glVertexShader != null, "Vertex shader compiled successfully."); + // Bind both a_1 and a_2 to the same position and verify the link fails. + // Do so for all valid positions available. + for (var l = 0; l < maxAttributes; l++) { + var glProgram = gl.createProgram(); + gl.bindAttribLocation(glProgram, l, 'a_1'); + gl.bindAttribLocation(glProgram, l, 'a_2'); + gl.attachShader(glProgram, glVertexShader); + gl.attachShader(glProgram, glFragmentShader); + gl.linkProgram(glProgram); + var linkStatus = gl.getProgramParameter(glProgram, gl.LINK_STATUS); + assertMsg(!linkStatus, "Link should fail when both attributes are aliased to location " + l); + } + }); + }); +};
\ No newline at end of file diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-enum-tests.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-enum-tests.js new file mode 100644 index 0000000000..32d451f7a2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-enum-tests.js @@ -0,0 +1,123 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +description("This test ensures various WebGL functions fail when passed invalid OpenGL ES enums."); + +debug(""); +debug("Canvas.getContext"); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("canvas", undefined, contextVersion); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + debug(""); + debug("Checking gl enums."); + + var buffer = new ArrayBuffer(2); + var buf = new Uint16Array(buffer); + var tex = gl.createTexture(); + var program = wtu.createProgram(gl, wtu.loadStandardVertexShader(gl), wtu.loadStandardFragmentShader(gl)); + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + var tests = [ + "gl.disable(desktopGL['CLIP_PLANE0'])", + "gl.disable(desktopGL['POINT_SPRITE'])", + "gl.getBufferParameter(gl.ARRAY_BUFFER, desktopGL['PIXEL_PACK_BUFFER'])", + "gl.hint(desktopGL['PERSPECTIVE_CORRECTION_HINT'], gl.FASTEST)", + "gl.isEnabled(desktopGL['CLIP_PLANE0'])", + "gl.isEnabled(desktopGL['POINT_SPRITE'])", + "gl.pixelStorei(desktopGL['PACK_SWAP_BYTES'], 1)", + "gl.getParameter(desktopGL['NUM_COMPRESSED_TEXTURE_FORMATS'])", + "gl.getParameter(desktopGL['EXTENSIONS'])", + "gl.getParameter(desktopGL['SHADER_COMPILER'])", + "gl.getParameter(desktopGL['SHADER_BINARY_FORMATS'])", + "gl.getParameter(desktopGL['NUM_SHADER_BINARY_FORMATS'])", + ]; + + if (contextVersion < 2) { + tests = tests.concat([ + "gl.blendEquation(desktopGL['MIN'])", + "gl.blendEquation(desktopGL['MAX'])", + "gl.blendEquationSeparate(desktopGL['MIN'], gl.FUNC_ADD)", + "gl.blendEquationSeparate(desktopGL['MAX'], gl.FUNC_ADD)", + "gl.blendEquationSeparate(gl.FUNC_ADD, desktopGL['MIN'])", + "gl.blendEquationSeparate(gl.FUNC_ADD, desktopGL['MAX'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['STREAM_READ'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['STREAM_COPY'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['STATIC_READ'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['STATIC_COPY'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['DYNAMIC_READ'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['DYNAMIC_COPY'])", + "gl.bindTexture(desktopGL['TEXTURE_2D_ARRAY'], tex)", + "gl.bindTexture(desktopGL['TEXTURE_3D'], tex)", + ]); + } else { + tests = tests.concat([ + "gl.bindTexture(desktopGL['TEXTURE_RECTANGLE_EXT'], tex)", + "gl.enable(desktopGL['PRIMITIVE_RESTART_FIXED_INDEX'])", + "gl.getActiveUniforms(program, [0], desktopGL['UNIFORM_NAME_LENGTH'])", + "gl.getProgramParameter(program, desktopGL['ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH'])", + "gl.getProgramParameter(program, desktopGL['TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH'])", + "gl.getProgramParameter(program, desktopGL['PROGRAM_BINARY_RETRIEVABLE_HINT'])", + "gl.getProgramParameter(program, desktopGL['PROGRAM_BINARY_LENGTH'])", + "gl.getParameter(program, desktopGL['NUM_PROGRAM_BINARY_FORMATS'])", + ]); + } + + for (var ii = 0; ii < tests.length; ++ii) { + TestEval(tests[ii]); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, tests[ii] + " should return INVALID_ENUM."); + } + + gl.bindTexture(gl.TEXTURE_2D, tex); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + tests = [ + "gl.getTexParameter(gl.TEXTURE_2D, desktopGL['GENERATE_MIPMAP'])", + "gl.texParameteri(gl.TEXTURE_2D, desktopGL['GENERATE_MIPMAP'], 1)", + "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, desktopGL['CLAMP_TO_BORDER'])", + ]; + + if (contextVersion < 2) { + tests = tests.concat([ + "gl.texParameteri(desktopGL['TEXTURE_2D_ARRAY'], gl.TEXTURE_MAG_FILTER, gl.NEAREST)", + "gl.texParameteri(desktopGL['TEXTURE_3D'], gl.TEXTURE_MAG_FILTER, gl.NEAREST)", + ]); + } else { + tests = tests.concat([ + "gl.texParameteri(desktopGL['TEXTURE_2D'], desktopGL['TEXTURE_SWIZZLE_R_EXT'], gl.RED)", + "gl.texParameteri(desktopGL['TEXTURE_2D'], desktopGL['TEXTURE_SWIZZLE_G_EXT'], gl.RED)", + "gl.texParameteri(desktopGL['TEXTURE_2D'], desktopGL['TEXTURE_SWIZZLE_B_EXT'], gl.RED)", + "gl.texParameteri(desktopGL['TEXTURE_2D'], desktopGL['TEXTURE_SWIZZLE_A_EXT'], gl.RED)", + "gl.texParameteri(desktopGL['TEXTURE_2D'], gl.TEXTURE_WRAP_R, desktopGL['CLAMP_TO_BORDER'])", + ]); + } + + for (var ii = 0; ii < tests.length; ++ii) { + TestEval(tests[ii]); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, tests[ii] + " should return INVALID_ENUM."); + } + if (contextVersion >= 2) { + var uniformBlockProgram = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(uniformBlockProgram); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.LINK_STATUS)', 'true'); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + gl.getActiveUniformBlockParameter(uniformBlockProgram, 0, desktopGL['UNIFORM_BLOCK_NAME_LENGTH']); + shouldBe('gl.getError()', 'gl.INVALID_ENUM'); + } +} + +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-get-tex-parameter.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-get-tex-parameter.js new file mode 100644 index 0000000000..8844bbf544 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-get-tex-parameter.js @@ -0,0 +1,183 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +description(); +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("example", undefined, contextVersion); + +// NOTE: We explicitly do this in a funky order +// to hopefully find subtle bugs. + +var targets = [ + 'TEXTURE_2D', + 'TEXTURE_2D', + 'TEXTURE_CUBE_MAP', + 'TEXTURE_CUBE_MAP' +]; + +if (contextVersion > 1) { + targets = targets.concat([ + 'TEXTURE_2D_ARRAY', + 'TEXTURE_2D_ARRAY', + 'TEXTURE_3D', + 'TEXTURE_3D' + ]); +} + +// Create textures on different active textures. +for (var ii = 0; ii < targets.length; ++ii) { + var target = targets[ii]; + var tex = gl.createTexture(); + gl.activeTexture(gl.TEXTURE0 + ii); + gl.bindTexture(gl[target], tex); +} + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + +var states = [ + { state: 'TEXTURE_WRAP_S', default: 'REPEAT', value1: 'CLAMP_TO_EDGE', value2: 'REPEAT' }, + { state: 'TEXTURE_WRAP_T', default: 'REPEAT', value1: 'MIRRORED_REPEAT', value2: 'REPEAT' }, + { state: 'TEXTURE_MAG_FILTER', default: 'LINEAR', value1: 'NEAREST', value2: 'LINEAR' }, + { state: 'TEXTURE_MIN_FILTER', default: 'NEAREST_MIPMAP_LINEAR', value1: 'LINEAR_MIPMAP_LINEAR', value2: 'NEAREST' } +]; + +if (contextVersion > 1) { + states = states.concat([ + { state: 'TEXTURE_WRAP_R', default: 'REPEAT', value1: 'CLAMP_TO_EDGE', value2: 'MIRRORED_REPEAT' }, + { state: 'TEXTURE_COMPARE_FUNC', default: 'LEQUAL', value1: 'GREATER', value2: 'LESS' }, + { state: 'TEXTURE_COMPARE_MODE', default: 'NONE', value1: 'COMPARE_REF_TO_TEXTURE', value2: 'NONE' }, + { state: 'TEXTURE_BASE_LEVEL', default: 0, value1: 100, value2: 99 }, + { state: 'TEXTURE_MAX_LEVEL', default: 1000, value1: 800, value2: 300 }, + { state: 'TEXTURE_MIN_LOD', default: -1000.0, value1: -500.0, value2: -999.0 }, + { state: 'TEXTURE_MAX_LOD', default: 1000.0, value1: 500.0, value2: 999.0 }, + // Note: For TEXTURE_IMMUTABLE_LEVELS and TEXTURE_IMMUTABLE_FORMAT, + // these two pname are used by getTexParameter API only, not available in texParameter[fi] in specifications. + // Thus, these two states store default value only. + { state: 'TEXTURE_IMMUTABLE_LEVELS', default: 0, }, + { state: 'TEXTURE_IMMUTABLE_FORMAT', default: false, } + ]); +} + +function getStateInfoValue(stateInfo, item, method) { + switch (stateInfo.state) { + case 'TEXTURE_WRAP_R': + case 'TEXTURE_WRAP_S': + case 'TEXTURE_WRAP_T': + case 'TEXTURE_MAG_FILTER': + case 'TEXTURE_MIN_FILTER': + case 'TEXTURE_COMPARE_FUNC': + case 'TEXTURE_COMPARE_MODE': + if (method === 'Get') { + return 'gl["' + stateInfo[item] + '"]'; + } else if (method === 'Set') { + return gl[stateInfo[item]]; + } + break; + case 'TEXTURE_BASE_LEVEL': + case 'TEXTURE_MAX_LEVEL': + case 'TEXTURE_MIN_LOD': + case 'TEXTURE_MAX_LOD': + if (method === 'Get') { + return '' + stateInfo[item]; + } else if (method === 'Set') { + return stateInfo[item]; + } + break; + case 'TEXTURE_IMMUTABLE_LEVELS': + case 'TEXTURE_IMMUTABLE_FORMAT': + // Return default value only. + return '' + stateInfo.default; + default: + wtu.error("Not reached!"); + return null; + break; + } +} + +function applyStates(fn) { + for (var ss = 0; ss < states.length; ++ss) { + var stateInfo = states[ss]; + for (var ii = 0; ii < targets.length; ++ii) { + var target = targets[ii]; + gl.activeTexture(gl.TEXTURE0 + ii); + fn(target, stateInfo); + } + } +} + +// test the default state. +applyStates(function(target, stateInfo) { + var a = 'gl.getTexParameter(gl["' + target + '"], gl["' + stateInfo.state + '"])'; + var b = getStateInfoValue(stateInfo, 'default', 'Get'); + shouldBe(a, b); +}); + +// test new state +applyStates(function(target, stateInfo) { + switch (stateInfo.state) { + case 'TEXTURE_IMMUTABLE_FORMAT': + case 'TEXTURE_IMMUTABLE_LEVELS': + // Skip these two pname for texParameterf[fi]. + break; + case 'TEXTURE_MIN_LOD': + case 'TEXTURE_MAX_LOD': + gl.texParameterf(gl[target], gl[stateInfo.state], getStateInfoValue(stateInfo, 'value1', 'Set')); + break; + default: + gl.texParameteri(gl[target], gl[stateInfo.state], getStateInfoValue(stateInfo, 'value1', 'Set')); + break; + } +}); + +applyStates(function(target, stateInfo) { + var a = 'gl.getTexParameter(gl["' + target + '"], gl["' + stateInfo.state + '"])'; + var b = getStateInfoValue(stateInfo, 'value1', 'Get'); + shouldBe(a, b); +}); + +// test different states on each target. +function getItem(count) { + return (count % 2) ? 'value2' : 'value1'; +} + +applyStates(function() { + var count = 0; + return function(target, stateInfo) { + switch (stateInfo.state) { + case 'TEXTURE_IMMUTABLE_FORMAT': + case 'TEXTURE_IMMUTABLE_LEVELS': + // Skip these two pname for texParameterf[fi]. + break; + case 'TEXTURE_MIN_LOD': + case 'TEXTURE_MAX_LOD': + gl.texParameterf(gl[target], gl[stateInfo.state], getStateInfoValue(stateInfo, getItem(count), 'Set')); + break; + default: + gl.texParameteri(gl[target], gl[stateInfo.state], getStateInfoValue(stateInfo, getItem(count), 'Set')); + break; + } + ++count; + } +}()); + +applyStates(function() { + var count = 0; + return function(target, stateInfo) { + var a = 'gl.getTexParameter(gl["' + target + '"], gl["' + stateInfo.state + '"])'; + var b = getStateInfoValue(stateInfo, getItem(count), 'Get'); + shouldBe(a, b); + ++count; + }; +}()); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js new file mode 100644 index 0000000000..bd97434b77 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js @@ -0,0 +1,1090 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +const wtu = WebGLTestUtils; +description("Test of get calls against GL objects like getBufferParameter, etc."); + +let gl = wtu.create3DContext(undefined, undefined, contextVersion); + +async function testInvalidArgument(funcName, argumentName, validArgumentArray, func) { + let validArguments = {}; + for (let ii = 0; ii < validArgumentArray.length; ++ii) { + validArguments[validArgumentArray[ii]] = true; + } + let success = true; + const MAX = 0x10000; + const STEP = Math.ceil(MAX / 10); + for (let ii = 0; ii < MAX; ii += 1) { + if (ii && ii % STEP == 0) { + debug(`(${ii} of ${MAX}: ${(ii/MAX*100).toFixed(1)}%)`); + await wtu.dispatchPromise(); // Spin the event loop. + } + if (!validArguments[ii]) { + let result = func(ii); + if (result !== null) { + success = false; + testFailed(funcName + " returned " + result + " instead of null for invalid " + argumentName + " enum: " + wtu.glEnumToString(gl, ii)); + break; + } + let err = gl.getError(); + if (err != gl.INVALID_ENUM) { + success = false; + testFailed(funcName + " did not generate INVALID_ENUM for invalid " + argumentName + " enum: " + wtu.glEnumToString(gl, ii)); + break; + } + } + } + if (success) { + testPassed(funcName + " correctly handled invalid " + argumentName + " enums"); + } +} + +(async () => { + debug(""); + debug("test getBufferParameter"); + // Test getBufferParameter + let bufferTypes = [gl.ARRAY_BUFFER, gl.ELEMENT_ARRAY_BUFFER]; + if (contextVersion > 1) { + bufferTypes = bufferTypes.concat([gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, gl.PIXEL_PACK_BUFFER, gl.PIXEL_UNPACK_BUFFER, gl.TRANSFORM_FEEDBACK_BUFFER, gl.UNIFORM_BUFFER]); + } + for (let bb = 0; bb < bufferTypes.length; ++bb) { + let bufferType = bufferTypes[bb]; + let buffer = gl.createBuffer(); + gl.bindBuffer(bufferType, buffer); + gl.bufferData(bufferType, 16, gl.DYNAMIC_DRAW); + let expression1 = "gl.getBufferParameter(" + bufferType + ", gl.BUFFER_SIZE)"; + let expression2 = "gl.getBufferParameter(" + bufferType + ", gl.BUFFER_USAGE)"; + shouldBe(expression1, '16'); + shouldBe(expression2, 'gl.DYNAMIC_DRAW'); + await testInvalidArgument("getBufferParameter", "parameter", [gl.BUFFER_SIZE, gl.BUFFER_USAGE], function(bufferType) { + return function(parameter) { + return gl.getBufferParameter(bufferType, parameter); + }; + }(bufferType)); + gl.bindBuffer(bufferType, null); + } + await testInvalidArgument( + "getBufferParameter", + "target", + bufferTypes, + function(target) { + return gl.getBufferParameter(target, gl.BUFFER_SIZE); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + let testCases = [ + { contextStencil: true}, + { contextStencil: false} + ]; + + for (let run = 0; run < testCases.length; ++run) { + debug(""); + debug("Test getFramebufferAttachmentParameter with stencil " + testCases[run].contextStencil); + + if (testCases[run].contextStencil) { + gl = wtu.create3DContext(null, {stencil: true}, contextVersion); + } else { + gl = wtu.create3DContext(null, {stencil: false}, contextVersion); + } + + window.texture = gl.createTexture(); + window.anotherTexture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, + new Uint8Array([ + 0, 0, 0, 255, + 255, 255, 255, 255, + 255, 255, 255, 255, + 0, 0, 0, 255])); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.bindTexture(gl.TEXTURE_2D, null); + window.framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + let colorAttachmentsNum = 1; + if (contextVersion > 1) { + gl.bindTexture(gl.TEXTURE_2D, anotherTexture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, + new Uint8Array([ + 0, 0, 0, 255, + 255, 255, 255, 255, + 255, 255, 255, 255, + 0, 0, 0, 255])); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.bindTexture(gl.TEXTURE_2D, null); + colorAttachmentsNum = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + colorAttachmentsNum - 1, gl.TEXTURE_2D, anotherTexture, 0); + } + window.renderbuffer = gl.createRenderbuffer(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + if (contextVersion == 1) + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 2, 2); + else + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, 2, 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer); + if (contextVersion > 1) + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + // The for loop tests two color attachments for WebGL 2: the first one (gl.COLOR_ATTACHMENT0) + // and the last one (gl.COLOR_ATTACHMENT0 + gl.MAX_COLOR_ATTACHMENTS - 1). + for (let ii = 0; ii < colorAttachmentsNum; ii += (colorAttachmentsNum > 1 ? colorAttachmentsNum - 1 : 1)) { + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.TEXTURE'); + if (ii == 0) + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'texture'); + else + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'anotherTexture'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL)', '0'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE)', '0'); + if (contextVersion > 1) { + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_RED_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER)', '0'); + } + } + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.RENDERBUFFER'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'renderbuffer'); + if (contextVersion > 1) { + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.RENDERBUFFER'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'renderbuffer'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.RENDERBUFFER'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'renderbuffer'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + } + let validParametersForFBAttachment = + [ gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, + gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE + ]; + if (contextVersion > 1) { + validParametersForFBAttachment = validParametersForFBAttachment.concat([ + gl.FRAMEBUFFER_ATTACHMENT_RED_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, + gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, + gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER + ]); + } + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "parameter", + validParametersForFBAttachment, + function(parameter) { + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT, parameter); + } + ); + let validTargetsForFBAttachment = [gl.FRAMEBUFFER]; + if (contextVersion > 1) { + validTargetsForFBAttachment = validTargetsForFBAttachment.concat([gl.READ_FRAMEBUFFER, gl.DRAW_FRAMEBUFFER]); + } + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "target", + validTargetsForFBAttachment, + function(target) { + return gl.getFramebufferAttachmentParameter(target, gl.COLOR_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + } + ); + let validAttachmentsForFBAttachment = new Array( + gl.COLOR_ATTACHMENT0, + gl.DEPTH_ATTACHMENT, + gl.STENCIL_ATTACHMENT, + gl.DEPTH_STENCIL_ATTACHMENT + ); + if (contextVersion > 1) { + for (let ii = 1; ii < gl.getParameter(gl.MAX_COLOR_ATTACHMENTS); ++ii) { + validAttachmentsForFBAttachment[validAttachmentsForFBAttachment.length] = gl.COLOR_ATTACHMENT0 + ii; + } + } + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "attachment", + validAttachmentsForFBAttachment, + function(attachment) { + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, attachment, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + } + ); + if (contextVersion > 1) { + // test default framebuffer + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.FRAMEBUFFER_DEFAULT'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.FRAMEBUFFER_DEFAULT'); + if (testCases[run].contextStencil) + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.FRAMEBUFFER_DEFAULT'); + else + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_RED_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH, gl.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + if (testCases[run].contextStencil) { + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + } else { + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE)'); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + } + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "parameter", + validParametersForFBAttachment, + function(parameter) { + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, parameter); + } + ); + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "target", + validTargetsForFBAttachment, + function(target) { + return gl.getFramebufferAttachmentParameter(target, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + } + ); + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "attachment", + [ gl.BACK, + gl.DEPTH, + gl.STENCIL + ], + function(attachment) { + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, attachment, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + } + ); + } + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("test getAttachedShaders"); + window.standardVert = wtu.loadStandardVertexShader(gl); + window.standardFrag = wtu.loadStandardFragmentShader(gl); + window.standardProgram = gl.createProgram(); + gl.attachShader(standardProgram, standardVert); + gl.attachShader(standardProgram, standardFrag); + gl.linkProgram(standardProgram); + window.shaders = gl.getAttachedShaders(standardProgram); + shouldBe('shaders.length', '2'); + shouldBeTrue('shaders[0] == standardVert && shaders[1] == standardFrag || shaders[1] == standardVert && shaders[0] == standardFrag'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldThrow('gl.getAttachedShaders(null)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldThrow('gl.getAttachedShaders(standardVert)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getProgramParameter"); + shouldBe('gl.getProgramParameter(standardProgram, gl.DELETE_STATUS)', 'false'); + shouldBe('gl.getProgramParameter(standardProgram, gl.LINK_STATUS)', 'true'); + shouldBe('typeof gl.getProgramParameter(standardProgram, gl.VALIDATE_STATUS)', '"boolean"'); + shouldBe('gl.getProgramParameter(standardProgram, gl.ATTACHED_SHADERS)', '2'); + shouldBe('gl.getProgramParameter(standardProgram, gl.ACTIVE_ATTRIBUTES)', '2'); + shouldBe('gl.getProgramParameter(standardProgram, gl.ACTIVE_UNIFORMS)', '1'); + if (contextVersion > 1) { + let buffer = gl.createBuffer(); + gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, buffer); + gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 1024, gl.DYNAMIC_DRAW); + window.uniformBlockProgram = wtu.loadUniformBlockProgram(gl); + let transformFeedbackVars = ["normal", "ecPosition"]; + gl.transformFeedbackVaryings(uniformBlockProgram, transformFeedbackVars, gl.INTERLEAVED_ATTRIBS); + gl.linkProgram(uniformBlockProgram); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.LINK_STATUS)', 'true'); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.ACTIVE_UNIFORM_BLOCKS)', '1'); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.TRANSFORM_FEEDBACK_VARYINGS)', '2'); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.TRANSFORM_FEEDBACK_BUFFER_MODE)', 'gl.INTERLEAVED_ATTRIBS'); + } + window.program = standardProgram; + let validArrayForProgramParameter = [ + gl.DELETE_STATUS, + gl.LINK_STATUS, + gl.VALIDATE_STATUS, + gl.ATTACHED_SHADERS, + gl.ACTIVE_ATTRIBUTES, + gl.ACTIVE_UNIFORMS + ]; + if (contextVersion > 1) { + validArrayForProgramParameter = validArrayForProgramParameter.concat([ + gl.ACTIVE_UNIFORM_BLOCKS, + gl.TRANSFORM_FEEDBACK_VARYINGS, + gl.TRANSFORM_FEEDBACK_BUFFER_MODE + ]); + program = uniformBlockProgram; + } + await testInvalidArgument( + "getProgramParameter", + "parameter", + validArrayForProgramParameter, + function(parameter) { + return gl.getProgramParameter(program, parameter); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getRenderbufferParameter"); + shouldBe('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)', '2'); + shouldBe('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)', '2'); + // Note: we can't test the actual value of the internal format since + // the implementation is allowed to change it. + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_INTERNAL_FORMAT)'); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_DEPTH_SIZE)'); + let colorbuffer = gl.createRenderbuffer(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 2, 2); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_RED_SIZE)'); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_GREEN_SIZE)'); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_BLUE_SIZE)'); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_ALPHA_SIZE)'); + if (contextVersion > 1) { + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA4, 2, 2); + shouldBe('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_SAMPLES)', '4'); + } + let validArrayForRenderbuffer = new Array( + gl.RENDERBUFFER_WIDTH, + gl.RENDERBUFFER_HEIGHT, + gl.RENDERBUFFER_INTERNAL_FORMAT, + gl.RENDERBUFFER_RED_SIZE, + gl.RENDERBUFFER_GREEN_SIZE, + gl.RENDERBUFFER_BLUE_SIZE, + gl.RENDERBUFFER_ALPHA_SIZE, + gl.RENDERBUFFER_DEPTH_SIZE, + gl.RENDERBUFFER_STENCIL_SIZE + ); + if (contextVersion > 1) { + validArrayForRenderbuffer[validArrayForRenderbuffer.length] = gl.RENDERBUFFER_SAMPLES; + } + await testInvalidArgument( + "getRenderbufferParameter", + "parameter", + validArrayForRenderbuffer, + function(parameter) { + return gl.getRenderbufferParameter(gl.RENDERBUFFER, parameter); + }); + await testInvalidArgument( + "getRenderbufferParameter", + "target", + [ gl.RENDERBUFFER ], + function(target) { + return gl.getRenderbufferParameter(target, gl.RENDERBUFFER_WIDTH); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getShaderParameter"); + shouldBe('gl.getShaderParameter(standardVert, gl.SHADER_TYPE)', 'gl.VERTEX_SHADER'); + shouldBe('gl.getShaderParameter(standardVert, gl.DELETE_STATUS)', 'false'); + shouldBe('gl.getShaderParameter(standardVert, gl.COMPILE_STATUS)', 'true'); + await testInvalidArgument( + "getShaderParameter", + "parameter", + [ gl.DELETE_STATUS, + gl.COMPILE_STATUS, + gl.SHADER_TYPE + ], + function(parameter) { + return gl.getShaderParameter(standardVert, parameter); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getTexParameter"); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER)', 'gl.NEAREST'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER)', 'gl.NEAREST'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)', 'gl.CLAMP_TO_EDGE'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T)', 'gl.CLAMP_TO_EDGE'); + if (contextVersion > 1) { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC, gl.LEQUAL); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 10); + gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAX_LOD, 10); + gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_LOD, 0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL)', '0'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC)', 'gl.LEQUAL'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE)', 'gl.COMPARE_REF_TO_TEXTURE'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL)', '10'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAX_LOD)', '10'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_LOD)', '0'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R)', 'gl.CLAMP_TO_EDGE'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_IMMUTABLE_FORMAT)', 'false'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_IMMUTABLE_LEVELS)', '0'); + } + let validParametersForTexture = [ + gl.TEXTURE_MAG_FILTER, + gl.TEXTURE_MIN_FILTER, + gl.TEXTURE_WRAP_S, + gl.TEXTURE_WRAP_T, + ]; + if (contextVersion > 1) { + validParametersForTexture = validParametersForTexture.concat([ + gl.TEXTURE_BASE_LEVEL, + gl.TEXTURE_COMPARE_FUNC, + gl.TEXTURE_COMPARE_MODE, + gl.TEXTURE_MAX_LEVEL, + gl.TEXTURE_MAX_LOD, + gl.TEXTURE_MIN_LOD, + gl.TEXTURE_WRAP_R, + gl.TEXTURE_IMMUTABLE_FORMAT, + gl.TEXTURE_IMMUTABLE_LEVELS, + ]); + } + await testInvalidArgument( + "getTexParameter", + "parameter", + validParametersForTexture, + function(parameter) { + return gl.getTexParameter(gl.TEXTURE_2D, parameter); + } + ); + let validTargetsForTexture = [ gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP]; + if (contextVersion > 1) { + validTargetsForTexture = validTargetsForTexture.concat([ gl.TEXTURE_3D, gl.TEXTURE_2D_ARRAY]); + } + await testInvalidArgument( + "getTexParameter", + "target", + validTargetsForTexture, + function(target) { + return gl.getTexParameter(target, gl.TEXTURE_MAG_FILTER); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getUniform with all variants of data types"); + debug("Boolean uniform variables"); + window.boolProgram = wtu.loadProgramFromFile(gl, "../../resources/boolUniformShader.vert", "../../resources/noopUniformShader.frag"); + shouldBe('gl.getProgramParameter(boolProgram, gl.LINK_STATUS)', 'true'); + window.bvalLoc = gl.getUniformLocation(boolProgram, "bval"); + window.bval2Loc = gl.getUniformLocation(boolProgram, "bval2"); + window.bval3Loc = gl.getUniformLocation(boolProgram, "bval3"); + window.bval4Loc = gl.getUniformLocation(boolProgram, "bval4"); + gl.useProgram(boolProgram); + gl.uniform1i(bvalLoc, 1); + gl.uniform2i(bval2Loc, 1, 0); + gl.uniform3i(bval3Loc, 1, 0, 1); + gl.uniform4i(bval4Loc, 1, 0, 1, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(boolProgram, bvalLoc)', 'true'); + shouldBe('gl.getUniform(boolProgram, bval2Loc)', '[true, false]'); + shouldBe('gl.getUniform(boolProgram, bval3Loc)', '[true, false, true]'); + shouldBe('gl.getUniform(boolProgram, bval4Loc)', '[true, false, true, false]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Integer uniform variables"); + window.intProgram = wtu.loadProgramFromFile(gl, "../../resources/intUniformShader.vert", "../../resources/noopUniformShader.frag"); + shouldBe('gl.getProgramParameter(intProgram, gl.LINK_STATUS)', 'true'); + window.ivalLoc = gl.getUniformLocation(intProgram, "ival"); + window.ival2Loc = gl.getUniformLocation(intProgram, "ival2"); + window.ival3Loc = gl.getUniformLocation(intProgram, "ival3"); + window.ival4Loc = gl.getUniformLocation(intProgram, "ival4"); + gl.useProgram(intProgram); + gl.uniform1i(ivalLoc, 1); + gl.uniform2i(ival2Loc, 2, 3); + gl.uniform3i(ival3Loc, 4, 5, 6); + gl.uniform4i(ival4Loc, 7, 8, 9, 10); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(intProgram, ivalLoc)', '1'); + shouldBe('gl.getUniform(intProgram, ival2Loc)', '[2, 3]'); + shouldBe('gl.getUniform(intProgram, ival3Loc)', '[4, 5, 6]'); + shouldBe('gl.getUniform(intProgram, ival4Loc)', '[7, 8, 9, 10]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Float uniform variables"); + window.floatProgram = wtu.loadProgramFromFile(gl, "../../resources/floatUniformShader.vert", "../../resources/noopUniformShader.frag"); + shouldBe('gl.getProgramParameter(floatProgram, gl.LINK_STATUS)', 'true'); + window.fvalLoc = gl.getUniformLocation(floatProgram, "fval"); + window.fval2Loc = gl.getUniformLocation(floatProgram, "fval2"); + window.fval3Loc = gl.getUniformLocation(floatProgram, "fval3"); + window.fval4Loc = gl.getUniformLocation(floatProgram, "fval4"); + gl.useProgram(floatProgram); + gl.uniform1f(fvalLoc, 11); + gl.uniform2f(fval2Loc, 12, 13); + gl.uniform3f(fval3Loc, 14, 15, 16); + gl.uniform4f(fval4Loc, 17, 18, 19, 20); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(floatProgram, fvalLoc)', '11'); + shouldBe('gl.getUniform(floatProgram, fval2Loc)', '[12, 13]'); + shouldBe('gl.getUniform(floatProgram, fval3Loc)', '[14, 15, 16]'); + shouldBe('gl.getUniform(floatProgram, fval4Loc)', '[17, 18, 19, 20]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Sampler uniform variables"); + window.samplerProgram = wtu.loadProgramFromFile(gl, "../../resources/noopUniformShader.vert", "../../resources/samplerUniformShader.frag"); + shouldBe('gl.getProgramParameter(samplerProgram, gl.LINK_STATUS)', 'true'); + window.s2DValLoc = gl.getUniformLocation(samplerProgram, "s2D"); + window.sCubeValLoc = gl.getUniformLocation(samplerProgram, "sCube"); + gl.useProgram(samplerProgram); + gl.uniform1i(s2DValLoc, 0); + gl.uniform1i(sCubeValLoc, 1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(samplerProgram, s2DValLoc)', '0'); + shouldBe('gl.getUniform(samplerProgram, sCubeValLoc)', '1'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Matrix uniform variables"); + window.matProgram = wtu.loadProgramFromFile(gl, "../../resources/matUniformShader.vert", "../../resources/noopUniformShader.frag"); + shouldBe('gl.getProgramParameter(matProgram, gl.LINK_STATUS)', 'true'); + window.mval2Loc = gl.getUniformLocation(matProgram, "mval2"); + window.mval3Loc = gl.getUniformLocation(matProgram, "mval3"); + window.mval4Loc = gl.getUniformLocation(matProgram, "mval4"); + gl.useProgram(matProgram); + gl.uniformMatrix2fv(mval2Loc, false, [1, 2, 3, 4]); + gl.uniformMatrix3fv(mval3Loc, false, [5, 6, 7, 8, 9, 10, 11, 12, 13]); + gl.uniformMatrix4fv(mval4Loc, false, [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(matProgram, mval2Loc)', '[1, 2, 3, 4]'); + shouldBe('gl.getUniform(matProgram, mval3Loc)', '[5, 6, 7, 8, 9, 10, 11, 12, 13]'); + shouldBe('gl.getUniform(matProgram, mval4Loc)', '[14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + if (contextVersion > 1) { + debug("Unsigned Integer uniform variables"); + window.uintProgram = wtu.loadProgramFromFile(gl, "../../resources/uintUniformShader.vert", "../../resources/noopUniformShaderES3.frag"); + shouldBe('gl.getProgramParameter(uintProgram, gl.LINK_STATUS)', 'true'); + window.uvalLoc = gl.getUniformLocation(uintProgram, "uval"); + window.uval2Loc = gl.getUniformLocation(uintProgram, "uval2"); + window.uval3Loc = gl.getUniformLocation(uintProgram, "uval3"); + window.uval4Loc = gl.getUniformLocation(uintProgram, "uval4"); + gl.useProgram(uintProgram); + gl.uniform1ui(uvalLoc, 1); + gl.uniform2ui(uval2Loc, 2, 3); + gl.uniform3ui(uval3Loc, 4, 5, 6); + gl.uniform4ui(uval4Loc, 7, 8, 9, 10); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(uintProgram, uvalLoc)', '1'); + shouldBe('gl.getUniform(uintProgram, uval2Loc)', '[2, 3]'); + shouldBe('gl.getUniform(uintProgram, uval3Loc)', '[4, 5, 6]'); + shouldBe('gl.getUniform(uintProgram, uval4Loc)', '[7, 8, 9, 10]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Matrix uniform variables for WebGL 2"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + window.matForWebGL2Program = wtu.loadProgramFromFile(gl, "../../resources/matForWebGL2UniformShader.vert", "../../resources/noopUniformShaderES3.frag"); + shouldBe('gl.getProgramParameter(matForWebGL2Program, gl.LINK_STATUS)', 'true'); + window.mval2x3Loc = gl.getUniformLocation(matForWebGL2Program, "mval2x3"); + window.mval2x4Loc = gl.getUniformLocation(matForWebGL2Program, "mval2x4"); + window.mval3x2Loc = gl.getUniformLocation(matForWebGL2Program, "mval3x2"); + window.mval3x4Loc = gl.getUniformLocation(matForWebGL2Program, "mval3x4"); + window.mval4x2Loc = gl.getUniformLocation(matForWebGL2Program, "mval4x2"); + window.mval4x3Loc = gl.getUniformLocation(matForWebGL2Program, "mval4x3"); + gl.useProgram(matForWebGL2Program); + gl.uniformMatrix2x3fv(mval2x3Loc, false, [1, 2, 3, 4, 5, 6]); + gl.uniformMatrix2x4fv(mval2x4Loc, false, [7, 8, 9, 10, 11, 12, 13, 14]); + gl.uniformMatrix3x2fv(mval3x2Loc, false, [15, 16, 17, 18, 19, 20]); + gl.uniformMatrix3x4fv(mval3x4Loc, false, [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]); + gl.uniformMatrix4x2fv(mval4x2Loc, false, [33, 34, 35, 36, 37, 38, 39, 40]); + gl.uniformMatrix4x3fv(mval4x3Loc, false, [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(matForWebGL2Program, mval2x3Loc)', '[1, 2, 3, 4, 5, 6]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval2x4Loc)', '[7, 8, 9, 10, 11, 12, 13, 14]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval3x2Loc)', '[15, 16, 17, 18, 19, 20]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval3x4Loc)', '[21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval4x2Loc)', '[33, 34, 35, 36, 37, 38, 39, 40]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval4x3Loc)', '[41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Sampler uniform variables for WebGL2"); + window.samplerForWebGL2Program = wtu.loadProgramFromFile(gl, "../../resources/noopUniformShaderES3.vert", "../../resources/samplerForWebGL2UniformShader.frag"); + shouldBe('gl.getProgramParameter(samplerForWebGL2Program, gl.LINK_STATUS)', 'true'); + window.s3DValLoc = gl.getUniformLocation(samplerForWebGL2Program, "s3D"); + window.s2DArrayValLoc = gl.getUniformLocation(samplerForWebGL2Program, "s2DArray"); + gl.useProgram(samplerForWebGL2Program); + gl.uniform1i(s3DValLoc, 0); + gl.uniform1i(s2DArrayValLoc, 1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(samplerForWebGL2Program, s3DValLoc)', '0'); + shouldBe('gl.getUniform(samplerForWebGL2Program, s2DArrayValLoc)', '1'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + } + + debug(""); + debug("test getVertexAttrib"); + let array = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + window.buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW); + // Vertex attribute 0 is special in that it has no current state, so + // fetching GL_CURRENT_VERTEX_ATTRIB generates an error. Use attribute + // 1 for these tests instead. + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)', 'buffer'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_ENABLED)', 'true'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_SIZE)', '4'); + // Stride MUST be the value the user put in. + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_STRIDE)', '0'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_TYPE)', 'gl.FLOAT'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED)', 'false'); + if (contextVersion > 1) { + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_DIVISOR)', '0'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_INTEGER)', 'false'); + gl.vertexAttribDivisor(1, 2); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_DIVISOR)', '2'); + } + gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 36, 12); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_STRIDE)', '36'); + shouldBe('gl.getVertexAttribOffset(1, gl.VERTEX_ATTRIB_ARRAY_POINTER)', '12'); + gl.disableVertexAttribArray(1); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_ENABLED)', 'false'); + gl.vertexAttrib4f(1, 5, 6, 7, 8); + shouldBe('gl.getVertexAttrib(1, gl.CURRENT_VERTEX_ATTRIB)', '[5, 6, 7, 8]'); + if (contextVersion > 1) { + let intArray = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + gl.bufferData(gl.ARRAY_BUFFER, intArray, gl.DYNAMIC_DRAW); + gl.enableVertexAttribArray(1); + // feed fixed-point data to buffer + gl.vertexAttribIPointer(1, 4, gl.INT, false, 0, 0); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_TYPE)', 'gl.INT'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_INTEGER)', 'true'); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + let validArrayForVertexAttrib = new Array( + gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, + gl.VERTEX_ATTRIB_ARRAY_ENABLED, + gl.VERTEX_ATTRIB_ARRAY_SIZE, + gl.VERTEX_ATTRIB_ARRAY_STRIDE, + gl.VERTEX_ATTRIB_ARRAY_TYPE, + gl.VERTEX_ATTRIB_ARRAY_NORMALIZED, + gl.CURRENT_VERTEX_ATTRIB + ); + if (contextVersion > 1) { + validArrayForVertexAttrib[validArrayForVertexAttrib.length] = gl.VERTEX_ATTRIB_ARRAY_DIVISOR; + validArrayForVertexAttrib[validArrayForVertexAttrib.length] = gl.VERTEX_ATTRIB_ARRAY_INTEGER; + } + await testInvalidArgument( + "getVertexAttrib", + "parameter", + validArrayForVertexAttrib, + function(parameter) { + return gl.getVertexAttrib(1, parameter); + } + ); + let numVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, 'gl.getVertexAttrib(' + numVertexAttribs + ', gl.CURRENT_VERTEX_ATTRIB)'); + + debug(""); + debug("Test cases where name == 0"); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + + shouldNotBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + gl.deleteTexture(texture); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + shouldNotBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + gl.deleteRenderbuffer(renderbuffer); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + gl.deleteBuffer(buffer); + shouldBeNull('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + if (contextVersion > 1) { + debug(""); + debug("Test getInternalformatParameter") + + shouldBeNonNull('gl.getInternalformatParameter(gl.RENDERBUFFER, gl.R32I, gl.SAMPLES)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + await testInvalidArgument( + "getInternalformatParameter", + "target", + [ gl.RENDERBUFFER ], + function(target) { + return gl.getInternalformatParameter(target, gl.R32I, gl.SAMPLES); + }); + + await testInvalidArgument( + "getInternalformatParameter", + "pname", + [ gl.SAMPLES ], + function(pname) { + return gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA4, pname); + }); + + let validArrayForInterformat = new Array( + gl.R8, gl.R8_SNORM, gl.RG8, gl.RG8_SNORM, + gl.RGB8, gl.RGB8_SNORM, gl.RGB565, gl.RGBA4, + gl.RGB5_A1, gl.RGBA8, gl.RGBA8_SNORM, gl.RGB10_A2, + gl.RGB10_A2UI, gl.SRGB8, gl.SRGB8_ALPHA8, gl.R16F, + gl.RG16F, gl.RGB16F, gl.RGBA16F, gl.R32F, + gl.RG32F, gl.RGB32F, gl.RGBA32F, gl.R11F_G11F_B10F, + gl.RGB9_E5, gl.R8I, gl.R8UI, gl.R16I, + gl.R16UI, gl.R32I, gl.R32UI, gl.RG8I, + gl.RG8UI, gl.RG16I, gl.RG16UI, gl.RG32I, + gl.RG32UI, gl.RGB8I, gl.RGB8UI, gl.RGB16I, + gl.RGB16UI, gl.RGB32I, gl.RGB32UI, gl.RGBA8I, + gl.RGBA8UI, gl.RGBA16I, gl.RGBA16UI, gl.RGBA32I, + gl.RGBA32UI, gl.RGB, gl.RGBA, gl.DEPTH_STENCIL, gl.DEPTH_COMPONENT16, + gl.DEPTH_COMPONENT24, gl.DEPTH_COMPONENT32F, gl.DEPTH24_STENCIL8, + gl.DEPTH32F_STENCIL8, gl.STENCIL_INDEX8 + ); + await testInvalidArgument( + "getInternalformatParameter", + "internalformat", + validArrayForInterformat, + function(internalformat) { + return gl.getInternalformatParameter(gl.RENDERBUFFER, internalformat, gl.SAMPLES); + }); + + + debug(""); + debug("Test getIndexedParameter"); + window.buffer = gl.createBuffer(); + gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, buffer); + gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 64, gl.DYNAMIC_DRAW); + gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer, 4, 8); + shouldBe('gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)', 'buffer'); + shouldBe('gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)', '8'); + shouldBe('gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)', '4'); + window.buffer1 = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, buffer1); + gl.bufferData(gl.UNIFORM_BUFFER, 64, gl.DYNAMIC_DRAW); + window.offsetUniform = gl.getParameter(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT); + gl.bindBufferRange(gl.UNIFORM_BUFFER, 1, buffer1, offsetUniform, 8); + shouldBe('gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)', 'buffer1'); + shouldBe('gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, 1)', '8'); + shouldBe('gl.getIndexedParameter(gl.UNIFORM_BUFFER_START, 1)', 'offsetUniform'); + + gl.bindBufferBase(gl.UNIFORM_BUFFER, 1, null); + shouldBe('gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)', 'null'); + + let validArrayForTarget = new Array( + gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, + gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, + gl.TRANSFORM_FEEDBACK_BUFFER_START, + gl.UNIFORM_BUFFER_BINDING, + gl.UNIFORM_BUFFER_SIZE, + gl.UNIFORM_BUFFER_START + ); + await testInvalidArgument( + "getIndexedParameter", + "target", + validArrayForTarget, + function(target) { + return gl.getIndexedParameter(target, 0); + }); + + debug(""); + debug("Test getSamplerParameter"); + window.sampler = gl.createSampler(); + gl.samplerParameteri(sampler, gl.TEXTURE_COMPARE_FUNC, gl.LEQUAL); + gl.samplerParameteri(sampler, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE); + gl.samplerParameteri(sampler, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.samplerParameterf(sampler, gl.TEXTURE_MAX_LOD, 10); + gl.samplerParameteri(sampler, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.samplerParameterf(sampler, gl.TEXTURE_MIN_LOD, 0); + gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_COMPARE_FUNC)', 'gl.LEQUAL'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_COMPARE_MODE)', 'gl.COMPARE_REF_TO_TEXTURE'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_MAG_FILTER)', 'gl.NEAREST'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_MAX_LOD)', '10'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_MIN_FILTER)', 'gl.NEAREST'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_MIN_LOD)', '0'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_WRAP_R)', 'gl.CLAMP_TO_EDGE'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_WRAP_S)', 'gl.CLAMP_TO_EDGE'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_WRAP_T)', 'gl.CLAMP_TO_EDGE'); + let validArrayForSamplerParameter = new Array( + gl.TEXTURE_COMPARE_FUNC, + gl.TEXTURE_COMPARE_MODE, + gl.TEXTURE_MAG_FILTER, + gl.TEXTURE_MAX_LOD, + gl.TEXTURE_MIN_FILTER, + gl.TEXTURE_MIN_LOD, + gl.TEXTURE_WRAP_R, + gl.TEXTURE_WRAP_S, + gl.TEXTURE_WRAP_T + ); + await testInvalidArgument( + "getSamplerParameter", + "pname", + validArrayForSamplerParameter, + function(pname) { + return gl.getSamplerParameter(sampler, pname); + }); + + debug(""); + debug("Test getSyncParameter"); + window.sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); + shouldBe('gl.getSyncParameter(sync, gl.OBJECT_TYPE)', 'gl.SYNC_FENCE'); + let sync_status = gl.getSyncParameter(sync, gl.SYNC_STATUS); + switch (sync_status) { + case gl.UNSIGNALED: + testPassed('gl.getSyncParameter(sync, gl.SYNC_CONDITION) is gl.UNSIGNALED'); + break; + case gl.SIGNALED: + testPassed('gl.getSyncParameter(sync, gl.SYNC_CONDITION) is gl.SIGNALED'); + break; + default: + testFailed('gl.getSyncParameter(sync, gl.SYNC_CONDITION) was ' + sync_status + + ', expected gl.UNSIGNALED or gl.SIGNALED'); + break; + } + shouldBe('gl.getSyncParameter(sync, gl.SYNC_CONDITION)', 'gl.SYNC_GPU_COMMANDS_COMPLETE'); + shouldBe('gl.getSyncParameter(sync, gl.SYNC_FLAGS)', '0'); + let validArrayForSyncParameter = new Array( + gl.OBJECT_TYPE, + gl.SYNC_STATUS, + gl.SYNC_CONDITION, + gl.SYNC_FLAGS + ); + await testInvalidArgument( + "getSyncParameter", + "pname", + validArrayForSyncParameter, + function(pname) { + return gl.getSyncParameter(sync, pname); + }); + + debug(""); + debug("Test getQueryParameter"); + window.query = gl.createQuery(); + gl.beginQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query); + gl.endQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); + shouldBe('gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE)', 'false'); + // Queries' results are tested elsewhere in the conformance suite. It's complicated + // to wait for this query's result to become available and verify it. + let validArrayForPname = new Array( + gl.QUERY_RESULT, + gl.QUERY_RESULT_AVAILABLE + ); + await testInvalidArgument( + "getQueryParameter", + "pname", + validArrayForPname, + function(pname) { + return gl.getQueryParameter(query, pname); + } + ); + + debug(""); + debug("Test getFragDataLocation"); + let baseVertShader = '' + + '#version 300 es\n' + + 'uniform mat4 modelViewMatrix;\n' + + 'uniform mat4 projectionMatrix;\n' + + 'in vec4 vertex;\n' + + 'out vec4 position;\n' + + 'void main (void)\n' + + '{\n' + + ' position = modelViewMatrix * vertex;\n' + + ' gl_Position = projectionMatrix * position;\n' + + '}\n'; + let baseFragShader = '' + + '#version 300 es\n' + + 'in lowp vec4 position;\n' + + 'layout(location = 0) out mediump vec4 fragColor;\n' + + 'void main (void)\n' + + '{\n' + + ' fragColor = position;\n' + + '}\n'; + window.vertShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertShader, baseVertShader); + gl.compileShader(vertShader); + shouldBe('gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)', 'true'); + window.fragShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragShader, baseFragShader); + gl.compileShader(fragShader); + shouldBe('gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)', 'true'); + window.program = gl.createProgram(); + gl.attachShader(program, vertShader); + gl.attachShader(program, fragShader); + gl.linkProgram(program); + shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)','true'); + shouldBe('gl.getFragDataLocation(program, "vertexColor")', '-1'); + shouldBe('gl.getFragDataLocation(program, "modelViewMatrix")', '-1'); + shouldBe('gl.getFragDataLocation(program, "projectionMatrix")', '-1'); + shouldBe('gl.getFragDataLocation(program, "position")', '-1'); + shouldBe('gl.getFragDataLocation(program, "fragColor")', '0'); + + debug(""); + debug("Test getActiveUniforms"); + program = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(program); + shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true'); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + + let numActiveUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + let blockIndex = gl.getUniformBlockIndex(program, "Transform"); + let uniformIndices = []; + for (let i = 0; i < numActiveUniforms; i++) + uniformIndices.push(i); + let types = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_TYPE); + let sizes = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_SIZE); + let blockIndices = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_BLOCK_INDEX); + let offsets = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_OFFSET); + let arrayStrides = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_ARRAY_STRIDE); + let matrixStrides = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_MATRIX_STRIDE); + let rowMajors = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_IS_ROW_MAJOR); + for (let i = 0; i < numActiveUniforms; i++) { + if (types[i] != gl.FLOAT_MAT4 && types[i] != gl.FLOAT_MAT3) + testFailed("expected value: GL_FLOAT_MAT4 or GL_FLOAT_MAT3" + " actual value for UNIFORM_TYPE for uniform index[" + i + "]:" + wtu.glEnumToString(gl, types[i])); + if (sizes[i] != 1) + testFailed("expected value: 1" + " actual value for UNIFORM_SIZE for uniform index[" + i + "]:" + sizes[i]); + if (blockIndices[i] != blockIndex) + testFailed("expected value: 0" + " actual value for UNIFORM_BLOCK_INDEX for uniform index[" + i + "]:" + blockIndices[i]); + if (offsets[i] < 0) + testFailed("expected value >= 0" + " actual value for UNIFORM_OFFSET for uniform index[" + i + "]:" + offsets[i]); + if (arrayStrides[i] != 0) + testFailed("expected value: 0" + " actual value for UNIFORM_ARRAY_STRIDE for uniform index[" + i + "]:" + arrayStrides[i]); + if (matrixStrides[i] < 0) + testFailed("expected value >= 0" + " actual value for UNIFORM_MATRIX_STRIDE for uniform index[" + i + "]:" + matrixStrides[i]); + shouldBe(`"${typeof rowMajors[i]}"`, '"boolean"'); + if (rowMajors[i] != false) + testFailed("expected value: 0" + " actual value for UNIFORM_IS_ROW_MAJOR for uniform index[" + i + "]:" + rowMajors[i]); + } + + validArrayForPname = new Array( + gl.UNIFORM_TYPE, + gl.UNIFORM_SIZE, + gl.UNIFORM_BLOCK_INDEX, + gl.UNIFORM_OFFSET, + gl.UNIFORM_ARRAY_STRIDE, + gl.UNIFORM_MATRIX_STRIDE, + gl.UNIFORM_IS_ROW_MAJOR + ); + await testInvalidArgument( + "getActiveUniforms", + "pname", + validArrayForPname, + function(pname) { + return gl.getActiveUniforms(program, uniformIndices, pname); + } + ); + + debug(""); + debug("Test getUniformBlockIndex"); + program = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(program); + shouldBeTrue('gl.getProgramParameter(program, gl.LINK_STATUS)'); + shouldBe('gl.getUniformBlockIndex(program, "Transform")', '0'); + shouldBe('gl.getUniformBlockIndex(program, "u_modelViewMatrix")', 'gl.INVALID_INDEX'); + shouldBe('gl.getUniformBlockIndex(program, "normal")', 'gl.INVALID_INDEX'); + shouldBe('gl.getUniformBlockIndex(program, "u_normal")', 'gl.INVALID_INDEX'); + window.noUniformProgram = wtu.loadStandardProgram(gl); + gl.linkProgram(noUniformProgram); + shouldBeTrue('gl.getProgramParameter(noUniformProgram, gl.LINK_STATUS)'); + shouldBe('gl.getUniformBlockIndex(noUniformProgram, "u_modelViewProjMatrix")', 'gl.INVALID_INDEX'); + shouldBe('gl.getUniformBlockIndex(noUniformProgram, "u_normal")', 'gl.INVALID_INDEX'); + + debug(""); + debug("Test getActiveUniformBlockName"); + program = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(program); + shouldBeTrue('gl.getProgramParameter(program, gl.LINK_STATUS)'); + shouldBeEqualToString('gl.getActiveUniformBlockName(program, 0)', 'Transform'); + shouldBeNull('gl.getActiveUniformBlockName(program, -1)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeNull('gl.getActiveUniformBlockName(program, 1)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeNull('gl.getActiveUniformBlockName(program, gl.INVALID_INDEX)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + window.noLinkProgram = gl.createProgram(); + shouldBeFalse('gl.getProgramParameter(noLinkProgram, gl.LINK_STATUS)'); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getActiveUniformBlockName(noLinkProgram, 0)'); + noUniformProgram = wtu.loadStandardProgram(gl); + gl.linkProgram(noUniformProgram); + shouldBeTrue('gl.getProgramParameter(noUniformProgram, gl.LINK_STATUS)'); + shouldBeNull('gl.getActiveUniformBlockName(noUniformProgram, -1)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeNull('gl.getActiveUniformBlockName(noUniformProgram, 0)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeNull('gl.getActiveUniformBlockName(noUniformProgram, gl.INVALID_INDEX)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + debug(""); + debug("Test getActiveUniformBlockParameter"); + program = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(program); + shouldBeTrue('gl.getProgramParameter(program, gl.LINK_STATUS)'); + shouldBe('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_BINDING)', '0'); + gl.uniformBlockBinding(program, 0, 1); + shouldBe('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_BINDING)', '1'); + // The actual block data size can be bigger than 164, depending on the uniform block layout. + shouldBeTrue('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_DATA_SIZE) >= 164'); + shouldBe('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_ACTIVE_UNIFORMS)', '3'); + shouldBeTrue('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER)'); + shouldBeFalse('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER)'); + let indices = gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES); + for (let i = 0; i < 3; i++) { + if (indices[i] < 0) + testFailed("expected value >= 0" + " actual value for UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES for uniform index[" + i + "]:" + indices[i]); + } + validArrayForPname = new Array( + gl.UNIFORM_BLOCK_BINDING, + gl.UNIFORM_BLOCK_DATA_SIZE, + gl.UNIFORM_BLOCK_ACTIVE_UNIFORMS, + gl.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, + gl.UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, + gl.UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER + ); + await testInvalidArgument( + "getActiveUniformBlockParameter", + "pname", + validArrayForPname, + function(pname) { + return gl.getActiveUniformBlockParameter(program, 0, pname); + } + ); + } + + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + finishTest(); +})(); + +let successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-vertex-attrib.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-vertex-attrib.js new file mode 100644 index 0000000000..97cad40868 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-vertex-attrib.js @@ -0,0 +1,263 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +description("This test ensures WebGL implementations vertexAttrib can be set and read."); + +debug(""); +debug("Canvas.getContext"); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("canvas", undefined, contextVersion); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + debug(""); + debug("Checking gl.vertexAttrib."); + + var numVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + for (var ii = 0; ii < numVertexAttribs; ++ii) { + gl.vertexAttrib1fv(ii, [1]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib1fv(ii, new Float32Array([-1])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '-1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib2fv(ii, [1, 2]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib2fv(ii, new Float32Array([1, -2])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '-2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib3fv(ii, [1, 2, 3]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '3'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib3fv(ii, new Float32Array([1, -2, 3])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '-2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '3'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib4fv(ii, [1, 2, 3, 4]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '3'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '4'); + + gl.vertexAttrib4fv(ii, new Float32Array([1, 2, -3, 4])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '-3'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '4'); + + gl.vertexAttrib1f(ii, 5); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '5'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib2f(ii, 6, 7); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '6'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '7'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib3f(ii, 7, 8, 9); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '7'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '8'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '9'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib4f(ii, 6, 7, 8, 9); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '6'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '7'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '8'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '9'); + + if (contextVersion > 1) { + gl.vertexAttribI4i(ii, -1, 0, 1, 2); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Int32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '-1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '2'); + + gl.vertexAttribI4ui(ii, 0, 1, 2, 3); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Uint32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '3'); + + gl.vertexAttribI4iv(ii, [-1, 0, 1, 2]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Int32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '-1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '2'); + + gl.vertexAttribI4iv(ii, new Int32Array([1, 0, -1, 2])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Int32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '-1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '2'); + + gl.vertexAttribI4uiv(ii, [0, 1, 2, 3]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Uint32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '3'); + + gl.vertexAttribI4uiv(ii, new Uint32Array([0, 2, 1, 3])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Uint32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '3'); + } + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Checking out-of-range vertexAttrib index"); + gl.getVertexAttrib(numVertexAttribs, gl.CURRENT_VERTEX_ATTRIB); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib1fv(numVertexAttribs, [1]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib1fv(numVertexAttribs, new Float32Array([-1])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2fv(numVertexAttribs, [1, 2]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2fv(numVertexAttribs, new Float32Array([1, -2])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3fv(numVertexAttribs, [1, 2, 3]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3fv(numVertexAttribs, new Float32Array([1, -2, 3])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4fv(numVertexAttribs, [1, 2, 3, 4]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4fv(numVertexAttribs, new Float32Array([1, 2, -3, 4])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib1f(numVertexAttribs, 5); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2f(numVertexAttribs, 6, 7); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3f(numVertexAttribs, 7, 8, 9); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4f(numVertexAttribs, 6, 7, 8, 9); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + if (contextVersion > 1) { + gl.vertexAttribI4i(numVertexAttribs, -1, 0, 1, 2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4ui(numVertexAttribs, 0, 1, 2, 3); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4iv(numVertexAttribs, [-1, 0, 1, 2]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4iv(numVertexAttribs, new Int32Array([1, 0, -1, 2])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4uiv(numVertexAttribs, [0, 1, 2, 3]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4uiv(numVertexAttribs, new Uint32Array([0, 2, 1, 3])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + } + + debug(""); + debug("Checking invalid array lengths"); + numVertexAttribs = numVertexAttribs - 1; + gl.vertexAttrib1fv(numVertexAttribs, []); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib1fv(numVertexAttribs, new Float32Array([])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2fv(numVertexAttribs, [1]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2fv(numVertexAttribs, new Float32Array([1])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3fv(numVertexAttribs, [1, 2]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3fv(numVertexAttribs, new Float32Array([1, -2])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4fv(numVertexAttribs, [1, 2, 3]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4fv(numVertexAttribs, new Float32Array([1, 2, -3])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + if (contextVersion > 1) { + gl.vertexAttribI4iv(numVertexAttribs, [-1, 0, 1]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4iv(numVertexAttribs, new Int32Array([1, 0, -1])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4uiv(numVertexAttribs, [0, 1, 2]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4uiv(numVertexAttribs, new Uint32Array([0, 2, 1])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + } +} + +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/instanceof-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/instanceof-test.js new file mode 100644 index 0000000000..edcad3b1f6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/instanceof-test.js @@ -0,0 +1,105 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +var wtu = WebGLTestUtils; +description(document.title); +debug("Tests that instanceof works on WebGL objects."); +debug(""); + +function checkGLError(message) { + var error = gl.getError(); + if (error != gl.NO_ERROR) { + wtu.error("Error: " + message + " caused " + wtu.glEnumToString(gl, error)); + } +} + +var gl = wtu.create3DContext("canvas", undefined, contextVersion); +if (contextVersion === 1) { + shouldBeTrue('gl instanceof WebGLRenderingContext'); +} else if (contextVersion === 2) { + shouldBeTrue('gl instanceof WebGL2RenderingContext'); +} + +shouldBeTrue('gl.createBuffer() instanceof WebGLBuffer'); +checkGLError("createBuffer") + +shouldBeTrue('gl.createFramebuffer() instanceof WebGLFramebuffer'); +checkGLError("createFramebuffer") + +shouldBeTrue('gl.createProgram() instanceof WebGLProgram'); +checkGLError("createProgram") + +shouldBeTrue('gl.createRenderbuffer() instanceof WebGLRenderbuffer'); +checkGLError("createRenderbuffer") + +shouldBeTrue('gl.createShader(gl.VERTEX_SHADER) instanceof WebGLShader'); +checkGLError("createShader") + +shouldBeTrue('gl.createTexture() instanceof WebGLTexture'); +checkGLError("createTexture") + +if (contextVersion > 1) { + shouldBeTrue('gl.createQuery() instanceof WebGLQuery'); + checkGLError("createQuery") + + shouldBeTrue('gl.createSampler() instanceof WebGLSampler'); + checkGLError("createSampler") + + shouldBeTrue('gl.createTransformFeedback() instanceof WebGLTransformFeedback'); + checkGLError("createTransformFeedback") + + shouldBeTrue('gl.createVertexArray() instanceof WebGLVertexArrayObject'); + checkGLError("createVertexArray") +} + +var program = wtu.setupProgram(gl, ['vshader', 'fshader'], ['vPosition'], [0]); + +shouldBeTrue('gl.getUniformLocation(program, "color") instanceof WebGLUniformLocation'); +checkGLError("getUniformLocation") + +shouldBeTrue('gl.getActiveAttrib(program, 0) instanceof WebGLActiveInfo'); +checkGLError("getActiveAttrib") + +shouldBeTrue('gl.getActiveUniform(program, 0) instanceof WebGLActiveInfo'); +checkGLError("getActiveUniform") + +debug(""); +debug("Tests that those WebGL objects can not be constructed through new operator"); +debug(""); + +function shouldThrowWithNew(objectType, objectName) { + try { + new objectType; + testFailed('new ' + objectName + ' did not throw'); + } catch (e) { + testPassed('new ' + objectName + ' threw an error'); + } +} + +shouldThrowWithNew(window.WebGLRenderingContext, 'WebGLRenderingContext'); +shouldThrowWithNew(window.WebGLActiveInfo, 'WebGLActiveInfo'); +shouldThrowWithNew(window.WebGLBuffer, 'WebGLBuffer'); +shouldThrowWithNew(window.WebGLFramebuffer, 'WebGLFramebuffer'); +shouldThrowWithNew(window.WebGLProgram, 'WebGLProgram'); +shouldThrowWithNew(window.WebGLRenderbuffer, 'WebGLRenderbuffer'); +shouldThrowWithNew(window.WebGLShader, 'WebGLShader'); +shouldThrowWithNew(window.WebGLTexture, 'WebGLTexture'); +shouldThrowWithNew(window.WebGLUniformLocation, 'WebGLUniformLocation'); +shouldThrowWithNew(window.WebGLShaderPrecisionFormat, 'WebGLShaderPrecisionFormat'); +if (contextVersion > 1) { + shouldThrowWithNew(window.WebGLQuery, 'WebGLQuery'); + shouldThrowWithNew(window.WebGLSampler, 'WebGLSampler'); + shouldThrowWithNew(window.WebGLSync, 'WebGLSync'); + shouldThrowWithNew(window.WebGLTransformFeedback, 'WebGLTransformFeedback'); + shouldThrowWithNew(window.WebGLVertexArrayObject, 'WebGLVertexArrayObject'); +} + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/invalid-vertex-attrib-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/invalid-vertex-attrib-test.js new file mode 100644 index 0000000000..b28c484843 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/invalid-vertex-attrib-test.js @@ -0,0 +1,129 @@ +var createInvalidAttribTestFn = (function() { + +async function testPreserveDrawingBufferTrue(gl, drawFn, clear) { + debug(''); + debug(`test preserveDrawingBuffer: true with ${drawFn.name} ${clear ? 'with' : 'without'} clear`); + + if (clear) { + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + const skipTest = drawFn(gl); + if (skipTest) { + debug('skipped: extension does not exist'); + return; + } + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + + await waitForComposite(); + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function setupWebGL({ + webglVersion, + shadersFn, + attribs, +}) { + const positionBuf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1, -1, + 1, -1, + -1, 1, + -1, 1, + 1, -1, + 1, 1, + ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + const indexBuf = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuf); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 3, 4, 5]), gl.STATIC_DRAW); + return gl; +} + +function createInvalidAttribTestFn(gl) { + const vs = ` + attribute vec4 vPosition; + void main() + { + gl_Position = vPosition; + } + `; + + const fs = ` + precision mediump float; + void main() + { + gl_FragColor = vec4(1, 0, 0, 1); + } + ` + + const program = wtu.setupProgram(gl, [vs, fs], ["vPosition"]); + if (!program) { + debug(`program failed to compile: ${wtu.getLastError()}`); + } + + const positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1, -1, + 1, -1, + -1, 1, + -1, 1, + 1, -1, + 1, 1, + ]), gl.STATIC_DRAW); + + const indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, + new Uint8Array([0, 1, 2, 3, 4, 5]), + gl.STATIC_DRAW); + + return async function invalidAttribTestFn(drawFn) { + debug(''); + + // reset attribs + gl.bindBuffer(gl.ARRAY_BUFFER, null); + const numAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + for (let i = 0; i < numAttribs; ++i) { + gl.disableVertexAttribArray(i); + gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); + } + + debug(`test ${drawFn.name} draws with valid attributes`); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + gl.clearColor(0, 0, 0, 0,); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [0, 0, 0, 0], "canvas should be zero"); + + drawFn(gl); + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + debug(`test ${drawFn.name} generates INVALID_OPERATION draws with enabled attribute no buffer bound`); + gl.enableVertexAttribArray(1); + + gl.clearColor(0, 0, 0, 0,); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [0, 0, 0, 0], "canvas should be zero"); + + drawFn(gl); + + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should generate INVALID_OPERATION"); + wtu.checkCanvas(gl, [0, 0, 0, 0], "canvas should be zero"); + }; +} + +return createInvalidAttribTestFn; +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/iterable-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/iterable-test.js new file mode 100644 index 0000000000..45e509f50a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/iterable-test.js @@ -0,0 +1,183 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +IterableTest = (function() { + + var wtu = WebGLTestUtils; + + function run(test, iterations) { + var target = iterations || 10; + var count = 0; + + function doNextTest() { + ++count; + debug("Test " + count + " of " + target); + var success = test(); + if (count < target && success !== false) { + wtu.dispatchPromise(doNextTest); + } else { + finishTest(); + } + } + + doNextTest(); + } + + // Creates a canvas and a texture then exits. There are + // no references to either so both should be garbage collected. + function createContextCreationAndDestructionTest() { + var textureSize = null; + + return function() { + var canvas = document.createElement("canvas"); + // This is safe for any device. See drawingBufferWidth in spec. + canvas.width = 2048; + canvas.height = 2048; + var gl = wtu.create3DContext(canvas); + if (textureSize === null) { + var maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); + textureSize = Math.min(1024, maxTextureSize); + } + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize, textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, + null); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); + + return true; + }; + } + + // Creates many small canvases and attaches them to the DOM. + // This tests an edge case discovered in Chrome where the creation of multiple + // WebGL contexts would eventually lead to context creation failure. + // (crbug.com/319265) The test does not require that old contexts remain + // valid, only that new ones can be created. + function createContextCreationTest() { + return function() { + var canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = 1; + + document.body.appendChild(canvas); + + var gl = wtu.create3DContext(canvas); + if (!gl) { + return false; + } + + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); + + return true; + }; + } + + // Draws rectangle on a passed canvas with preserveDrawingBuffer + // and antialiasing ON, tests rect color on every iteration. + function createMultisampleCorruptionTest(gl) { + var lastContext = null; + // Allocate a read back buffer in advance and reuse it for all iterations + // to avoid memory issues because of late garbage collection. + var readBackBuf = new Uint8Array(gl.canvas.width * gl.canvas.height * 4); + + var program = wtu.loadStandardProgram(gl); + var uniforms = wtu.getUniformMap(gl, program); + gl.useProgram(program); + + gl.clearColor(1.0, 0.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,2.5,0, 1.5,1.5,0, 2.5,1.5,0 ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + gl.vertexAttrib3f(1, 0.0, 0.0, 1.0); + + var identityMat = new Float32Array([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]); + + gl.uniformMatrix4fv(uniforms.u_modelViewProjMatrix.location, false, identityMat); + + function test() { + var gl2 = wtu.create3DContext(null, {antialias: true}); + + gl2.canvas.width = gl2.canvas.height = 1024; + gl2.canvas.style.width = gl2.canvas.style.height = "1px"; + document.body.appendChild(gl2.canvas); + + gl2.clearColor(1.0, 0.0, 0.0, 1.0); + gl2.clear(gl2.COLOR_BUFFER_BIT); + + if(lastContext) { + gl.drawArrays(gl.TRIANGLES, 0, 3); + var msg = "Canvas should be red"; + wtu.checkCanvasRectColor(gl, + 0, 0, gl.canvas.width, gl.canvas.height, + [255, 0, 0, 255], null, + function() { + testPassed(msg); + }, + function() { + testFailed(msg); + return false; + }, + debug, readBackBuf); + document.body.removeChild(lastContext.canvas); + } + + lastContext = gl2; + return true; + }; + + // First pass does initialization + test(); + + return test; + } + + // Draws repeatedly to a large canvas with preserveDrawingBuffer enabled to + // try and provoke a context loss. + function createPreserveDrawingBufferLeakTest(gl) { + var contextActive = true; + gl.canvas.addEventListener("webglcontextlost", () => { + testFailed("Context was lost"); + contextActive = false; + }); + + function test() { + var x = Math.random() * gl.drawingBufferWidth; + var y = Math.random() * gl.drawingBufferHeight; + var width = Math.random() * (gl.drawingBufferWidth - x); + var height = Math.random() * (gl.drawingBufferHeight - y); + + gl.enable(gl.SCISSOR_TEST); + gl.scissor(x, y, width, height); + gl.clearColor(Math.random(), Math.random(), Math.random(), 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); + + return contextActive; + }; + + return test; + } + + return { + run: run, + + createContextCreationAndDestructionTest: createContextCreationAndDestructionTest, + createContextCreationTest: createContextCreationTest, + createMultisampleCorruptionTest: createMultisampleCorruptionTest, + createPreserveDrawingBufferLeakTest: createPreserveDrawingBufferLeakTest + }; + +})(); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/line-rendering-quality.js b/dom/canvas/test/webgl-conf/checkout/js/tests/line-rendering-quality.js new file mode 100644 index 0000000000..dfa7c02167 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/line-rendering-quality.js @@ -0,0 +1,163 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; +description("Verifies that lines, both aliased and antialiased, have acceptable quality."); + +let wtu = WebGLTestUtils; +let gl; + +let aa_fbo; + +function setupWebGL1Test(canvasId, useAntialiasing) { + gl = wtu.create3DContext(canvasId, { antialias: useAntialiasing }, contextVersion); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors during WebGL 1.0 setup"); +} + +function setupWebGL2Test(canvasId, useAntialiasing) { + gl = wtu.create3DContext(canvasId, { antialias: false }, contextVersion); + // In WebGL 2.0, we always allocate the back buffer without + // antialiasing. The only question is whether we allocate a + // framebuffer with a multisampled renderbuffer attachment. + aa_fbo = null; + if (useAntialiasing) { + aa_fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, aa_fbo); + let rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + let supported = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES); + // Prefer 4, then 8, then max. + let preferred = [4, 8]; + let allocated = false; + for (let value of preferred) { + if (supported.indexOf(value) >= 0) { + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, value, gl.RGBA8, + gl.drawingBufferWidth, gl.drawingBufferHeight); + allocated = true; + break; + } + } + if (!allocated) { + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, supported[supported.length - 1], + gl.RGBA8, gl.drawingBufferWidth, gl.drawingBufferHeight); + } + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors during WebGL 2.0 setup"); +} + +function setupLines() { + let prog = wtu.setupSimpleColorProgram(gl, 0); + let loc = gl.getUniformLocation(prog, 'u_color'); + if (loc == null) { + testFailed('Failed to fetch color uniform'); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of line program"); + gl.uniform4f(loc, 1.0, 1.0, 1.0, 1.0); + let buffer = gl.createBuffer(); + let scale = 0.5; + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -scale, -scale, 0.0, 1.0, + -scale, scale, 0.0, 1.0, + scale, scale, 0.0, 1.0, + scale, -scale, 0.0, 1.0, + -scale, -scale, 0.0, 1.0, + ]), gl.STATIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of buffer"); + gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of attribute array"); +} + +function renderLines(contextVersion, useAntialiasing) { + gl.clearColor(0.0, 0.0, 0.5, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.LINE_STRIP, 0, 5); + if (contextVersion == 2 && useAntialiasing) { + // Blit aa_fbo into the real back buffer. + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, aa_fbo); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + let w = gl.drawingBufferWidth; + let h = gl.drawingBufferHeight; + gl.blitFramebuffer(0, 0, w, h, + 0, 0, w, h, + gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + } +} + +function pixelAboveThreshold(arr, pixelIndex, threshold) { + return (arr[4 * pixelIndex + 0] >= threshold && + arr[4 * pixelIndex + 1] >= threshold && + arr[4 * pixelIndex + 2] >= threshold && + arr[4 * pixelIndex + 3] >= threshold); +} + +function checkLine(arr, threshold, direction) { + // Count number of crossings from below threshold to above (or equal + // to) threshold. Must be equal to 2. + + let numPixels = arr.length / 4; + let numUpCrossings = 0; + let numDownCrossings = 0; + for (let index = 0; index < numPixels - 1; ++index) { + let curPixel = pixelAboveThreshold(arr, index, threshold); + let nextPixel = pixelAboveThreshold(arr, index + 1, threshold); + if (!curPixel && nextPixel) { + ++numUpCrossings; + } else if (curPixel && !nextPixel) { + ++numDownCrossings; + } + } + if (numUpCrossings != numDownCrossings) { + testFailed('Found differing numbers of up->down and down->up transitions'); + } + if (numUpCrossings == 2) { + testPassed('Found 2 lines, looking in the ' + direction + ' direction'); + } else { + testFailed('Found ' + numUpCrossings + ' lines, looking in the ' + + direction + ' direction, expected 2'); + } +} + +function checkResults() { + // Check the middle horizontal and middle vertical line of the canvas. + let w = gl.drawingBufferWidth; + let h = gl.drawingBufferHeight; + let t = 100; + let arr = new Uint8Array(4 * w); + gl.readPixels(0, Math.floor(h / 2), + w, 1, gl.RGBA, gl.UNSIGNED_BYTE, arr); + checkLine(arr, t, 'horizontal'); + arr = new Uint8Array(4 * h); + gl.readPixels(Math.floor(w / 2), 0, + 1, h, gl.RGBA, gl.UNSIGNED_BYTE, arr); + checkLine(arr, t, 'vertical'); +} + +function runTest(contextVersion, canvasId, useAntialiasing) { + switch (contextVersion) { + case 1: { + setupWebGL1Test(canvasId, useAntialiasing); + break; + } + case 2: { + setupWebGL2Test(canvasId, useAntialiasing); + } + } + setupLines(); + renderLines(contextVersion, useAntialiasing); + checkResults(); +} + +function runTests() { + runTest(contextVersion, 'testbed', false); + runTest(contextVersion, 'testbed2', true); +} + +runTests(); +let successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js b/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js new file mode 100644 index 0000000000..1202b7868c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js @@ -0,0 +1,247 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +NoOverOptimizeOnUniformArrayTester = (function(){ + +var vshader = [ + "attribute vec4 a_position;", + "void main()", + "{", + " gl_Position = a_position;", + "}" +].join('\n'); + +var fshader_max = [ + "precision mediump float;", + "uniform vec4 colora[$(maxUniformVectors)];", + "void main()", + "{", + " gl_FragColor = vec4(colora[$(usedUniformVector)]);", + "}" +].join('\n'); + +var fshader_max_ab_ab = [ + "precision mediump float;", + "uniform vec4 $(decl1);", + "uniform vec4 $(decl2);", + "void main()", + "{", + "gl_FragColor = vec4($(usage1) + $(usage2));", + "}" +].join('\n'); + +// MaxInt32 is 2^32-1. We need +1 of that to test overflow conditions +var MaxInt32PlusOne = 4294967296; + +function setupTests(gl) { + var tests = []; + var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); + + // This test is to test drivers the have bugs related to optimizing + // an array of uniforms when only 1 of those uniforms is used. + tests.push({ + desc: "using last element", + maxUniformVectors: maxUniformVectors, + usedUniformVector: maxUniformVectors - 1, + shader: "fshader-max", + color: [0, 1, 0, 1], + arrayName: "colora", + extraName: "colorb", + }); + tests.push({ + desc: "using first element", + maxUniformVectors: maxUniformVectors, + usedUniformVector: 0, + shader: "fshader-max", + color: [0, 1, 0, 1], + arrayName: "colora", + extraName: "colorb", + }); + + // Generate test shaders. We're trying to force the driver to + // overflow from 1 array into the next if it optimizes. So for example if it was C + // + // int big[4]; + // int little[1]; + // big[5] = 124; + // + // Would end up setting little[0] instead of big. Some drivers optimize + // where if you only use say 'big[3]' it will actually only allocate just 1 element + // for big. + // + // But, some drivers have a bug where the fact that they optimized big to 1 element + // does not get passed down to glUniform so when setting the uniform 'big[3]' they + // overwrite memory. + // + // If the driver crashes, yea. We found a bug. We can block the driver. + // Otherwise we try various combinations so that setting 'little[0]' first + // and then setting all elements of 'big' we hope it will overwrite 'little[0]' + // which will show the bug and again we can block the driver. + // + // We don't know how the driver will order, in memory, the various uniforms + // or for that matter we don't even know if they will be contiguous in memory + // but to hopefully expose any bugs we try various combinations. + // + // It could be the compiler orders uniforms alphabetically. + // It could be it orders them in order of declaration. + // It could be it orders them in order of usage. + // + // We also test using only first element of big or just the last element of big. + // + for (var nameOrder = 0; nameOrder < 2; ++nameOrder) { + var name1 = nameOrder ? "colora" : "colorb"; + var name2 = nameOrder ? "colorb" : "colora"; + for (var last = 0; last < 2; ++last) { + var usedUniformVector = last ? maxUniformVectors - 2 : 0; + for (var declOrder = 0; declOrder < 2; ++declOrder) { + var bigName = declOrder ? name1 : name2; + var littleName = declOrder ? name2 : name1; + var decl1 = bigName + "[" + (maxUniformVectors - 1) + "]"; + var decl2 = littleName + "[1]"; + if (declOrder) { + var t = decl1; + decl1 = decl2; + decl2 = t; + } + for (var usageOrder = 0; usageOrder < 2; ++usageOrder) { + var usage1 = bigName + "[" + usedUniformVector + "]"; + var usage2 = littleName + "[0]"; + if (usageOrder) { + var t = usage1; + usage1 = usage2; + usage2 = t; + } + var fSrc = wtu.replaceParams(fshader_max_ab_ab, { + decl1: decl1, + decl2: decl2, + usage1: usage1, + usage2: usage2, + }); + var desc = "testing: " + name1 + ":" + name2 + " using " + (last ? "last" : "first") + + " creating uniforms " + decl1 + " " + decl2 + " and accessing " + usage1 + " " + usage2; + tests.push({ + desc: desc, + maxUniformVectors: maxUniformVectors - 1, + usedUniformVector: usedUniformVector, + source: fSrc, + color: [0, 0, 0, 1], + arrayName: bigName, + extraName: littleName, + }); + } + } + } + } + return tests; +}; + +function testUniformOptimizationIssues(test) { + debug(""); + debug(test.desc); + var fshader = test.source; + if (!fshader) { + fshader = wtu.replaceParams(fshader_max, test); + } + + var consoleElem = document.getElementById("console"); + wtu.addShaderSource( + consoleElem, "vertex shader", vshader); + wtu.addShaderSource( + consoleElem, "fragment shader", fshader); + + var program = wtu.loadProgram(gl, vshader, fshader); + gl.useProgram(program); + + var colorbLocation = gl.getUniformLocation(program, test.extraName + "[0]"); + if (colorbLocation) { + gl.uniform4fv(colorbLocation, [0, 1, 0, 0]); + } + + // Ensure that requesting an array uniform past MaxInt32PlusOne returns no uniform + var nameMaxInt32PlusOne = test.arrayName + "[" + (test.usedUniformVector + MaxInt32PlusOne) + "]"; + assertMsg(gl.getUniformLocation(program, nameMaxInt32PlusOne) === null, + "Requesting " + nameMaxInt32PlusOne + " uniform should return a null uniform location"); + + // Set just the used uniform + var name = test.arrayName + "[" + test.usedUniformVector + "]"; + var uniformLocation = gl.getUniformLocation(program, name); + gl.uniform4fv(uniformLocation, test.color); + wtu.setupIndexedQuad(gl, 1); + wtu.clearAndDrawIndexedQuad(gl, 1); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + + // Set all the unused uniforms + var locations = []; + var allRequiredUniformLocationsQueryable = true; + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + var name = test.arrayName + "[" + ii + "]"; + var uniformLocation = gl.getUniformLocation(program, name); + locations.push(uniformLocation); + if (ii == test.usedUniformVector) { + continue; + } + // Locations > usedUnformVector may not exist. + // Locations <= usedUniformVector MUST exist. + if (ii <= test.usedUniformVector && (uniformLocation === undefined || uniformLocation === null)) { + allRequiredUniformLocationsQueryable = false; + } + gl.uniform4fv(uniformLocation, [1, 0, 0, 1]); + } + if (allRequiredUniformLocationsQueryable) { + testPassed("allRequiredUniformLocationsQueryable is true."); + } + else { + testFailed("allRequiredUniformLocationsQueryable should be true. Was false."); + } + var positionLoc = gl.getAttribLocation(program, "a_position"); + wtu.setupIndexedQuad(gl, 1, positionLoc); + wtu.clearAndDrawIndexedQuad(gl, 1); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + + // Check we can read & write each uniform. + // Note: uniforms past test.usedUniformVector might not exist. + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + gl.uniform4fv(locations[ii], [ii + 4, ii + 2, ii + 3, ii + 1]); + } + + var kEpsilon = 0.01; + var isSame = function(v1, v2) { + return Math.abs(v1 - v2) < kEpsilon; + }; + + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + var location = locations[ii]; + if (location) { + var value = gl.getUniform(program, locations[ii]); + if (!isSame(value[0], ii + 4) || + !isSame(value[1], ii + 2) || + !isSame(value[2], ii + 3) || + !isSame(value[3], ii + 1)) { + testFailed("location: " + ii + " was not correct value"); + break; + } + } + } +} + +function runOneTest(gl, test) { + testUniformOptimizationIssues(test); +}; + +function runTests(gl, tests) { + debug(""); + debug("Test drivers don't over optimize unused array elements"); + + for (var ii = 0; ii < tests.length; ++ii) { + runOneTest(gl, tests[ii]); + } +}; + +return { + setupTests : setupTests, + runTests : runTests +}; + +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/oes-texture-float-and-half-float-linear.js b/dom/canvas/test/webgl-conf/checkout/js/tests/oes-texture-float-and-half-float-linear.js new file mode 100644 index 0000000000..362023ce01 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/oes-texture-float-and-half-float-linear.js @@ -0,0 +1,151 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function testTexLinear(gl, extensionName, internalFormatWebgl2, pixelType) { + var wtu = WebGLTestUtils; + + // Before the extension is enabled + var extensionEnabled = false; + runTestSuite(extensionEnabled); + + if (!gl.getExtension(extensionName)) + testPassed("No " + extensionName + " support -- this is legal"); + else { + // After the extension is enabled + extensionEnabled = true; + runTestSuite(extensionEnabled); + } + + function runTestSuite(extensionEnabled) + { + var magF = [gl.NEAREST, gl.LINEAR]; + var minF = [gl.NEAREST, gl.LINEAR, gl.NEAREST_MIPMAP_NEAREST, gl.NEAREST_MIPMAP_LINEAR, gl.LINEAR_MIPMAP_NEAREST, gl.LINEAR_MIPMAP_LINEAR]; + var tex2DFShader = [ + 'uniform sampler2D tex;', + 'void main() {', + ' gl_FragData[0] = texture2D(tex, vec2(0.5, 0.5)) * vec4(4.0, 2.0, 2.0, 1);', + '}'].join('\n'); + + var positionVertexShader = [ + 'attribute vec4 vPosition;', + 'void main() {', + ' gl_Position = vPosition;', + '}'].join('\n'); + + var texCubeFShader = [ + 'uniform samplerCube tex;', + 'void main() {', + ' gl_FragColor = textureCube(tex, normalize(vec3(0.5, 0.5, 1))) * vec4(4.0, 2.0, 2.0, 1);', + '}'].join('\n'); + + var vs = wtu.loadShader(gl, positionVertexShader, gl.VERTEX_SHADER); + var fs_2d = wtu.loadShader(gl, tex2DFShader, gl.FRAGMENT_SHADER); + var fs_cube = wtu.loadShader(gl, texCubeFShader, gl.FRAGMENT_SHADER); + + // TEXTURE_2D + var program = wtu.setupProgram(gl, [vs, fs_2d]); + gl.useProgram(program); + wtu.setupUnitQuad(gl); + for (var kk = 0; kk < 2; ++kk) { + for (var ii = 0; ii < 6; ++ii) { + var linear = false; + if (magF[kk] == gl.LINEAR || (minF[ii] != gl.NEAREST && minF[ii] != gl.NEAREST_MIPMAP_NEAREST)) + linear = true; + var color = (!extensionEnabled && linear) ? [0, 0, 0, 255] : [255, 255, 255, 255]; + runEachTest(gl.TEXTURE_2D, magF[kk], minF[ii], linear, extensionEnabled, color); + } + } + + // TEXTURE_CUBE_MAP + var programCube = wtu.setupProgram(gl, [vs, fs_cube]); + gl.useProgram(programCube); + wtu.setupUnitQuad(gl); + for (var kk = 0; kk < 2; ++kk) { + for (var ii = 0; ii < 6; ++ii) { + var linear = false; + if (magF[kk] == gl.LINEAR || (minF[ii] != gl.NEAREST && minF[ii] != gl.NEAREST_MIPMAP_NEAREST)) + linear = true; + var color = (!extensionEnabled && linear) ? [0, 0, 0, 255] : [255, 255, 255, 255]; + runEachTest(gl.TEXTURE_CUBE_MAP, magF[kk], minF[ii], linear, extensionEnabled, color); + } + } + } + + function runEachTest(textureTarget, magFilter, minFilter, linear, extensionEnabled, expected) + { + const format = gl.RGBA; + let internalFormat = format; + if (wtu.isWebGL2(gl)) { + internalFormat = gl[internalFormatWebgl2]; + } + var numberOfChannels = 4; + debug(""); + debug("testing target: " + wtu.glEnumToString(gl,textureTarget) + + ", testing format: " + wtu.glEnumToString(gl, format) + + ", magFilter is: " + wtu.glEnumToString(gl, magFilter) + + ", minFilter is: " + wtu.glEnumToString(gl, minFilter) + + ", " + extensionName + " is " + (extensionEnabled ? "enabled": "not enabled") + ); + + // Generate data. + var width = 4; + var height = 4; + var canvas2d = document.createElement('canvas'); + canvas2d.width = width; + canvas2d.height = height; + var ctx2d = canvas2d.getContext('2d'); + var color = [64, 128, 128, 255]; + ctx2d.fillStyle = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ")"; + ctx2d.fillRect(0, 0, width, height); + + var texture = gl.createTexture(); + gl.bindTexture(textureTarget, texture); + gl.texParameteri(textureTarget, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(textureTarget, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(textureTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(textureTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + if (textureTarget == gl.TEXTURE_2D) { + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, gl[pixelType], canvas2d); + if (minFilter != gl.NEAREST && minFilter != gl.LINEAR) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors during texture setup"); + gl.generateMipmap(gl.TEXTURE_2D); + if (gl.getError() != gl.NO_ERROR) { + debug("generateMipmap failed for floating-point TEXTURE_2D -- this is legal -- skipping the rest of this test"); + return; + } + } + } else if (textureTarget == gl.TEXTURE_CUBE_MAP) { + var targets = [ + gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + for (var tt = 0; tt < targets.length; ++tt) + gl.texImage2D(targets[tt], 0, internalFormat, format, gl[pixelType], canvas2d); + if (minFilter != gl.NEAREST && minFilter != gl.LINEAR) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors during texture setup"); + gl.generateMipmap(gl.TEXTURE_CUBE_MAP); + if (gl.getError() != gl.NO_ERROR) { + debug("generateMipmap failed for floating-point TEXTURE_CUBE_MAP -- this is legal -- skipping the rest of this test"); + return; + } + } + } + wtu.clearAndDrawUnitQuad(gl); + if (!linear) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, pixelType + " texture with non-Linear filter should succeed with NO_ERROR no matter whether " + extensionName + " is enabled or not"); + } else if (!extensionEnabled) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, pixelType + " texture with Linear filter should produce [0, 0, 0, 1.0] with NO_ERROR if " + extensionName + " isn't enabled"); + } else { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, pixelType + " texture with Linear filter should succeed with NO_ERROR if " + extensionName + " is enabled"); + } + + wtu.checkCanvas(gl, expected, "should be " + expected[0] + "," + expected[1] + "," + expected[2] + "," + expected[3]); + } +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/offscreencanvas-transfer-image-bitmap.js b/dom/canvas/test/webgl-conf/checkout/js/tests/offscreencanvas-transfer-image-bitmap.js new file mode 100644 index 0000000000..b25a7dd026 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/offscreencanvas-transfer-image-bitmap.js @@ -0,0 +1,57 @@ +function testTransferToImageBitmap(webglContextVersion, bitmap) { + var internalFormat = "RGBA"; + var pixelFormat = "RGBA"; + var pixelType = "UNSIGNED_BYTE"; + + var width = 32; + var height = 32; + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + var gl = WebGLTestUtils.create3DContext(canvas); + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + TexImageUtils.setupTexturedQuad(gl, internalFormat); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(gl.TEXTURE_2D, texture); + // Set up texture parameters + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + var targets = [gl.TEXTURE_2D]; + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], bitmap); + } + for (var tt = 0; tt < targets.length; ++tt) { + // Draw the triangles + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + var buf = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); + _checkCanvas(buf, width, height, webglContextVersion); + } +} + +function _checkCanvas(buf, width, height, webglContextVersion) +{ + for (var i = 0; i < width * height; i++) { + if (buf[i * 4] != 255 || buf[i * 4 + 1] != 255 || + buf[i * 4 + 2] != 0 || buf[i * 4 + 3] != 255) { + testFailed("OffscreenCanvas." + webglContextVersion + + ": This pixel should be [255, 255, 0, 255], but it is: [" + buf[i * 4] + ", " + + buf[i * 4 + 1] + ", " + buf[i * 4 + 2] + ", " + buf[i * 4 + 3] + "]."); + return; + } + } + testPassed("TransferToImageBitmap test on OffscreenCanvas." + webglContextVersion + " passed"); +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/out-of-bounds-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/out-of-bounds-test.js new file mode 100644 index 0000000000..75e3496dfb --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/out-of-bounds-test.js @@ -0,0 +1,321 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; + +var OutOfBoundsTest = (function() { + +var runCommonInvalidValueTests = function(callTemplate, gl, wtu, ext) { + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: -1, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: -1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: -1, type: 'gl.UNSIGNED_BYTE', offset: 1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: 1, type: 'gl.UNSIGNED_BYTE', offset: -1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: '0xffffffff', type: 'gl.UNSIGNED_BYTE', offset: 0})); +}; + +var setupProgramAndBindVertexArray = function(gl, wtu) { + var program = wtu.loadStandardProgram(gl); + + gl.useProgram(program); + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.enableVertexAttribArray(0); + + return program; +}; + +var setupProgram2 = function(gl, wtu) { + var vshader = [ + 'attribute vec3 aOne;', + 'attribute vec2 aTwo;', + 'void main() {', + ' gl_Position = vec4(aOne, 1.0) + vec4(aTwo, 0.0, 1.0);', + '}' + ].join('\n'); + + var fshader = [ + 'void main() {', + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);', + '}' + ].join('\n'); + + var program = wtu.setupProgram(gl, [vshader, fshader], [ "aOne", "aTwo" ]); + if (!program) { + testFailed("failed to create test program"); + } + return program; +}; + +var runDrawArraysTest = function(callTemplate, gl, wtu, ext) { + var program = setupProgramAndBindVertexArray(gl, wtu); + + debug("Test empty buffer") + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ ]), gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 0, count: 1})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 0, count: 10000})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 1, count: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 100, count: 0})); + runCommonInvalidValueTests(callTemplate, gl, wtu, ext); + + debug("") + debug("Test buffer with 3 float vectors") + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 3})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 3, count: 2})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 0, count: 10000})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 100, count: 0})); + runCommonInvalidValueTests(callTemplate, gl, wtu, ext); + + debug("") + debug("Test buffer with interleaved (3+2) float vectors") + + setupProgram2(gl, wtu); + + var vbo = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + // enough for 9 vertices, so 3 triangles + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(9*5), gl.STATIC_DRAW); + + // bind first 3 elements, with a stride of 5 float elements + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 5*4, 0); + // bind 2 elements, starting after the first 3; same stride of 5 float elements + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 5*4, 3*4); + + gl.enableVertexAttribArray(0); + gl.enableVertexAttribArray(1); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9})); + + // negative values must generate INVALID_VALUE; they can never be valid + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: 0, count: -500})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: -200, count: 1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: -200, count: -500})); + + // 0xffffffff needs to convert to a 'long' IDL argument as -1, as per + // WebIDL 4.1.7. JS ToInt32(0xffffffff) == -1. Thus INVALID_VALUE. + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: 0, count: '0xffffffff'})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: '0xffffffff', count: '0xffffffff'})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: '0xffffffff', count: 1})); + + // values that could otherwise be valid but aren't due to bindings generate + // INVALID_OPERATION + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 0, count: 10000})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: '0x7fffffff', count: 1})); +}; + +var runDrawElementsTest = function(callTemplate, gl, wtu, ext) { + var program = setupProgramAndBindVertexArray(gl, wtu); + var contextVersion = wtu.getDefault3DContextVersion(); + + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + + + debug(''); + debug('Test null index buffer'); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: 0})); + + debug(''); + debug('Test empty index buffer'); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer()); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 10000, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 1, type: 'gl.UNSIGNED_BYTE', offset: 0})); + runCommonInvalidValueTests(callTemplate, gl, wtu, ext); + + debug(''); + debug('Test buffer with 3 byte indexes'); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([ 0, 1, 2 ]), gl.STATIC_DRAW); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 2})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 10000, type: 'gl.UNSIGNED_BYTE', offset: 0})); + runCommonInvalidValueTests(callTemplate, gl, wtu, ext); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: 4})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 10000, type: 'gl.UNSIGNED_BYTE', offset: 10000})); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, (new Uint8Array([ 3, 0, 1, 2 ])).subarray(1), gl.STATIC_DRAW)'); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, new Uint8Array([ 3, 0, 1]))'); + var indexValidationError = wtu.shouldGenerateGLError(gl, [gl.INVALID_OPERATION, gl.NO_ERROR], wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, (new Uint8Array([ 3, 0, 1, 2 ])).subarray(1))'); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: 0})); + + debug(''); + debug('Test buffer with interleaved (3+2) float vectors'); + + setupProgram2(gl, wtu); + + var vbo = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + // enough for 9 vertices, so 3 triangles + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(9*5), gl.STATIC_DRAW); + + // bind first 3 elements, with a stride of 5 float elements + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 5*4, 0); + // bind 2 elements, starting after the first 3; same stride of 5 float elements + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 5*4, 3*4); + + gl.enableVertexAttribArray(0); + gl.enableVertexAttribArray(1); + + var ebo = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo); + // For WebGL 2, PRIMITIVE_RESTART_FIXED_INDEX is always enabled. + // 0xffff will be handled as a primitive restart. + if (contextVersion <= 1) { + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( + [ 0, 1, 2, + 1, 2, 0, + 2, 0, 1, + 201, 202, 203, + 0x7fff, 0x7fff, 0x7fff, + 0xffff, 0xffff, 0xffff ]), + gl.STATIC_DRAW); + } else { + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( + [ 0, 1, 2, + 1, 2, 0, + 2, 0, 1, + 201, 202, 203, + 0x7fff, 0x7fff, 0x7fff, + 0xffff - 1, 0xffff - 1, 0xffff - 1 ]), + gl.STATIC_DRAW); + } + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 9, type: 'gl.UNSIGNED_SHORT', offset: 0})); + + + // invalid operation with indices that would be valid with correct bindings + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 9, type: 'gl.UNSIGNED_SHORT', offset: 1000})); + wtu.shouldGenerateGLError(gl, indexValidationError, wtu.replaceParams(callTemplate, {count: 12, type: 'gl.UNSIGNED_SHORT', offset: 0})); + wtu.shouldGenerateGLError(gl, indexValidationError, wtu.replaceParams(callTemplate, {count: 15, type: 'gl.UNSIGNED_SHORT', offset: 0})); + wtu.shouldGenerateGLError(gl, indexValidationError, wtu.replaceParams(callTemplate, {count: 18, type: 'gl.UNSIGNED_SHORT', offset: 0})); + wtu.shouldGenerateGLError(gl, indexValidationError, wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_SHORT', offset: 2*15})); + + // 0xffffffff needs to convert to a 'long' IDL argument as -1, as per + // WebIDL 4.1.7. JS ToInt32(0xffffffff) == -1. Thus INVALID_VALUE. + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: '0xffffffff', type: 'gl.UNSIGNED_SHORT', offset: 0})); + // offset is defined as GLintptr, which is long long in IDL (64-bit). + // 2^32 - 1 should not overflow, and only result in INVALID_OPERATION. + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 1, type: 'gl.UNSIGNED_SHORT', offset: '0xffffffff'})); + + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: '0x7fffffff', type: 'gl.UNSIGNED_SHORT', offset: 0})); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_SHORT', offset: 0})); + + // invalid operation with offset that's not a multiple of the type size + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 1})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 2})); + + // invalid operation if no buffer is bound to ELEMENT_ARRAY_BUFFER + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 0})); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo); + + debug(''); + debug('Test buffer setting attrib 0 to a buffer too small and disable it.'); + var smallVBO = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, smallVBO); + gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0x10); + gl.disableVertexAttribArray(0); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 2})); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 2})); +}; + +var runInstancedTest = function(callTemplate, gl, wtu, ext) { + setupProgram2(gl, wtu); + + // Initialize non-instanced attribute data. + // Enough for 9 vertices, so 3 triangles. + var vbo = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(9*3), gl.STATIC_DRAW); + + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + + // Setup buffer for instanced attribute data. + var vbo2 = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo2); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); + + gl.enableVertexAttribArray(0); + gl.enableVertexAttribArray(1); + + debug('Test out-of-range instanced attributes'); + debug(''); + + debug('Test with an empty buffer for the instanced attribute'); + ext.vertexAttribDivisorANGLE(1, 1); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 10000, primcount: 0})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 1})); + + debug('Test with a buffer with 1 float for the instanced attribute'); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(1), gl.STATIC_DRAW); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 10000, primcount: 0})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 1})); + + debug(''); + debug('Test with a buffer with 2 floats for the instanced attribute'); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(2), gl.STATIC_DRAW); + debug('Divisor 1'); + ext.vertexAttribDivisorANGLE(1, 1); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 2})); + debug('Divisor 3'); + ext.vertexAttribDivisorANGLE(1, 3); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 3})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 4})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 10000})); + + debug(''); + debug('Test with a buffer with 4 floats for the instanced attribute'); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(4), gl.STATIC_DRAW); + debug('Divisor 1'); + ext.vertexAttribDivisorANGLE(1, 1); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 2})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 3})); + debug('Divisor 2'); + ext.vertexAttribDivisorANGLE(1, 2); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 4})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 5})); +}; + +var runDrawArraysInstancedTest = function(callTemplate, gl, wtu, ext) { + runInstancedTest(callTemplate, gl, wtu, ext); +}; + +var runDrawElementsInstancedTest = function(callTemplate, gl, wtu, ext) { + var ebo = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array( + [ 0, 1, 2, + 5, 4, 3, + 6, 7, 8 ]), + gl.STATIC_DRAW); + callTemplate = wtu.replaceParams(callTemplate, {type: 'gl.UNSIGNED_BYTE', offset: '$(offset)', count: '$(count)', primcount: '$(primcount)'}); + runInstancedTest(callTemplate, gl, wtu, ext); +}; + +return { + runDrawArraysTest: runDrawArraysTest, + runDrawArraysInstancedTest: runDrawArraysInstancedTest, + runDrawElementsTest: runDrawElementsTest, + runDrawElementsInstancedTest: runDrawElementsInstancedTest +}; + +})(); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ovr_multiview2_util.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ovr_multiview2_util.js new file mode 100644 index 0000000000..5de4dc88d8 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ovr_multiview2_util.js @@ -0,0 +1,263 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +"use strict"; + +function createTextureWithNearestFiltering(target) +{ + let texture = gl.createTexture(); + gl.bindTexture(target, texture); + gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture parameter setup should succeed"); + return texture; +} + +// Write a transformation matrix to elements of floatArray starting from index. +// The matrix transforms a unit square (-1 to 1) to a rectangle with the width scaleX and the left edge at offsetX. +function setupTranslateAndScaleXMatrix(floatArray, index, scaleX, offsetX) +{ + // x position is transformed according to this equation: scaleX * x0 + translateX = offsetX + // By substituting x0 with -1 (unit square x value for the left edge), we get the following: + let translateX = offsetX + scaleX; + + floatArray[index] = scaleX; + floatArray[index + 1] = 0.0; + floatArray[index + 2] = 0.0; + floatArray[index + 3] = 0.0; + + floatArray[index + 4] = 0.0; + floatArray[index + 5] = 1.0; + floatArray[index + 6] = 0.0; + floatArray[index + 7] = 0.0; + + floatArray[index + 8] = 0.0; + floatArray[index + 9] = 0.0; + floatArray[index + 10] = 1.0; + floatArray[index + 11] = 0.0; + + floatArray[index + 12] = translateX; + floatArray[index + 13] = 0.0; + floatArray[index + 14] = 0.0; + floatArray[index + 15] = 1.0; +} + +// Check the currently bound read framebuffer with dimensions <width> x <height>. +// The framebuffer should be divided into <strips> equally wide vertical strips, with the one indicated by +// <coloredStripIndex> colored with <expectedStripColor>. The rest of the framebuffer should be colored transparent black. +// A two pixel wide region at each edge of the colored region is left unchecked to allow for some tolerance for rasterization. +function checkVerticalStrip(width, height, strips, coloredStripIndex, expectedStripColor, framebufferDescription) +{ + let colorRegionLeftEdge = (width / strips) * coloredStripIndex; + let colorRegionRightEdge = (width / strips) * (coloredStripIndex + 1); + if (coloredStripIndex > 0) { + wtu.checkCanvasRect(gl, 0, 0, colorRegionLeftEdge - 1, height, [0, 0, 0, 0], 'the left edge of ' + framebufferDescription + ' should be untouched'); + } + if (coloredStripIndex < strips - 1) { + wtu.checkCanvasRect(gl, colorRegionRightEdge + 1, 0, width - colorRegionRightEdge - 1, height, [0, 0, 0, 0], 'the right edge of ' + framebufferDescription + ' should be untouched'); + } + wtu.checkCanvasRect(gl, colorRegionLeftEdge + 1, 0, colorRegionRightEdge - colorRegionLeftEdge - 2, height, expectedStripColor, 'a thin strip in ' + framebufferDescription + ' should be colored ' + expectedStripColor); +} + +function getMultiviewPassthroughVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + + 'void main() {', + ' gl_Position = a_position;', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +// This shader splits the viewport into <views> equally sized vertical strips. +// The input quad defined by "a_position" is transformed to fill a different +// strip in each view. +function getMultiviewOffsetVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + + 'void main() {', + ' vec4 pos = a_position;', + " // Transform the quad to a thin vertical strip that's offset along the x axis according to the view id.", + ' pos.x = (pos.x * 0.5 + 0.5 + float(gl_ViewID_OVR)) * 2.0 / $(num_views).0 - 1.0;', + ' gl_Position = pos;', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +// This shader transforms the incoming "a_position" with transforms for each +// view given in the uniform array "transform". +function getMultiviewRealisticUseCaseVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'uniform mat4 transform[$(num_views)];', + 'in vec4 a_position;', + + 'void main() {', + " // Transform the quad with the transformation matrix chosen according to gl_ViewID_OVR.", + ' vec4 pos = transform[gl_ViewID_OVR] * a_position;', + ' gl_Position = pos;', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +function getMultiviewColorFragmentShader() { + return ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'out vec4 my_FragColor;', + + 'void main() {', + ' uint mask = gl_ViewID_OVR + 1u;', + ' my_FragColor = vec4(((mask & 4u) != 0u) ? 1.0 : 0.0,', + ' ((mask & 2u) != 0u) ? 1.0 : 0.0,', + ' ((mask & 1u) != 0u) ? 1.0 : 0.0,', + ' 1.0);', + '}'].join('\n'); +} + +function getMultiviewColorFragmentShaderForDrawBuffers(drawBuffers) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'out vec4 my_FragColor[$(drawBuffers)];', + + 'void main() {', + ' uint mask;']; + + for (let i = 0; i < drawBuffers; ++i) { + shaderCode.push(wtu.replaceParams(' mask = gl_ViewID_OVR + $(i)u;', {'i': i + 1})); + shaderCode.push(wtu.replaceParams(' my_FragColor[$(i)] = vec4(((mask & 4u) != 0u) ? 1.0 : 0.0,', {'i': i})); + shaderCode.push(' ((mask & 2u) != 0u) ? 1.0 : 0.0,'); + shaderCode.push(' ((mask & 1u) != 0u) ? 1.0 : 0.0,'); + shaderCode.push(' 1.0);'); + } + shaderCode.push('}'); + shaderCode = shaderCode.join('\n'); + return wtu.replaceParams(shaderCode, {'drawBuffers' : drawBuffers}); +} + +function getMultiviewVaryingVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + 'out float testVarying;', + + 'void main() {', + ' gl_Position = a_position;', + ' testVarying = float(gl_ViewID_OVR);', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +function getMultiviewVaryingFragmentShader() { + return ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'in float testVarying;', + 'out vec4 my_FragColor;', + + 'void main() {', + ' int mask = int(testVarying + 0.1) + 1;', + ' my_FragColor = vec4(((mask & 4) != 0) ? 1.0 : 0.0,', + ' ((mask & 2) != 0) ? 1.0 : 0.0,', + ' ((mask & 1) != 0) ? 1.0 : 0.0,', + ' 1.0);', + '}'].join('\n'); +} + +function getMultiviewFlatVaryingVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + 'flat out int testVarying;', + + 'void main() {', + ' gl_Position = a_position;', + ' testVarying = int(gl_ViewID_OVR);', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +function getMultiviewFlatVaryingFragmentShader() { + return ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'flat in int testVarying;', + 'out vec4 my_FragColor;', + + 'void main() {', + ' int mask = testVarying + 1;', + ' my_FragColor = vec4(((mask & 4) != 0) ? 1.0 : 0.0,', + ' ((mask & 2) != 0) ? 1.0 : 0.0,', + ' ((mask & 1) != 0) ? 1.0 : 0.0,', + ' 1.0);', + '}'].join('\n'); +} + +function getMultiviewInstancedVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + 'out vec4 color;', + + 'void main() {', + ' vec4 pos = a_position;', + " // Transform the quad to a thin vertical strip that's offset along the x axis according to the view id and instance id.", + ' pos.x = (pos.x * 0.5 + 0.5 + float(gl_ViewID_OVR) + float(gl_InstanceID)) * 2.0 / ($(num_views).0 * 2.0) - 1.0;', + ' int mask = gl_InstanceID + 1;', + ' color = vec4(((mask & 4) != 0) ? 1.0 : 0.0,', + ' ((mask & 2) != 0) ? 1.0 : 0.0,', + ' ((mask & 1) != 0) ? 1.0 : 0.0,', + ' 1.0);', + ' gl_Position = pos;', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +function getInstanceColorFragmentShader() { + return ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'in vec4 color;', + 'out vec4 my_FragColor;', + + 'void main() {', + ' my_FragColor = color;', + '}'].join('\n'); +} + +function getExpectedColor(view) { + var mask = (view + 1); + return [(mask & 4) ? 255 : 0, (mask & 2) ? 255 : 0, (mask & 1) ? 255 : 0, 255]; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/shader-with-non-reserved-words.js b/dom/canvas/test/webgl-conf/checkout/js/tests/shader-with-non-reserved-words.js new file mode 100644 index 0000000000..561c1385af --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/shader-with-non-reserved-words.js @@ -0,0 +1,664 @@ +/* +Copyright (c) 2022 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +async function testNonReservedWords(part, numParts) { + + description(`test shaders using reserved words as identifiers compile ${part} of ${numParts}`); + + const DXWords = [ + "Buffer", + "double", + "uint", + "half", + "dword", + "string", + "texture", + "pixelshader", + "vertexshader", + "switch", + "min16float", + "min10float", + "min16int", + "min12int", + "min16uint", + "vector", + "matrix", + "float2", + "float3", + "float4", + "float1x1", + "float1x2", + "float1x3", + "float1x4", + "float2x1", + "float2x2", + "float2x3", + "float2x4", + "float3x1", + "float3x2", + "float3x3", + "float3x4", + "float4x1", + "float4x2", + "float4x3", + "float4x4", + "int1x1", + "int1x2", + "int1x3", + "int1x4", + "int2x1", + "int2x2", + "int2x3", + "int2x4", + "int3x1", + "int3x2", + "int3x3", + "int3x4", + "int4x1", + "int4x2", + "int4x3", + "int4x4", + "double1x1", + "double1x2", + "double1x3", + "double1x4", + "double2x1", + "double2x2", + "double2x3", + "double2x4", + "double3x1", + "double3x2", + "double3x3", + "double3x4", + "double4x1", + "double4x2", + "double4x3", + "double4x4", + "abort", + "abs", + "acos", + "all", + "AllMemoryBarrier", + "AllMemoryBarrierWithGroupSync", + "any", + "asdouble", + "asfloat", + "asin", + "asint", + "asint", + "asuint", + "asuint", + "atan", + "atan2", + "ceil", + "clamp", + "clip", + "cos", + "cosh", + "countbits", + "cross", + "D3DCOLORtoUBYTE4", + "ddx", + "ddx_coarse", + "ddx_fine", + "ddy", + "ddy_coarse", + "ddy_fine", + "degrees", + "determinant", + "DeviceMemoryBarrier", + "DeviceMemoryBarrierWithGroupSync", + "distance", + "dot", + "dst", + "errorf", + "EvaluateAttributeAtCentroid", + "EvaluateAttributeAtSample", + "EvaluateAttributeSnapped", + "exp", + "exp2", + "f16tof32", + "f32tof16", + "faceforward", + "firstbithigh", + "firstbitlow", + "floor", + "fma", + "fmod", + "frac", + "frexp", + "fwidth", + "GetRenderTargetSampleCount", + "GetRenderTargetSamplePosition", + "GroupMemoryBarrier", + "GroupMemoryBarrierWithGroupSync", + "InterlockedAdd", + "InterlockedAnd", + "InterlockedCompareExchange", + "InterlockedCompareStore", + "InterlockedExchange", + "InterlockedMax", + "InterlockedMin", + "InterlockedOr", + "InterlockedXor", + "isfinite", + "isinf", + "isnan", + "ldexp", + "length", + "lerp", + "lit", + "log", + "log10", + "log2", + "mad", + "max", + "min", + "modf", + "msad4", + "mul", + "noise", + "normalize", + "pow", + "printf", + "Process2DQuadTessFactorsAvg", + "Process2DQuadTessFactorsMax", + "Process2DQuadTessFactorsMin", + "ProcessIsolineTessFactors", + "ProcessQuadTessFactorsAvg", + "ProcessQuadTessFactorsMax", + "ProcessQuadTessFactorsMin", + "ProcessTriTessFactorsAvg", + "ProcessTriTessFactorsMax", + "ProcessTriTessFactorsMin", + "radians", + "rcp", + "reflect", + "refract", + "reversebits", + "round", + "rsqrt", + "saturate", + "sign", + "sin", + "sincos", + "sinh", + "smoothstep", + "sqrt", + "step", + "tan", + "tanh", + "tex1D", + "tex1D", + "tex1Dbias", + "tex1Dgrad", + "tex1Dlod", + "tex1Dproj", + "tex2D", + "tex2D", + "tex2Dbias", + "tex2Dgrad", + "tex2Dlod", + "tex2Dproj", + "tex3D", + "tex3D", + "tex3Dbias", + "tex3Dgrad", + "tex3Dlod", + "tex3Dproj", + "texCUBE", + "texCUBE", + "texCUBEbias", + "texCUBEgrad", + "texCUBElod", + "texCUBEproj", + "transpose", + "trunc" + ]; + + const GLSL_4_20_11_words = [ + "attribute", + "const", + "uniform", + "varying", + "coherent", + "volatile", + "restrict", + "readonly", + "writeonly", + "atomic_uint", + "layout", + "centroid", + "flat", + "smooth", + "noperspective", + "patch", + "sample", + "break", + "continue", + "do", + "for", + "while", + "switch", + "case", + "default", + "if", + "else", + "subroutine", + "in", + "out", + "inout", + "float", + "double", + "int", + "void", + "bool", + "true", + "false", + "invariant", + "discard", + "return", + "mat2", + "mat3", + "mat4", + "dmat2", + "dmat3", + "dmat4", + "mat2x2", + "mat2x3", + "mat2x4", + "dmat2x2", + "dmat2x3", + "dmat2x4", + "mat3x2", + "mat3x3", + "mat3x4", + "dmat3x2", + "dmat3x3", + "dmat3x4", + "mat4x2", + "mat4x3", + "mat4x4", + "dmat4x2", + "dmat4x3", + "dmat4x4", + "vec2", + "vec3", + "vec4", + "ivec2", + "ivec3", + "ivec4", + "bvec2", + "bvec3", + "bvec4", + "dvec2", + "dvec3", + "dvec4", + "uint", + "uvec2", + "uvec3", + "uvec4", + "lowp", + "mediump", + "highp", + "precision", + "sampler1D", + "sampler2D", + "sampler3D", + "samplerCube", + "sampler1DShadow", + "sampler2DShadow", + "samplerCubeShadow", + "sampler1DArray", + "sampler2DArray", + "sampler1DArrayShadow", + "sampler2DArrayShadow", + "isampler1D", + "isampler2D", + "isampler3D", + "isamplerCube", + "isampler1DArray", + "isampler2DArray", + "usampler1D", + "usampler2D", + "usampler3D", + "usamplerCube", + "usampler1DArray", + "usampler2DArray", + "sampler2DRect", + "sampler2DRectShadow", + "isampler2DRect", + "usampler2DRect", + "samplerBuffer", + "isamplerBuffer", + "usamplerBuffer", + "sampler2DMS", + "isampler2DMS", + "usampler2DMS", + "sampler2DMSArray", + "isampler2DMSArray", + "usampler2DMSArray", + "samplerCubeArray", + "samplerCubeArrayShadow", + "isamplerCubeArray", + "usamplerCubeArray", + "image1D", + "iimage1D", + "uimage1D", + "image2D", + "iimage2D", + "uimage2D", + "image3D", + "iimage3D", + "uimage3D", + "image2DRect", + "iimage2DRect", + "uimage2DRect", + "imageCube", + "iimageCube", + "uimageCube", + "imageBuffer", + "iimageBuffer", + "uimageBuffer", + "image1DArray", + "iimage1DArray", + "uimage1DArray", + "image2DArray", + "iimage2DArray", + "uimage2DArray", + "imageCubeArray", + "iimageCubeArray", + "uimageCubeArray", + "image2DMS", + "iimage2DMS", + "uimage2DMS", + "image2DMSArray", + "iimage2DMSArray", + "uimage2DMSArray", + "struct" + ]; + + const GLSL_4_20_11_future_words = [ + "common", + "partition", + "active", + "asm", + "class", + "union", + "enum", + "typedef", + "template", + "this", + "packed", + "resource", + "goto", + "inline", + "noinline", + "public", + "static", + "extern", + "external", + "interface", + "long", + "short", + "half", + "fixed", + "unsigned", + "superp", + "input", + "output", + "hvec2", + "hvec3", + "hvec4", + "fvec2", + "fvec3", + "fvec4", + "sampler3DRect", + "filter", + "sizeof", + "cast", + "namespace", + "using", + "row_major" + ]; + + const GLSL_1_0_17_words = [ + "attribute", + "const", + "uniform", + "varying", + "break", + "continue", + "do", + "for", + "while", + "if", + "else", + "in", + "out", + "inout", + "float", + "int", + "void", + "bool", + "true", + "false", + "lowp", + "mediump", + "highp", + "precision", + "invariant", + "discard", + "return", + "mat2", + "mat3", + "mat4", + "vec2", + "vec3", + "vec4", + "ivec2", + "ivec3", + "ivec4", + "bvec2", + "bvec3", + "bvec4", + "sampler2D", + "samplerCube", + "struct" + ] + + const GLSL_1_0_17_FutureWords = [ + "asm", + "class", + "union", + "enum", + "typedef", + "template", + "this", + "packed", + "goto", + "switch", + "default", + "inline", + "noinline", + "volatile", + "public", + "static", + "extern", + "external", + "interface", + "flat", + "long", + "short", + "double", + "half", + "fixed", + "unsigned", + "superp", + "input", + "output", + "hvec2", + "hvec3", + "hvec4", + "dvec2", + "dvec3", + "dvec4", + "fvec2", + "fvec3", + "fvec4", + "sampler1D", + "sampler3D", + "sampler1DShadow", + "sampler2DShadow", + "sampler2DRect", + "sampler3DRect", + "sampler2DRectShadow", + "sizeof", + "cast", + "namespace", + "using" + ]; + + const allBadWords = [ + ...DXWords, + ...GLSL_4_20_11_words, + ...GLSL_4_20_11_future_words, + ]; + const numWordsPerPart = Math.ceil(allBadWords.length / numParts); + const firstWordNdx = numWordsPerPart * (part - 1); + const badWords = allBadWords.slice(firstWordNdx, firstWordNdx + numWordsPerPart); + debug(`running tests for words ${firstWordNdx} to ${firstWordNdx + badWords.length - 1} of ${allBadWords.length}`); + + const shaders = { + vertexShader0: ` +struct $replaceMe { + vec4 $replaceMe; +}; +struct Foo { + $replaceMe $replaceMe; +}; +attribute vec4 position; +void main() +{ + Foo f; + f.$replaceMe.$replaceMe = position; + gl_Position = f.$replaceMe.$replaceMe; +} +`, + fragmentShader0: ` +precision mediump float; +vec4 $replaceMe() { + return vec4(0,1,0,1); +} +void main() +{ + gl_FragColor = $replaceMe(); +} +`, + vertexShader1: ` +attribute vec4 $replaceMe; +void main() +{ + gl_Position = $replaceMe; +} +`, + fragmentShader1: ` +precision mediump float; +vec4 foo(vec4 $replaceMe) { + return $replaceMe; +} +void main() +{ + gl_FragColor = foo(vec4(1,0,1,1)); +} +`, + vertexShader2: ` +varying vec4 $replaceMe; +attribute vec4 position; +void main() +{ + gl_Position = position; + $replaceMe = position; +} +`, + fragmentShader2: ` +precision mediump float; +varying vec4 $replaceMe; +void main() +{ + gl_FragColor = $replaceMe; +} +`, + vertexShader3: ` +attribute vec4 position; +void main() +{ + gl_Position = position; +} +`, + fragmentShader3: ` +precision mediump float; +uniform vec4 $replaceMe; +void main() +{ + gl_FragColor = $replaceMe; +} +`, + }; + + const wtu = WebGLTestUtils; + const gl = wtu.create3DContext(); + const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); + + const reservedWords = new Set([ + ...GLSL_1_0_17_words, + ...GLSL_1_0_17_FutureWords, + ]); + + const checkedWords = new Set(); + + const src = []; + for (let ii = 0; ii < 4; ++ii) { + const vSrc = shaders[`vertexShader${ii}`]; + const fSrc = shaders[`fragmentShader${ii}`]; + src.push({vSrc: vSrc, fSrc: fSrc}); + } + + for (const badWord of badWords) { + testWord(badWord); + await wait(); + } + finishTest(); + + function testWord(word) { + if (reservedWords.has(word) || checkedWords.has(word)) { + return; + } + checkedWords.add(word); + debug(""); + debug(`testing: ${word}`); + + for (let ii = 0; ii < src.length; ++ii) { + const vs = src[ii].vSrc.replace(/\$replaceMe/g, word); + const fs = src[ii].fSrc.replace(/\$replaceMe/g, word); + + let success = true; + const program = wtu.loadProgram(gl, vs, fs, function(msg) { + debug(msg); + success = false; + }, true); + if (success) { + testPassed(`shader with: '${word}' compiled`); + } else { + testFailed(`shader with: '${word}' failed to compile`); + } + if (program) { + gl.deleteProgram(program); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no GL errors"); + } + } +}
\ No newline at end of file diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas-sub-rectangle.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas-sub-rectangle.js new file mode 100644 index 0000000000..792f832451 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas-sub-rectangle.js @@ -0,0 +1,292 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var realRedColor = [255, 0, 0]; + var realGreenColor = [0, 255, 0]; + var realBlueColor = [0, 0, 255]; + var realCyanColor = [0, 255, 255]; + var redColor = realRedColor; + var greenColor = realGreenColor; + var blueColor = realBlueColor; + var cyanColor = realCyanColor; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking a sub-rectangle of a canvas (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + + // The sub-rectangle tests only apply to WebGL 2.0 for the + // time being, though the tests for the WebGL 1.0 + // format/internal format/type combinations are generated into + // conformance/textures/. + if (wtu.getDefault3DContextVersion() < 2) { + debug('Test only applies to WebGL 2.0'); + finishTest(); + return; + } + + gl = wtu.create3DContext("example", { preserveDrawingBuffer: true }); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + case gl.RG: + case gl.RG_INTEGER: + blueColor = [0, 0, 0]; + cyanColor = [0, 255, 0]; + break; + + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var canvas2d = document.createElement('canvas'); + runTest(canvas2d, setupSourceCanvas2D, '2D-rendered canvas'); + + var canvasWebGL = document.createElement('canvas'); + runTest(canvasWebGL, setupSourceCanvasWebGL, 'WebGL-rendered canvas'); + + finishTest(); + } + + function fillStyle2D(ctx, color) { + ctx.fillStyle = 'rgb(' + color[0] + ', ' + color[1] + ', ' + color[2] + ')'; + } + + function setupSourceCanvas2D(canvas) { + var width = canvas.width; + var height = canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + + var ctx = canvas.getContext('2d'); + // Always use the same pattern for this test: four quadrants: + // red green + // blue cyan + // Handle odd-sized canvases + fillStyle2D(ctx, realRedColor); + ctx.fillRect(0, 0, halfWidth, halfHeight); + fillStyle2D(ctx, realGreenColor); + ctx.fillRect(halfWidth, 0, width - halfWidth, halfHeight); + fillStyle2D(ctx, realBlueColor); + ctx.fillRect(0, halfHeight, halfWidth, height - halfHeight); + fillStyle2D(ctx, realCyanColor); + ctx.fillRect(halfWidth, halfHeight, width - halfWidth, height - halfHeight); + } + + function clearColorWebGL(ctx, color) { + ctx.clearColor(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT); + } + + function setupSourceCanvasWebGL(canvas) { + var width = canvas.width; + var height = canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + + var ctx = canvas.getContext('webgl'); + // Always use the same pattern for this test: four quadrants: + // red green + // blue cyan + // Handle odd-sized canvases + + ctx.viewport(0, 0, width, height); + ctx.enable(ctx.SCISSOR_TEST); + // OpenGL origin is lower-left + ctx.scissor(0, 0, halfWidth, halfHeight); + clearColorWebGL(ctx, realBlueColor); + ctx.scissor(halfWidth, 0, width - halfWidth, halfHeight); + clearColorWebGL(ctx, realCyanColor); + ctx.scissor(0, halfHeight, halfWidth, height - halfHeight); + clearColorWebGL(ctx, realRedColor); + ctx.scissor(halfWidth, halfHeight, width - halfWidth, height - halfHeight); + clearColorWebGL(ctx, realGreenColor); + } + + function runOneIteration(sourceDescription, useTexSubImage2D, flipY, + canvas, canvasSize, canvasSetupFunction, + sourceSubRectangle, expected, + bindingTarget, program) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ', sourceSubRectangle=' + sourceSubRectangle; + } + debug(''); + debug('Testing ' + sourceDescription + ' with ' + + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ', flipY=' + flipY + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + // Initialize the contents of the source canvas. + var width = canvasSize[0]; + var height = canvasSize[1]; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + canvas.width = width; + canvas.height = height; + canvasSetupFunction(canvas); + + // Upload the source canvas to the texture and draw it to a quad. + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // In this test, this is always specified. It's currently WebGL 2.0-specific. + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + // Upload the image into the texture + var uploadWidth = sourceSubRectangle[2]; + var uploadHeight = sourceSubRectangle[3]; + for (var tt = 0; tt < targets.length; ++tt) { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], + uploadWidth, uploadHeight, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + uploadWidth, uploadHeight, + gl[pixelFormat], gl[pixelType], canvas); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + uploadWidth, uploadHeight, 0, + gl[pixelFormat], gl[pixelType], canvas); + } + } + + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + + // The tests are constructed to upload a single solid color + // out of the canvas. + var outputCanvasWidth = gl.drawingBufferWidth; + var outputCanvasHeight = gl.drawingBufferHeight; + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + var msg = 'should be ' + expected; + wtu.checkCanvasRect(gl, 0, 0, outputCanvasWidth, outputCanvasHeight, expected, msg); + } + } + + function runTest(canvas, canvasSetupFunction, sourceDescription) + { + var program = tiu.setupTexturedQuad(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_2D, program, canvas, canvasSetupFunction, sourceDescription); + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_CUBE_MAP, program, canvas, canvasSetupFunction, sourceDescription); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + } + + function runTestOnBindingTarget(bindingTarget, program, canvas, canvasSetupFunction, sourceDescription) { + var cases = [ + // Small canvas cases. Expected that these won't be + // GPU-accelerated in most browsers' implementations. + { expected: redColor, flipY: false, size: [2, 2], subRect: [0, 0, 1, 1] }, + { expected: greenColor, flipY: false, size: [2, 2], subRect: [1, 0, 1, 1] }, + { expected: blueColor, flipY: false, size: [2, 2], subRect: [0, 1, 1, 1] }, + { expected: cyanColor, flipY: false, size: [2, 2], subRect: [1, 1, 1, 1] }, + { expected: redColor, flipY: true, size: [2, 2], subRect: [0, 1, 1, 1] }, + { expected: greenColor, flipY: true, size: [2, 2], subRect: [1, 1, 1, 1] }, + { expected: blueColor, flipY: true, size: [2, 2], subRect: [0, 0, 1, 1] }, + { expected: cyanColor, flipY: true, size: [2, 2], subRect: [1, 0, 1, 1] }, + + // Larger canvas cases. Expected that these will be + // GPU-accelerated in most browsers' implementations. + // Changes will be gladly accepted to trigger more + // browsers' heuristics to accelerate these canvases. + { expected: redColor, flipY: false, size: [384, 384], subRect: [ 0, 0, 192, 192] }, + { expected: greenColor, flipY: false, size: [384, 384], subRect: [192, 0, 192, 192] }, + { expected: blueColor, flipY: false, size: [384, 384], subRect: [ 0, 192, 192, 192] }, + { expected: cyanColor, flipY: false, size: [384, 384], subRect: [192, 192, 192, 192] }, + { expected: blueColor, flipY: true, size: [384, 384], subRect: [ 0, 0, 192, 192] }, + { expected: cyanColor, flipY: true, size: [384, 384], subRect: [192, 0, 192, 192] }, + { expected: redColor, flipY: true, size: [384, 384], subRect: [ 0, 192, 192, 192] }, + { expected: greenColor, flipY: true, size: [384, 384], subRect: [192, 192, 192, 192] }, + + ]; + + for (var i in cases) { + runOneIteration(sourceDescription, false, cases[i].flipY, + canvas, cases[i].size, canvasSetupFunction, + cases[i].subRect, + cases[i].expected, bindingTarget, program); + runOneIteration(sourceDescription, true, cases[i].flipY, + canvas, cases[i].size, canvasSetupFunction, + cases[i].subRect, + cases[i].expected, bindingTarget, program); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas.js new file mode 100644 index 0000000000..a96eeb9de0 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas.js @@ -0,0 +1,468 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var whiteColor = [255, 255, 255, 255]; + var redColor = [255, 0, 0, 255]; + var greenColor = [0, 255, 0, 255]; + var semiTransparentRedColor = [127, 0, 0, 127]; + var semiTransparentGreenColor = [0, 127, 0, 127]; + var repeatCount; + + function replicateRedChannel(color) + { + color[1] = color[0]; + color[2] = color[0]; + } + + function zapColorChannels(color) + { + color[0] = 0; + color[1] = 0; + color[2] = 0; + } + + function setAlphaChannelTo1(color) + { + color[3] = 255; + } + + function replicateAllRedChannels() + { + replicateRedChannel(redColor); + replicateRedChannel(semiTransparentRedColor); + replicateRedChannel(greenColor); + replicateRedChannel(semiTransparentGreenColor); + } + + function setAllAlphaChannelsTo1() + { + setAlphaChannelTo1(redColor); + setAlphaChannelTo1(semiTransparentRedColor); + setAlphaChannelTo1(greenColor); + setAlphaChannelTo1(semiTransparentGreenColor); + } + + function repeatCountForTextureFormat(internalFormat, pixelFormat, pixelType) + { + // There were bugs in early WebGL 1.0 implementations when repeatedly uploading canvas + // elements into textures. In response, this test was changed into a regression test by + // repeating all of the cases multiple times. Unfortunately, this means that adding a new + // case above significantly increases the run time of the test suite. The problem is made + // even worse by the addition of many more texture formats in WebGL 2.0. + // + // Doing repeated runs with just a couple of WebGL 1.0's supported texture formats acts as a + // sufficient regression test for the old bugs. For this reason the test has been changed to + // only repeat for those texture formats. + if ((internalFormat == 'RGBA' && pixelFormat == 'RGBA' && pixelType == 'UNSIGNED_BYTE') || + (internalFormat == 'RGB' && pixelFormat == 'RGB' && pixelType == 'UNSIGNED_BYTE')) { + return 4; + } + + return 1; + } + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking canvas elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + repeatCount = repeatCountForTextureFormat(internalFormat, pixelFormat, pixelType); + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + // Zap green and blue channels. + whiteColor[1] = 0; + whiteColor[2] = 0; + greenColor[1] = 0; + semiTransparentGreenColor[1] = 0; + // Alpha channel is 1.0. + setAllAlphaChannelsTo1(); + break; + case gl.RG: + case gl.RG_INTEGER: + // Zap blue channel. + whiteColor[2] = 0; + // Alpha channel is 1.0. + setAllAlphaChannelsTo1(); + break; + case gl.LUMINANCE: + // Replicate red channels. + replicateAllRedChannels(); + // Alpha channel is 1.0. + setAllAlphaChannelsTo1(); + break; + case gl.ALPHA: + // Red, green and blue channels are all 0.0. + zapColorChannels(redColor); + zapColorChannels(semiTransparentRedColor); + zapColorChannels(greenColor); + zapColorChannels(semiTransparentGreenColor); + zapColorChannels(whiteColor); + break; + case gl.LUMINANCE_ALPHA: + // Replicate red channels. + replicateAllRedChannels(); + break; + case gl.RGB: + case gl.RGB_INTEGER: + // Alpha channel is 1.0. + setAllAlphaChannelsTo1(); + break; + default: + break; + } + + switch (gl[internalFormat]) { + case gl.SRGB8: + case gl.SRGB8_ALPHA8: + semiTransparentRedColor = wtu.sRGBToLinear(semiTransparentRedColor); + semiTransparentGreenColor = wtu.sRGBToLinear(semiTransparentGreenColor); + break; + case gl.RGBA8UI: + // For int and uint textures, TexImageUtils outputs the maximum value (in this case, + // 255) for the alpha channel all the time because of differences in behavior when + // sampling integer textures with and without alpha channels. Since changing this + // behavior may have large impact across the test suite, leave it as is for now. + setAllAlphaChannelsTo1(); + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.clearRect(0, 0, width, height); + ctx.fillStyle = "#ff0000"; + ctx.fillRect(0, 0, width, halfHeight); + ctx.fillStyle = "#00ff00"; + ctx.fillRect(0, halfHeight, width, height - halfHeight); + } + + function setCanvasToSemiTransparentRedGreen(ctx) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.clearRect(0, 0, width, height); + ctx.fillStyle = "rgba(127, 0, 0, 0.5)"; + ctx.fillRect(0, 0, width, halfHeight); + ctx.fillStyle = "rgba(0, 127, 0, 0.5)"; + ctx.fillRect(0, halfHeight, width, height - halfHeight); + } + + function drawTextInCanvas(ctx, bindingTarget) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + ctx.fillStyle = "#ffffff"; + ctx.fillRect(0, 0, width, height); + ctx.font = '20pt Arial'; + ctx.fillStyle = 'black'; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText("1234567890", width / 2, height / 4); + } + + function setCanvasTo257x257(ctx, bindingTarget) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + function setCanvasTo257x257SemiTransparent(ctx, bindingTarget) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToSemiTransparentRedGreen(ctx); + } + + function setCanvasToMin(ctx, bindingTarget) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + ctx.canvas.width = 2; + } else { + ctx.canvas.width = 1; + } + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function setCanvasToMinSemiTransparent(ctx, bindingTarget) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + ctx.canvas.width = 2; + } else { + ctx.canvas.width = 1; + } + ctx.canvas.height = 2; + setCanvasToSemiTransparentRedGreen(ctx); + } + + function runOneIteration(canvas, useTexSubImage2D, flipY, semiTransparent, program, bindingTarget, opt_texture, opt_fontTest) + { + var objType = 'canvas'; + if (canvas.transferToImageBitmap) + objType = 'OffscreenCanvas'; + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + ' canvas size: ' + canvas.width + 'x' + canvas.height + + ' source object type: ' + objType + + (opt_fontTest ? " with fonts" : " with" + (semiTransparent ? " semi-transparent" : "") + " red-green")); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (!opt_texture) { + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + var texture = opt_texture; + } + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + // Initialize the texture to black first + if (useTexSubImage2D) { + gl.texImage2D(targets[tt], 0, gl[internalFormat], canvas.width, canvas.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], canvas); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], canvas); + } + } + + var width = gl.canvas.width; + var height = gl.canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + var top = flipY ? 0 : (height - halfHeight); + var bottom = flipY ? (height - halfHeight) : 0; + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); + + if (opt_fontTest) { + // check half is a solid color. + wtu.checkCanvasRect( + gl, 0, top, width, halfHeight, + whiteColor, + "should be white"); + // check other half is not a solid color. + wtu.checkCanvasRectColor( + gl, 0, bottom, width, halfHeight, + whiteColor, 0, + function() { + testFailed("font missing"); + }, + function() { + testPassed("font rendered"); + }, + debug); + } else { + var localRed = semiTransparent ? semiTransparentRedColor : redColor; + var localGreen = semiTransparent ? semiTransparentGreenColor : greenColor; + + // Allow a tolerance for premultiplication/unmultiplication, especially for texture + // formats with lower bit depths. + var tolerance = 0; + if (semiTransparent) { + tolerance = 3; + if (pixelType == 'UNSIGNED_SHORT_5_6_5' || internalFormat == 'RGB565') { + tolerance = 6; + } else if (pixelType == 'UNSIGNED_SHORT_4_4_4_4' || internalFormat == 'RGBA4') { + tolerance = 9; + } else if (pixelType == 'UNSIGNED_SHORT_5_5_5_1' || internalFormat == 'RGB5_A1') { + // Semi-transparent values are allowed to convert to either 1 or 0 for this + // single-bit alpha format per OpenGL ES 3.0.5 section 2.1.6.2, "Conversion + // from Floating-Point to Normalized Fixed-Point". Ignore alpha for these + // tests. + tolerance = 6; + localRed = localRed.slice(0, 3); + localGreen = localGreen.slice(0, 3); + } else if (internalFormat == 'RGB10_A2') { + // The alpha channel is too low-resolution for any meaningful comparisons. + localRed = localRed.slice(0, 3); + localGreen = localGreen.slice(0, 3); + } + } + + // Check the top and bottom halves and make sure they have the right color. + debug("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, 0, bottom, width, halfHeight, localRed, + "shouldBe " + localRed, tolerance); + debug("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, 0, top, width, halfHeight, localGreen, + "shouldBe " + localGreen, tolerance); + } + + if (!useTexSubImage2D && pixelFormat == "RGBA") { + if (pixelType == "FLOAT") { + // Attempt to set a pixel in the texture to ensure the texture was + // actually created with floats. Regression test for http://crbug.com/484968 + var pixels = new Float32Array([1000.0, 1000.0, 1000.0, 1000.0]); + gl.texSubImage2D(targets[tt], 0, 0, 0, 1, 1, gl[pixelFormat], gl[pixelType], pixels); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Texture should be backed by floats"); + } else if (pixelType == "HALF_FLOAT_OES" || pixelType == "HALF_FLOAT") { + // Attempt to set a pixel in the texture to ensure the texture was + // actually created with half-floats. Regression test for http://crbug.com/484968 + var halfFloatTenK = 0x70E2; // Half float 10000 + var pixels = new Uint16Array([halfFloatTenK, halfFloatTenK, halfFloatTenK, halfFloatTenK]); + gl.texSubImage2D(targets[tt], 0, 0, 0, 1, 1, gl[pixelFormat], gl[pixelType], pixels); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Texture should be backed by half-floats"); + } + } + } + + if (false) { + var m = wtu.makeImageFromCanvas(gl.canvas); + document.getElementById("console").appendChild(m); + document.getElementById("console").appendChild(document.createElement("hr")); + } + + return texture; + } + + function runTest() + { + var canvas = document.createElement('canvas'); + + var cases = [ + { canvas: canvas, sub: false, flipY: true, semiTransparent: false, font: false, init: setCanvasToMin }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: false, font: false }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: false, font: false }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: false, font: false }, + { canvas: canvas, sub: false, flipY: true, semiTransparent: true, font: false, init: setCanvasToMinSemiTransparent }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: true, font: false }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: true, font: false }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: true, font: false }, + { canvas: canvas, sub: false, flipY: true, semiTransparent: false, font: false, init: setCanvasTo257x257 }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: false, font: false }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: false, font: false }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: false, font: false }, + { canvas: canvas, sub: false, flipY: true, semiTransparent: true, font: false, init: setCanvasTo257x257SemiTransparent }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: true, font: false }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: true, font: false }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: true, font: false }, + ]; + + // The font tests don't work with ALPHA-only textures since they draw to the color channels. + if (internalFormat != 'ALPHA') { + cases = cases.concat([ + { canvas: canvas, sub: false, flipY: true, semiTransparent: false, font: true, init: drawTextInCanvas }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: false, font: true }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: false, font: true }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: false, font: true }, + ]); + } + + if (window.OffscreenCanvas) { + var offscreenCanvas = new OffscreenCanvas(1, 1); + cases = cases.concat([ + { canvas: offscreenCanvas, sub: false, flipY: true, semiTransparent: false, font: false, init: setCanvasToMin }, + { canvas: offscreenCanvas, sub: false, flipY: false, semiTransparent: false, font: false }, + { canvas: offscreenCanvas, sub: true, flipY: true, semiTransparent: false, font: false }, + { canvas: offscreenCanvas, sub: true, flipY: false, semiTransparent: false, font: false }, + { canvas: offscreenCanvas, sub: false, flipY: true, semiTransparent: true, font: false, init: setCanvasToMinSemiTransparent }, + { canvas: offscreenCanvas, sub: false, flipY: false, semiTransparent: true, font: false }, + { canvas: offscreenCanvas, sub: true, flipY: true, semiTransparent: true, font: false }, + { canvas: offscreenCanvas, sub: true, flipY: false, semiTransparent: true, font: false }, + ]); + } + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_2D) { + program = tiu.setupTexturedQuad(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var count = repeatCount; + var caseNdx = 0; + var texture = undefined; + function runNextTest() { + var c = cases[caseNdx]; + var imageDataBefore = null; + if (c.init) { + c.init(c.canvas.getContext('2d'), bindingTarget); + } + texture = runOneIteration(c.canvas, c.sub, c.flipY, c.semiTransparent, program, bindingTarget, texture, c.font); + // for the first 2 iterations always make a new texture. + if (count < 2) { + gl.deleteTexture(texture); + texture = undefined; + } + ++caseNdx; + if (caseNdx == cases.length) { + caseNdx = 0; + --count; + if (!count) { + resolve("SUCCESS"); + return; + } + } + // While we are working with Canvases, it's really unlikely that + // waiting for composition will change anything here, and it's much + // slower, so just dispatchPromise. If we want to test with composites, + // we should test a more narrow subset of tests. + wtu.dispatchPromise(runNextTest); + } + runNextTest(); + }); + } + + runTexImageTest(gl.TEXTURE_2D).then(function(val) { + runTexImageTest(gl.TEXTURE_CUBE_MAP).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-blob.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-blob.js new file mode 100644 index 0000000000..f434b9834c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-blob.js @@ -0,0 +1,49 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + async function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from a Blob (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + debug('*** Running tests against red-green-semi-transparent.png ***'); + let response = await fetch(resourcePath + "red-green-semi-transparent.png"); + let blob = await response.blob(); + await runImageBitmapTest(blob, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false); + debug('*** Running tests against red-green-128x128-linear-profile.jpg ***'); + response = await fetch(resourcePath + "red-green-128x128-linear-profile.jpg"); + blob = await response.blob(); + // This test requires a huge tolerance because browsers - at least currently - vary + // widely in the colorspace conversion results for this image. + let tolerance = 120; + await runImageBitmapTest(blob, 1.0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false, tolerance); + finishTest(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-canvas.js new file mode 100644 index 0000000000..08ee96c0a1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-canvas.js @@ -0,0 +1,74 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from an HTMLCanvasElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var testCanvas = document.createElement('canvas'); + var ctx = testCanvas.getContext("2d"); + setCanvasToMin(ctx); + runImageBitmapTest(testCanvas, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false) + .then(() => { + setCanvasTo257x257(ctx); + return runImageBitmapTest(testCanvas, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false); + }).then(() => { + finishTest(); + }); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var halfWidth = Math.floor(width / 2); + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.fillStyle = "rgba(255, 0, 0, 1)"; + ctx.fillRect(0, 0, halfWidth, halfHeight); + ctx.fillStyle = "rgba(255, 0, 0, 0.5)"; + ctx.fillRect(halfWidth, 0, halfWidth, halfHeight); + ctx.fillStyle = "rgba(0, 255, 0, 1)"; + ctx.fillRect(0, halfHeight, halfWidth, halfHeight); + ctx.fillStyle = "rgba(0, 255, 0, 0.5)"; + ctx.fillRect(halfWidth, halfHeight, halfWidth, halfHeight); + } + + function setCanvasToMin(ctx) { + ctx.canvas.width = 2; + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function setCanvasTo257x257(ctx) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-bitmap.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-bitmap.js new file mode 100644 index 0000000000..09821d8b3f --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-bitmap.js @@ -0,0 +1,56 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from an ImageBitmap (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var imageData = new ImageData(new Uint8ClampedArray( + [255, 0, 0, 255, + 255, 0, 0, 0, + 0, 255, 0, 255, + 0, 255, 0, 0]), + 2, 2); + + createImageBitmap(imageData, {imageOrientation: "none", premultiplyAlpha: "none"}) + .catch( () => { + testPassed("createImageBitmap with options may be rejected if it is not supported. Retrying without options."); + return createImageBitmap(imageData); + }).then( bitmap => { + return runImageBitmapTest(bitmap, 0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false); + }, () => { + testFailed("createImageBitmap(imageData) should succeed."); + }).then(() => { + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-data.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-data.js new file mode 100644 index 0000000000..dd24721fe4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-data.js @@ -0,0 +1,49 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var imageData = new ImageData(new Uint8ClampedArray( + [255, 0, 0, 255, + 255, 0, 0, 0, + 0, 255, 0, 255, + 0, 255, 0, 0]), + 2, 2); + + runImageBitmapTest(imageData, 0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false) + .then(() => { + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image.js new file mode 100644 index 0000000000..1e4b18129e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image.js @@ -0,0 +1,46 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from an HTMLImageElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var image = new Image(); + image.onload = function() { + bufferedLogToConsole("Source image has been loaded"); + runImageBitmapTest(image, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false) + .then(() => { + finishTest(); + }); + } + image.src = resourcePath + "red-green-semi-transparent.png"; + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js new file mode 100644 index 0000000000..14cf4628be --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js @@ -0,0 +1,83 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + var videos = [ + { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, + { src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', }, + { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, + { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', }, + ]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from an HTMLVideoElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + finishTest(); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.type); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, async function() { + await runImageBitmapTest(video, 1, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false); + runNextVideo(); + }); + } + runNextVideo(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-data.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-data.js new file mode 100644 index 0000000000..8486b0b659 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-data.js @@ -0,0 +1,240 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imageData = null; + var blackColor = [0, 0, 0]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var canvas2d = document.getElementById("texcanvas"); + var context2d = canvas2d.getContext("2d"); + imageData = context2d.createImageData(2, 2); + var data = imageData.data; + data[0] = 255; + data[1] = 0; + data[2] = 0; + data[3] = 255; + data[4] = 255; + data[5] = 0; + data[6] = 0; + data[7] = 0; + data[8] = 0; + data[9] = 255; + data[10] = 0; + data[11] = 255; + data[12] = 0; + data[13] = 255; + data[14] = 0; + data[15] = 0; + + runTest(); + } + + function runOneIteration(useTexSubImage2D, flipY, premultiplyAlpha, + sourceSubRectangle, expected, + bindingTarget, program) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ', sourceSubRectangle=' + sourceSubRectangle; + } + debug(''); + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' and premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Handle the source sub-rectangle if specified (WebGL 2.0 only) + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (sourceSubRectangle) { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], imageData); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], imageData); + } + } else { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], imageData.width, imageData.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], imageData); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], imageData); + } + } + } + + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + } + + var width = gl.canvas.width; + var halfWidth = Math.floor(width / 2); + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + + var top = 0; + var bottom = height - halfHeight; + var left = 0; + var right = width - halfWidth; + + var tl, tr, bl, br; + if (expected.length == 1) { + tl = tr = bl = br = expected[0]; + } else { + tl = expected[0]; + tr = expected[1]; + bl = expected[2]; + br = expected[3]; + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + // Check the top pixel and bottom pixel and make sure they have + // the right color. + wtu.checkCanvasRect(gl, left, top, halfWidth, halfHeight, tl, "shouldBe " + tl); + wtu.checkCanvasRect(gl, right, top, halfWidth, halfHeight, tr, "shouldBe " + tr); + wtu.checkCanvasRect(gl, left, bottom, halfWidth, halfHeight, bl, "shouldBe " + bl); + wtu.checkCanvasRect(gl, right, bottom, halfWidth, halfHeight, br, "shouldBe " + br); + } + } + + function runTest() + { + var program = tiu.setupTexturedQuad(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_2D, program); + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_CUBE_MAP, program); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + function runTestOnBindingTarget(bindingTarget, program) { + var k = blackColor; + var r = redColor; + var g = greenColor; + var cases = [ + { expected: [r, r, g, g], flipY: false, premultiplyAlpha: false, sub: false }, + { expected: [r, r, g, g], flipY: false, premultiplyAlpha: false, sub: true }, + { expected: [r, k, g, k], flipY: false, premultiplyAlpha: true, sub: false }, + { expected: [r, k, g, k], flipY: false, premultiplyAlpha: true, sub: true }, + { expected: [g, g, r, r], flipY: true, premultiplyAlpha: false, sub: false }, + { expected: [g, g, r, r], flipY: true, premultiplyAlpha: false, sub: true }, + { expected: [g, k, r, k], flipY: true, premultiplyAlpha: true, sub: false }, + { expected: [g, k, r, k], flipY: true, premultiplyAlpha: true, sub: true }, + ]; + + if (wtu.getDefault3DContextVersion() > 1) { + var morecases = []; + // Make 2 copies of the original case: top left and bottom right 1x1 rectangles + for (var i = 0; i < cases.length; i++) { + for (var subX = 0; subX <= 1; subX++) { + var subY = subX == 0 ? 1 : 0; + // shallow-copy cases[i] into newcase + var newcase = Object.assign({}, cases[i]); + newcase.expected = [cases[i].expected[subY * 2 + subX]]; + newcase.sourceSubRectangle = [subX, subY, 1, 1]; + morecases.push(newcase); + } + } + cases = cases.concat(morecases); + } + + for (var i in cases) { + runOneIteration(cases[i].sub, cases[i].flipY, cases[i].premultiplyAlpha, + cases[i].sourceSubRectangle, cases[i].expected, + bindingTarget, program); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image.js new file mode 100644 index 0000000000..f3802ba777 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image.js @@ -0,0 +1,257 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imgCanvas; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking image elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + break; + + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + break; + + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + wtu.loadTexture(gl, resourcePath + "red-green.png", runTest); + } + + function runOneIteration(image, useTexSubImage2D, flipY, topColor, bottomColor, + sourceSubRectangle, bindingTarget, program) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; + } + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with ' + image.width + 'x' + image.height + ' flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Handle the source sub-rectangle if specified (WebGL 2.0 only) + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (sourceSubRectangle) { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], image); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], image); + } + } else { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], image.width, image.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], image); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], image); + } + } + } + + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + } + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + } + + function runTestOnImage(image) { + var cases = [ + { sub: false, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: redColor }, + { sub: true, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + + + if (wtu.getDefault3DContextVersion() > 1) { + cases = cases.concat([ + { sub: false, flipY: false, topColor: redColor, bottomColor: redColor, + sourceSubRectangle: [0, 0, 1, 1] }, + { sub: false, flipY: true, topColor: greenColor, bottomColor: greenColor, + sourceSubRectangle: [0, 0, 1, 1] }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: greenColor, + sourceSubRectangle: [0, 1, 1, 1] }, + { sub: false, flipY: true, topColor: redColor, bottomColor: redColor, + sourceSubRectangle: [0, 1, 1, 1] }, + { sub: true, flipY: false, topColor: redColor, bottomColor: redColor, + sourceSubRectangle: [0, 0, 1, 1] }, + { sub: true, flipY: true, topColor: greenColor, bottomColor: greenColor, + sourceSubRectangle: [0, 0, 1, 1] }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: greenColor, + sourceSubRectangle: [0, 1, 1, 1] }, + { sub: true, flipY: true, topColor: redColor, bottomColor: redColor, + sourceSubRectangle: [0, 1, 1, 1] }, + ]); + } + + var program = tiu.setupTexturedQuad(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, cases[i].sub, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + cases[i].sourceSubRectangle, + gl.TEXTURE_2D, program); + } + // cube map texture must be square. + if (image.width != image.height) + return; + // Skip sub-rectangle tests for cube map textures for the moment. + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + for (var i in cases) { + if (!cases[i].sourceSubRectangle) { + runOneIteration(image, cases[i].sub, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + undefined, + gl.TEXTURE_CUBE_MAP, program); + } + } + } + + function runTest(image) + { + runTestOnImage(image); + + imgCanvas = document.createElement("canvas"); + imgCanvas.width = 2; + imgCanvas.height = 2; + var imgCtx = imgCanvas.getContext("2d"); + var imgData = imgCtx.createImageData(2, 2); + imgData.data[0] = redColor[0]; + imgData.data[1] = redColor[1]; + imgData.data[2] = redColor[2]; + imgData.data[3] = 255; + imgData.data[4] = redColor[0]; + imgData.data[5] = redColor[1]; + imgData.data[6] = redColor[2]; + imgData.data[7] = 255; + imgData.data[8] = greenColor[0]; + imgData.data[9] = greenColor[1]; + imgData.data[10] = greenColor[2]; + imgData.data[11] = 255; + imgData.data[12] = greenColor[0]; + imgData.data[13] = greenColor[1]; + imgData.data[14] = greenColor[2]; + imgData.data[15] = 255; + imgCtx.putImageData(imgData, 0, 0); + + // apparently Image is different than <img>. + var newImage = new Image(); + newImage.onload = function() { + runTest2(newImage); + }; + newImage.onerror = function() { + testFailed("Creating image from canvas failed. Image src: " + this.src); + finishTest(); + }; + newImage.src = imgCanvas.toDataURL(); + } + + function runTest2(image) { + runTestOnImage(image); + + wtu.makeImageFromCanvas(imgCanvas, function() { + runTest3(this); + }); + } + + function runTest3(image) { + runTestOnImage(image); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-svg-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-svg-image.js new file mode 100644 index 0000000000..5cc6a8283e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-svg-image.js @@ -0,0 +1,140 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imgCanvas; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking SVG image elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + wtu.loadTexture(gl, resourcePath + "red-green.svg", runTest); + } + + function runOneIteration(image, useTexSubImage2D, flipY, topColor, bottomColor, bindingTarget, program) + { + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP')); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], image.width, image.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], image); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], image); + } + } + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + } + + function runTest(image) + { + var program = tiu.setupTexturedQuad(gl, internalFormat); + runTestOnBindingTarget(image, gl.TEXTURE_2D, program); + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + runTestOnBindingTarget(image, gl.TEXTURE_CUBE_MAP, program); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + function runTestOnBindingTarget(image, bindingTarget, program) { + var cases = [ + { sub: false, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: redColor }, + { sub: true, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + for (var i in cases) { + runOneIteration(image, cases[i].sub, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + bindingTarget, program); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js new file mode 100644 index 0000000000..6e8bcf96e9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js @@ -0,0 +1,291 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This block needs to be outside the onload handler in order for this +// test to run reliably in WebKit's test harness (at least the +// Chromium port). https://bugs.webkit.org/show_bug.cgi?id=87448 +initTestingHarness(); + +var old = debug; +var debug = function(msg) { + bufferedLogToConsole(msg); + old(msg); +}; + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + // Test each format separately because many browsers implement each + // differently. Some might be GPU accelerated, some might not. Etc... + var videos = [ + { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, + { src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', }, + { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, + { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', }, + ]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking video elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function runOneIteration(videoElement, unpackColorSpace, useTexSubImage2D, flipY, topColorName, bottomColorName, sourceSubRectangle, program, bindingTarget) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; + } + unpackColorSpaceString = ''; + if (unpackColorSpace) { + unpackColorSpaceString = ' unpackColorSpace=' + unpackColorSpace; + } + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString + unpackColorSpaceString); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Handle target color space. + if (unpackColorSpace) { + gl.unpackColorSpace = unpackColorSpace; + } + // Handle the source sub-rectangle if specified (WebGL 2.0 only) + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + } + // Upload the videoElement into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (sourceSubRectangle) { + // Initialize the texture to black first + if (useTexSubImage2D) { + // Skip sub-rectangle tests for cube map textures for the moment. + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + continue; + } + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], videoElement); + } + } else { + // Initialize the texture to black first + if (useTexSubImage2D) { + var width = videoElement.videoWidth; + var height = videoElement.videoHeight; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + width = Math.max(width, height); + height = width; + } + gl.texImage2D(targets[tt], 0, gl[internalFormat], + width, height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], videoElement); + } + } + } + + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + } + + var c = document.createElement("canvas"); + c.width = 16; + c.height = 16; + c.style.border = "1px solid black"; + var ctx = c.getContext("2d"); + ctx.drawImage(videoElement, 0, 0, 16, 16); + document.body.appendChild(c); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + // Compute the test colors. This test only tests RGB (not A). + const topColor = wtu.colorAsSampledWithInternalFormat( + wtu.namedColorInColorSpace(topColorName, unpackColorSpace), + internalFormat).slice(0, 3); + const bottomColor = wtu.colorAsSampledWithInternalFormat( + wtu.namedColorInColorSpace(bottomColorName, unpackColorSpace), + internalFormat).slice(0, 3); + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + const tolerance = Math.max(6, tiu.tolerance(internalFormat, pixelFormat, pixelType)); + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor, tolerance); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor, tolerance); + } + } + + function runTest(videoElement) + { + var cases = [ + { sub: false, flipY: true, topColor: 'Red', bottomColor: 'Green' }, + { sub: false, flipY: false, topColor: 'Green', bottomColor: 'Red' }, + { sub: true, flipY: true, topColor: 'Red', bottomColor: 'Green' }, + { sub: true, flipY: false, topColor: 'Green', bottomColor: 'Red' }, + ]; + + if (wtu.getDefault3DContextVersion() > 1) { + cases = cases.concat([ + { sub: false, flipY: false, topColor: 'Red', bottomColor: 'Red', + sourceSubRectangle: [20, 16, 40, 32] }, + { sub: false, flipY: true, topColor: 'Green', bottomColor: 'Green', + sourceSubRectangle: [20, 16, 40, 32] }, + { sub: false, flipY: false, topColor: 'Green', bottomColor: 'Green', + sourceSubRectangle: [20, 80, 40, 32] }, + { sub: false, flipY: true, topColor: 'Red', bottomColor: 'Red', + sourceSubRectangle: [20, 80, 40, 32] }, + { sub: true, flipY: false, topColor: 'Red', bottomColor: 'Red', + sourceSubRectangle: [20, 16, 40, 32] }, + { sub: true, flipY: true, topColor: 'Green', bottomColor: 'Green', + sourceSubRectangle: [20, 16, 40, 32] }, + { sub: true, flipY: false, topColor: 'Green', bottomColor: 'Green', + sourceSubRectangle: [20, 80, 40, 32] }, + { sub: true, flipY: true, topColor: 'Red', bottomColor: 'Red', + sourceSubRectangle: [20, 80, 40, 32] }, + ]); + } + + cases = tiu.crossProductTestCasesWithUnpackColorSpaces( + cases, tiu.unpackColorSpacesToTest(gl)); + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_2D) { + program = tiu.setupTexturedQuad(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + resolve("SUCCESS"); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.type); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, runTest); + } + function runTest() { + for (var i in cases) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // Cube map texture must be square but video is not square. + if (!cases[i].sub) { + break; + } + // Skip sub-rectangle tests for cube map textures for the moment. + if (cases[i].sourceSubRectangle) { + break; + } + } + runOneIteration(video, cases[i].unpackColorSpace, cases[i].sub, cases[i].flipY, + cases[i].topColor, + cases[i].bottomColor, + cases[i].sourceSubRectangle, + program, bindingTarget); + } + runNextVideo(); + } + runNextVideo(); + }); + } + + runTexImageTest(gl.TEXTURE_2D).then(function(val) { + runTexImageTest(gl.TEXTURE_CUBE_MAP).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-webgl-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-webgl-canvas.js new file mode 100644 index 0000000000..acb8768051 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-webgl-canvas.js @@ -0,0 +1,298 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +"use strict"; + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var redColor = [255, 0, 0, 255]; + var greenColor = [0, 255, 0, 255]; + var repeatCount; + + function shouldRepeatTestForTextureFormat(internalFormat, pixelFormat, pixelType) + { + // There were bugs in early WebGL 1.0 implementations when repeatedly uploading canvas + // elements into textures. In response, this test was changed into a regression test by + // repeating all of the cases multiple times. Unfortunately, this means that adding a new + // case above significantly increases the run time of the test suite. The problem is made + // even worse by the addition of many more texture formats in WebGL 2.0. + // + // Doing repeated runs with just a couple of WebGL 1.0's supported texture formats acts as a + // sufficient regression test for the old bugs. For this reason the test has been changed to + // only repeat for those texture formats. + return ((internalFormat == 'RGBA' && pixelFormat == 'RGBA' && pixelType == 'UNSIGNED_BYTE') || + (internalFormat == 'RGB' && pixelFormat == 'RGB' && pixelType == 'UNSIGNED_BYTE')); + } + + async function init() + { + description('Verify texImage2D and texSubImage2D code paths taking webgl canvas elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + return; + } + + repeatCount = (shouldRepeatTestForTextureFormat(internalFormat, pixelFormat, pixelType) ? 4 : 1); + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + await runTest(); + } + + function setCanvasToRedGreen(ctx, hasAlpha) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + + ctx.viewport(0, 0, width, height); + + ctx.enable(ctx.SCISSOR_TEST); + ctx.scissor(0, 0, width, halfHeight); + if (hasAlpha) { + ctx.clearColor(1.0, 0, 0, 1.0); + } else { + // The WebGL implementation is responsible for making all + // alpha values appear as though they were 1.0. + ctx.clearColor(1.0, 0, 0, 0.0); + } + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.scissor(0, halfHeight, width, height - halfHeight); + if (hasAlpha) { + ctx.clearColor(0.0, 1.0, 0, 1.0); + } else { + // The WebGL implementation is responsible for making all + // alpha values appear as though they were 1.0. + ctx.clearColor(0.0, 1.0, 0, 0.0); + } + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.disable(ctx.SCISSOR_TEST); + } + + function setCanvasTo257x257(ctx, bindingTarget, hasAlpha) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx, hasAlpha); + } + + function setCanvasToMin(ctx, bindingTarget, hasAlpha) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + ctx.canvas.width = 2; + } else { + ctx.canvas.width = 1; + } + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx, hasAlpha); + } + + function runOneIteration(canvas, useTexSubImage2D, alpha, flipY, program, bindingTarget, opt_texture) + { + var objType = 'canvas'; + if (canvas.transferToImageBitmap) + objType = 'OffscreenCanvas'; + else if (canvas.parentNode) + objType = 'canvas attached to DOM'; + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + ' with alpha=' + + alpha + ' flipY=' + flipY + ' source object: ' + objType + + ' bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + ' canvas size: ' + canvas.width + 'x' + canvas.height + ' with red-green'); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (!opt_texture) { + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + var texture = opt_texture; + } + // Set up pixel store parameters + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors before pixelStorei setup"); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors after setting UNPACK_FLIP_Y_WEBGL"); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors after setting UNPACK_PREMULTIPLY_ALPHA_WEBGL"); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors after setting UNPACK_COLORSPACE_CONVERSION_WEBGL"); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + // Initialize the texture to black first + if (useTexSubImage2D) { + gl.texImage2D(targets[tt], 0, gl[internalFormat], canvas.width, canvas.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], canvas); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], canvas); + } + } + + var width = gl.canvas.width; + var height = gl.canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + var top = flipY ? (height - halfHeight) : 0; + var bottom = flipY ? 0 : (height - halfHeight); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); + + // Check the top and bottom halves and make sure they have the right color. + debug("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, 0, bottom, width, halfHeight, redColor, + "shouldBe " + redColor); + debug("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, 0, top, width, halfHeight, greenColor, + "shouldBe " + greenColor); + } + + if (false) { + var ma = wtu.makeImageFromCanvas(canvas); + document.getElementById("console").appendChild(ma); + + var m = wtu.makeImageFromCanvas(gl.canvas); + document.getElementById("console").appendChild(m); + document.getElementById("console").appendChild(document.createElement("hr")); + } + + return texture; + } + + async function runTest() + { + for (let alpha of [ true, false ]) { + let ctx = wtu.create3DContext(null, { alpha:alpha }); + let canvas = ctx.canvas; + // Note: We use preserveDrawingBuffer:true to prevent canvas + // visibility from interfering with the tests. + let visibleCtx = wtu.create3DContext(null, { preserveDrawingBuffer:true, alpha:alpha }); + if (!visibleCtx) { + testFailed("context does not exist"); + return; + } + let visibleCanvas = visibleCtx.canvas; + let descriptionNode = document.getElementById("description"); + document.body.insertBefore(visibleCanvas, descriptionNode); + + let cases = [ + { sub: false, flipY: true, ctx: ctx, init: setCanvasToMin }, + { sub: false, flipY: false, ctx: ctx }, + { sub: true, flipY: true, ctx: ctx }, + { sub: true, flipY: false, ctx: ctx }, + { sub: false, flipY: true, ctx: ctx, init: setCanvasTo257x257 }, + { sub: false, flipY: false, ctx: ctx }, + { sub: true, flipY: true, ctx: ctx }, + { sub: true, flipY: false, ctx: ctx }, + { sub: false, flipY: true, ctx: visibleCtx, init: setCanvasToMin }, + { sub: false, flipY: false, ctx: visibleCtx }, + { sub: true, flipY: true, ctx: visibleCtx }, + { sub: true, flipY: false, ctx: visibleCtx }, + ]; + + if (window.OffscreenCanvas) { + let offscreen = new OffscreenCanvas(1, 1); + let offscreenCtx = wtu.create3DContext(offscreen, { alpha:alpha }); + cases = cases.concat([ + { sub: false, flipY: true, ctx: offscreenCtx, init: setCanvasToMin }, + { sub: false, flipY: false, ctx: offscreenCtx }, + { sub: true, flipY: true, ctx: offscreenCtx }, + { sub: true, flipY: false, ctx: offscreenCtx }, + ]); + } + + async function runTexImageTest(bindingTarget) { + let program; + if (bindingTarget == gl.TEXTURE_2D) { + program = tiu.setupTexturedQuad(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + } + + let count = repeatCount; + let caseNdx = 0; + let texture = undefined; + while (true) { + let c = cases[caseNdx]; + if (c.init) { + c.init(c.ctx, bindingTarget, alpha); + } + texture = runOneIteration(c.ctx.canvas, c.sub, alpha, c.flipY, program, bindingTarget, texture); + // for the first 2 iterations always make a new texture. + if (count < 2) { + gl.deleteTexture(texture); + texture = undefined; + } + ++caseNdx; + if (caseNdx == cases.length) { + caseNdx = 0; + --count; + if (!count) + return; + } + await wtu.dispatchPromise(function() {}); + } + } + + await runTexImageTest(gl.TEXTURE_2D); + await runTexImageTest(gl.TEXTURE_CUBE_MAP); + } + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + } + + return function() { + init().then(function(val) { + finishTest(); + }); + }; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas-sub-rectangle.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas-sub-rectangle.js new file mode 100644 index 0000000000..85f763a0de --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas-sub-rectangle.js @@ -0,0 +1,287 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var realRedColor = [255, 0, 0]; + var realGreenColor = [0, 255, 0]; + var realBlueColor = [0, 0, 255]; + var realCyanColor = [0, 255, 255]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var blueColor = [0, 0, 255]; + var cyanColor = [0, 255, 255]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking a sub-rectangle of a canvas (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + case gl.RG: + case gl.RG_INTEGER: + blueColor = [0, 0, 0]; + cyanColor = [0, 255, 0]; + break; + + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var canvas2d = document.createElement('canvas'); + runTest(canvas2d, setupSourceCanvas2D, '2D-rendered canvas'); + + var canvasWebGL = document.createElement('canvas'); + runTest(canvasWebGL, setupSourceCanvasWebGL, 'WebGL-rendered canvas'); + + finishTest(); + } + + function uploadCanvasToTexture(canvas, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight) + { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + var uploadWidth = canvas.width; + var uploadHeight = canvas.height; + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + uploadWidth = sourceSubRectangle[2]; + uploadHeight = sourceSubRectangle[3]; + } + if (unpackImageHeight) { + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); + } + // Upload the image into the texture + if (useTexSubImage3D) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, uploadWidth, uploadHeight, depth, + gl[pixelFormat], gl[pixelType], canvas); + } else { + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], canvas); + } + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texture upload"); + } + + function fillStyle2D(ctx, color) { + ctx.fillStyle = 'rgb(' + color[0] + ', ' + color[1] + ', ' + color[2] + ')'; + } + + function setupSourceCanvas2D(canvas) { + var width = canvas.width; + var height = canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + + var ctx = canvas.getContext('2d'); + // Always use the same pattern for this test: four quadrants: + // red green + // blue cyan + // Handle odd-sized canvases + fillStyle2D(ctx, realRedColor); + ctx.fillRect(0, 0, halfWidth, halfHeight); + fillStyle2D(ctx, realGreenColor); + ctx.fillRect(halfWidth, 0, width - halfWidth, halfHeight); + fillStyle2D(ctx, realBlueColor); + ctx.fillRect(0, halfHeight, halfWidth, height - halfHeight); + fillStyle2D(ctx, realCyanColor); + ctx.fillRect(halfWidth, halfHeight, width - halfWidth, height - halfHeight); + } + + function clearColorWebGL(ctx, color) { + ctx.clearColor(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT); + } + + function setupSourceCanvasWebGL(canvas) { + var width = canvas.width; + var height = canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + + var ctx = canvas.getContext('webgl'); + // Always use the same pattern for this test: four quadrants: + // red green + // blue cyan + // Handle odd-sized canvases + + ctx.viewport(0, 0, width, height); + ctx.enable(ctx.SCISSOR_TEST); + // OpenGL origin is lower-left + ctx.scissor(0, 0, halfWidth, halfHeight); + clearColorWebGL(ctx, realBlueColor); + ctx.scissor(halfWidth, 0, width - halfWidth, halfHeight); + clearColorWebGL(ctx, realCyanColor); + ctx.scissor(0, halfHeight, halfWidth, height - halfHeight); + clearColorWebGL(ctx, realRedColor); + ctx.scissor(halfWidth, halfHeight, width - halfWidth, height - halfHeight); + clearColorWebGL(ctx, realGreenColor); + } + + function runOneIteration(canvas, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight, + rTextureCoord, expectedColor, program, + canvasSize, canvasSetupFunction, sourceDescription) + { + debug(''); + debug('Testing ' + sourceDescription + ' with ' + + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') + + ', flipY=' + flipY + ', bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + ', sourceSubRectangle=' + sourceSubRectangle + + ', depth=' + depth + + (unpackImageHeight ? ', unpackImageHeight=' + unpackImageHeight : '') + + ', rTextureCoord=' + rTextureCoord); + + // Initialize the contents of the source canvas. + var width = canvasSize[0]; + var height = canvasSize[1]; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + canvas.width = width; + canvas.height = height; + canvasSetupFunction(canvas); + + uploadCanvasToTexture(canvas, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight); + var rCoordLocation = gl.getUniformLocation(program, 'uRCoord'); + if (!rCoordLocation) { + testFailed('Shader incorrectly set up; couldn\'t find uRCoord uniform'); + return; + } + gl.uniform1f(rCoordLocation, rTextureCoord); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check the rendered canvas + wtu.checkCanvasRect(gl, 0, 0, canvasSize[0], canvasSize[1], expectedColor, "shouldBe " + expectedColor); + } + + function runTest(canvas, canvasSetupFunction, sourceDescription) + { + var cases = [ + // Small canvas cases. Expected that these won't be + // GPU-accelerated in most browsers' implementations. + + // No UNPACK_IMAGE_HEIGHT specified. + { expected: redColor, flipY: false, size: [4, 4], subRect: [0, 0, 2, 2], depth: 2, rTextureCoord: 0.0 }, + { expected: blueColor, flipY: false, size: [4, 4], subRect: [0, 0, 2, 2], depth: 2, rTextureCoord: 1.0 }, + { expected: blueColor, flipY: true, size: [4, 4], subRect: [0, 0, 2, 2], depth: 2, rTextureCoord: 0.0 }, + { expected: redColor, flipY: true, size: [4, 4], subRect: [0, 0, 2, 2], depth: 2, rTextureCoord: 1.0 }, + { expected: greenColor, flipY: false, size: [4, 4], subRect: [2, 0, 2, 2], depth: 2, rTextureCoord: 0.0 }, + { expected: cyanColor, flipY: false, size: [4, 4], subRect: [2, 0, 2, 2], depth: 2, rTextureCoord: 1.0 }, + { expected: cyanColor, flipY: true, size: [4, 4], subRect: [2, 0, 2, 2], depth: 2, rTextureCoord: 0.0 }, + { expected: greenColor, flipY: true, size: [4, 4], subRect: [2, 0, 2, 2], depth: 2, rTextureCoord: 1.0 }, + + // Use UNPACK_IMAGE_HEIGHT to skip some pixels. + { expected: redColor, flipY: false, size: [4, 4], subRect: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0 }, + { expected: blueColor, flipY: false, size: [4, 4], subRect: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0 }, + { expected: blueColor, flipY: true, size: [4, 4], subRect: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0 }, + { expected: redColor, flipY: true, size: [4, 4], subRect: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0 }, + { expected: greenColor, flipY: false, size: [4, 4], subRect: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0 }, + { expected: cyanColor, flipY: false, size: [4, 4], subRect: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0 }, + { expected: cyanColor, flipY: true, size: [4, 4], subRect: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0 }, + { expected: greenColor, flipY: true, size: [4, 4], subRect: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0 }, + + // Larger canvas cases. Expected that these will be + // GPU-accelerated in most browsers' implementations. + // Changes will be gladly accepted to trigger more + // browsers' heuristics to accelerate these canvases. + + // No UNPACK_IMAGE_HEIGHT specified. + { expected: redColor, flipY: false, size: [384, 384], subRect: [0, 0, 192, 192], depth: 2, rTextureCoord: 0.0 }, + { expected: blueColor, flipY: false, size: [384, 384], subRect: [0, 0, 192, 192], depth: 2, rTextureCoord: 1.0 }, + { expected: blueColor, flipY: true, size: [384, 384], subRect: [0, 0, 192, 192], depth: 2, rTextureCoord: 0.0 }, + { expected: redColor, flipY: true, size: [384, 384], subRect: [0, 0, 192, 192], depth: 2, rTextureCoord: 1.0 }, + { expected: greenColor, flipY: false, size: [384, 384], subRect: [192, 0, 192, 192], depth: 2, rTextureCoord: 0.0 }, + { expected: cyanColor, flipY: false, size: [384, 384], subRect: [192, 0, 192, 192], depth: 2, rTextureCoord: 1.0 }, + { expected: cyanColor, flipY: true, size: [384, 384], subRect: [192, 0, 192, 192], depth: 2, rTextureCoord: 0.0 }, + { expected: greenColor, flipY: true, size: [384, 384], subRect: [192, 0, 192, 192], depth: 2, rTextureCoord: 1.0 }, + + // Use UNPACK_IMAGE_HEIGHT to skip some pixels. + { expected: redColor, flipY: false, size: [384, 384], subRect: [0, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 0.0 }, + { expected: blueColor, flipY: false, size: [384, 384], subRect: [0, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 1.0 }, + { expected: blueColor, flipY: true, size: [384, 384], subRect: [0, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 0.0 }, + { expected: redColor, flipY: true, size: [384, 384], subRect: [0, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 1.0 }, + { expected: greenColor, flipY: false, size: [384, 384], subRect: [192, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 0.0 }, + { expected: cyanColor, flipY: false, size: [384, 384], subRect: [192, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 1.0 }, + { expected: cyanColor, flipY: true, size: [384, 384], subRect: [192, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 0.0 }, + { expected: greenColor, flipY: true, size: [384, 384], subRect: [192, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 1.0 }, + ]; + + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + for (var i in cases) { + runOneIteration(canvas, false, cases[i].flipY, gl.TEXTURE_3D, + cases[i].depth, cases[i].subRect, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].expected, + program, cases[i].size, canvasSetupFunction, sourceDescription); + runOneIteration(canvas, true, cases[i].flipY, gl.TEXTURE_3D, + cases[i].depth, cases[i].subRect, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].expected, + program, cases[i].size, canvasSetupFunction, sourceDescription); + } + + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + for (var i in cases) { + runOneIteration(canvas, false, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].depth, cases[i].subRect, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].expected, + program, cases[i].size, canvasSetupFunction, sourceDescription); + runOneIteration(canvas, true, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].depth, cases[i].subRect, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].expected, + program, cases[i].size, canvasSetupFunction, sourceDescription); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas.js new file mode 100644 index 0000000000..c41cc4e897 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas.js @@ -0,0 +1,226 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var whiteColor = [255, 255, 255, 255]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking canvas elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + whiteColor = [255, 0, 0, 255]; + greenColor = [0, 0, 0]; + break; + case gl.RG: + case gl.RG_INTEGER: + whiteColor = [255, 255, 0, 255]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.fillStyle = "#ff0000"; + ctx.fillRect(0, 0, width, halfHeight); + ctx.fillStyle = "#00ff00"; + ctx.fillRect(0, halfHeight, width, height - halfHeight); + } + + function drawTextInCanvas(ctx, bindingTarget) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + ctx.fillStyle = "#ffffff"; + ctx.fillRect(0, 0, width, height); + ctx.font = '20pt Arial'; + ctx.fillStyle = 'black'; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText("1234567890", width / 2, height / 4); + } + + function setCanvasTo257x257(ctx, bindingTarget) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + function setCanvasToMin(ctx, bindingTarget) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + ctx.canvas.width = 2; + } else { + ctx.canvas.width = 1; + } + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function runOneIteration(canvas, flipY, program, bindingTarget, opt_texture, opt_fontTest) + { + var objType = 'canvas'; + if (canvas.transferToImageBitmap) + objType = 'OffscreenCanvas'; + debug('Testing ' + ' with flipY=' + flipY + ' bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + ' source object: ' + objType + ' canvas size: ' + canvas.width + 'x' + canvas.height + (opt_fontTest ? " with fonts" : " with red-green")); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (!opt_texture) { + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + var texture = opt_texture; + } + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], canvas.width, canvas.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, canvas.width, canvas.height, 1 /* depth */, + gl[pixelFormat], gl[pixelType], canvas); + + var width = gl.canvas.width; + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + var top = flipY ? 0 : (height - halfHeight); + var bottom = flipY ? (height - halfHeight) : 0; + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); + + if (opt_fontTest) { + // check half is a solid color. + wtu.checkCanvasRect( + gl, 0, top, width, halfHeight, + whiteColor, + "should be white"); + // check other half is not a solid color. + wtu.checkCanvasRectColor( + gl, 0, bottom, width, halfHeight, + whiteColor, 0, + function() { + testFailed("font missing"); + }, + function() { + testPassed("font renderered"); + }, + debug); + } else { + // Check the top and bottom halves and make sure they have the right color. + debug("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, 0, bottom, width, halfHeight, redColor, + "shouldBe " + redColor); + debug("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, 0, top, width, halfHeight, greenColor, + "shouldBe " + greenColor); + } + + return texture; + } + + function runTest(canvas) + { + var canvas = document.createElement('canvas'); + + var cases = [ + { canvas: canvas, flipY: true, font: false, init: setCanvasToMin }, + { canvas: canvas, flipY: false, font: false }, + { canvas: canvas, flipY: true, font: false, init: setCanvasTo257x257 }, + { canvas: canvas, flipY: false, font: false }, + { canvas: canvas, flipY: true, font: true, init: drawTextInCanvas }, + { canvas: canvas, flipY: false, font: true }, + ]; + + if (window.OffscreenCanvas) { + var offscreenCanvas = new OffscreenCanvas(1, 1); + cases = cases.concat([ + { canvas: offscreenCanvas, flipY: true, font: false, init: setCanvasToMin }, + { canvas: offscreenCanvas, flipY: false, font: false }, + ]); + } + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_3D) { + program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + } else { // TEXTURE_2D_ARRAY + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var count = 4; + var caseNdx = 0; + var texture = undefined; + function runNextTest() { + var c = cases[caseNdx]; + if (c.init) { + c.init(c.canvas.getContext('2d'), bindingTarget); + } + texture = runOneIteration(c.canvas, c.flipY, program, bindingTarget, texture, c.font); + // for the first 2 iterations always make a new texture. + if (count < 2) { + gl.deleteTexture(texture); + texture = undefined; + } + ++caseNdx; + if (caseNdx == cases.length) { + caseNdx = 0; + --count; + if (!count) { + resolve("SUCCESS"); + return; + } + } + wtu.waitForComposite(runNextTest); + } + runNextTest(); + }); + } + + runTexImageTest(gl.TEXTURE_3D).then(function(val) { + runTexImageTest(gl.TEXTURE_2D_ARRAY).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-blob.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-blob.js new file mode 100644 index 0000000000..b55bdcb143 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-blob.js @@ -0,0 +1,48 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from a Blob (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var xhr = new XMLHttpRequest(); + xhr.open("GET", resourcePath + "red-green-semi-transparent.png"); + xhr.responseType = 'blob'; + xhr.onload = function() { + var blob = xhr.response; + runImageBitmapTest(blob, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true) + .then(() => { + finishTest(); + }); + }; + xhr.send(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-canvas.js new file mode 100644 index 0000000000..805298b377 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-canvas.js @@ -0,0 +1,74 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from an HTMLCanvasElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var testCanvas = document.createElement('canvas'); + var ctx = testCanvas.getContext("2d"); + setCanvasToMin(ctx); + runImageBitmapTest(testCanvas, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true) + .then(() => { + setCanvasTo257x257(ctx); + return runImageBitmapTest(testCanvas, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true); + }).then(() => { + finishTest(); + }); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var halfWidth = Math.floor(width / 2); + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.fillStyle = "rgba(255, 0, 0, 1)"; + ctx.fillRect(0, 0, halfWidth, halfHeight); + ctx.fillStyle = "rgba(255, 0, 0, 0.5)"; + ctx.fillRect(halfWidth, 0, halfWidth, halfHeight); + ctx.fillStyle = "rgba(0, 255, 0, 1)"; + ctx.fillRect(0, halfHeight, halfWidth, halfHeight); + ctx.fillStyle = "rgba(0, 255, 0, 0.5)"; + ctx.fillRect(halfWidth, halfHeight, halfWidth, halfHeight); + } + + function setCanvasToMin(ctx) { + ctx.canvas.width = 2; + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function setCanvasTo257x257(ctx) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-bitmap.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-bitmap.js new file mode 100644 index 0000000000..44c9ffa378 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-bitmap.js @@ -0,0 +1,56 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from an ImageBitmap (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var imageData = new ImageData(new Uint8ClampedArray( + [255, 0, 0, 255, + 255, 0, 0, 0, + 0, 255, 0, 255, + 0, 255, 0, 0]), + 2, 2); + + createImageBitmap(imageData, {imageOrientation: "none", premultiplyAlpha: "none"}) + .catch( () => { + testPassed("createImageBitmap with options may be rejected if it is not supported. Retrying without options."); + return createImageBitmap(imageData); + }).then( bitmap => { + return runImageBitmapTest(bitmap, 0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true); + }, () => { + testFailed("createImageBitmap(imageData) should succeed."); + }).then(() => { + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-data.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-data.js new file mode 100644 index 0000000000..cca0358e17 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-data.js @@ -0,0 +1,49 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var imageData = new ImageData(new Uint8ClampedArray( + [255, 0, 0, 255, + 255, 0, 0, 0, + 0, 255, 0, 255, + 0, 255, 0, 0]), + 2, 2); + + runImageBitmapTest(imageData, 0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true) + .then(() => { + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image.js new file mode 100644 index 0000000000..0f1121bbb9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image.js @@ -0,0 +1,45 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from an HTMLImageElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var image = new Image(); + image.onload = function() { + runImageBitmapTest(image, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true) + .then(() => { + finishTest(); + }); + } + image.src = resourcePath + "red-green-semi-transparent.png"; + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js new file mode 100644 index 0000000000..a268f7d8d5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js @@ -0,0 +1,83 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + var videos = [ + { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, + { src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', }, + { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, + { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', }, + ]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from an HTMLVideoElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + finishTest(); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.type); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, async function() { + await runImageBitmapTest(video, 1, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true); + runNextVideo(); + }); + } + runNextVideo(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-data.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-data.js new file mode 100644 index 0000000000..bedf51a96a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-data.js @@ -0,0 +1,259 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imageData = null; + var blackColor = [0, 0, 0]; + var originalPixels = (function() { + // (red|green|blue|cyan)(opaque|transparent) + var ro = [255, 0, 0, 255]; var rt = [255, 0, 0, 0]; + var go = [0, 255, 0, 255]; var gt = [0, 255, 0, 0]; + var bo = [0, 0, 255, 255]; var bt = [0, 0, 255, 0]; + var co = [0, 255, 255, 255]; var ct = [0, 255, 255, 0]; + return [ro, rt, go, gt, + ro, rt, go, gt, + bo, bt, co, ct, + bo, bt, co, ct]; + })(); + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var canvas2d = document.getElementById("texcanvas"); + var context2d = canvas2d.getContext("2d"); + imageData = context2d.createImageData(4, 4); + var data = imageData.data; + for (var i = 0; i < originalPixels.length; i++) { + data.set(originalPixels[i], 4 * i); + } + + runTest(); + } + + function runOneIteration(useTexSubImage3D, flipY, premultiplyAlpha, bindingTarget, + depth, sourceSubRectangle, rTexCoord, program) + { + var expected = simulate(flipY, premultiplyAlpha, depth, sourceSubRectangle, rTexCoord); + var sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ', sourceSubRectangle=' + sourceSubRectangle; + sourceSubRectangleString += ', rTexCoord=' + rTexCoord; + } + debug(''); + debug('Testing ' + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + sourceSubRectangleString); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + var uploadWidth = imageData.width; + var uploadHeight = imageData.height; + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + uploadWidth = sourceSubRectangle[2]; + uploadHeight = sourceSubRectangle[3]; + } + // Upload the image into the texture + if (useTexSubImage3D) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, uploadWidth, uploadHeight, depth, + gl[pixelFormat], gl[pixelType], imageData); + } else { + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], imageData); + } + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texture upload"); + + var tl = expected[0][0]; + var tr = expected[0][1]; + var bl = expected[1][0]; + var br = expected[1][1]; + + var rCoordLocation = gl.getUniformLocation(program, 'uRCoord'); + if (!rCoordLocation) { + testFailed("Shader incorrectly set up; couldn't find uRCoord uniform"); + return; + } + gl.uniform1f(rCoordLocation, rTexCoord); + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + var width = gl.canvas.width; + var halfWidth = Math.floor(width / 2); + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + + var top = 0; + var bottom = height - halfHeight; + var left = 0; + var right = width - halfWidth; + + debug("Checking pixel values"); + debug("Expecting: " + expected); + var expectedH = expected.length; + var expectedW = expected[0].length; + var texelH = Math.floor(gl.canvas.height / expectedH); + var texelW = Math.floor(gl.canvas.width / expectedW); + // For each entry of the expected[][] array, check the appropriate + // canvas rectangle for correctness. + for (var row = 0; row < expectedH; row++) { + var y = row * texelH; + for (var col = 0; col < expectedW; col++) { + var x = col * texelW; + var val = expected[row][col]; + wtu.checkCanvasRect(gl, x, y, texelW, texelH, val, "should be " + val); + } + } + } + + function runTest() + { + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_3D, program); + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_2D_ARRAY, program); + + debug(""); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + function simulate(flipY, premultiplyAlpha, depth, sourceSubRectangle, rTexCoord) { + var ro = [255, 0, 0]; var rt = premultiplyAlpha ? [0, 0, 0] : [255, 0, 0]; + var go = [0, 255, 0]; var gt = premultiplyAlpha ? [0, 0, 0] : [0, 255, 0]; + var bo = [0, 0, 255]; var bt = premultiplyAlpha ? [0, 0, 0] : [0, 0, 255]; + var co = [0, 255, 255]; var ct = premultiplyAlpha ? [0, 0, 0] : [0, 255, 255]; + var expected = [[ro, rt, go, gt], + [ro, rt, go, gt], + [bo, bt, co, ct], + [bo, bt, co, ct]]; + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + for (var row = 0; row < 4; row++) { + for (var col = 0; col < 4; col++) { + expected[row][col][1] = 0; // zero the green channel + } + } + // fall-through + case gl.RG: + case gl.RG_INTEGER: + for (var row = 0; row < 4; row++) { + for (var col = 0; col < 4; col++) { + expected[row][col][2] = 0; // zero the blue channel + } + } + break; + default: + break; + } + + if (flipY) { + expected.reverse(); + } + + if (sourceSubRectangle) { + let expected2 = []; + for (var row = 0; row < sourceSubRectangle[3]; row++) { + expected2[row] = []; + for (var col = 0; col < sourceSubRectangle[2]; col++) { + expected2[row][col] = + expected[sourceSubRectangle[1] + row + rTexCoord * sourceSubRectangle[3]][sourceSubRectangle[0] + col]; + } + } + expected = expected2; + } + + return expected; + } + + function runTestOnBindingTarget(bindingTarget, program) { + var rects = [ + undefined, + [0, 0, 2, 2], + [2, 0, 2, 2], + ]; + var dbg = false; // Set to true for debug output images + if (dbg) { + (function() { + debug(""); + debug("Original ImageData (transparent pixels appear black):"); + var cvs = document.createElement("canvas"); + cvs.width = 4; + cvs.height = 4; + cvs.style.width = "32px"; + cvs.style.height = "32px"; + cvs.style.imageRendering = "pixelated"; + cvs.style.background = "#000"; + var ctx = cvs.getContext("2d"); + ctx.putImageData(imageData, 0, 0); + var output = document.getElementById("console"); + output.appendChild(cvs); + })(); + } + for (const sub of [false, true]) { + for (const flipY of [false, true]) { + for (const premul of [false, true]) { + for (let irect = 0; irect < rects.length; irect++) { + var rect = rects[irect]; + let depth = rect ? 2 : 1; + for (let rTexCoord = 0; rTexCoord < depth; rTexCoord++) { + // TODO: add tests for UNPACK_IMAGE_HEIGHT. + runOneIteration(sub, flipY, premul, bindingTarget, + depth, rect, rTexCoord, program); + if (dbg) { + debug("Actual:"); + var img = document.createElement("img"); + img.src = gl.canvas.toDataURL("image/png"); + var output = document.getElementById("console"); + output.appendChild(img); + } + } + } + } + } + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image.js new file mode 100644 index 0000000000..034bd8ab46 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image.js @@ -0,0 +1,260 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imgCanvas; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var blueColor = [0, 0, 255]; + var cyanColor = [0, 255, 255]; + var imageURLs = [resourcePath + "red-green.png", + resourcePath + "red-green-blue-cyan-4x4.png"]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking image elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + case gl.RG: + case gl.RG_INTEGER: + blueColor = [0, 0, 0]; + cyanColor = [0, 255, 0]; + break; + + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + wtu.loadImagesAsync(imageURLs, runTest); + } + + function uploadImageToTexture(image, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight) + { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + var uploadWidth = image.width; + var uploadHeight = image.height; + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + uploadWidth = sourceSubRectangle[2]; + uploadHeight = sourceSubRectangle[3]; + } + if (unpackImageHeight) { + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); + } + // Upload the image into the texture + if (useTexSubImage3D) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, uploadWidth, uploadHeight, depth, + gl[pixelFormat], gl[pixelType], image); + } else { + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], image); + } + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texture upload"); + } + + function runRedGreenTest(image) { + function runOneIteration(image, useTexSubImage3D, flipY, bindingTarget, topColor, bottomColor, program) + { + debug('Testing ' + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY')); + + uploadImageToTexture(image, useTexSubImage3D, flipY, bindingTarget, 1); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + + var cases = [ + { sub: false, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: redColor }, + { sub: true, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, cases[i].sub, cases[i].flipY, gl.TEXTURE_3D, + cases[i].topColor, cases[i].bottomColor, program); + } + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, cases[i].sub, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].topColor, cases[i].bottomColor, program); + } + } + + function runRedGreenBlueCyanTest(image) { + function runOneIteration(image, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight, + rTextureCoord, topColor, bottomColor, program) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; + } + unpackImageHeightString = ''; + if (unpackImageHeight) { + unpackImageHeightString = ' unpackImageHeight=' + unpackImageHeight; + } + debug('Testing ' + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + sourceSubRectangleString + ' depth=' + depth + unpackImageHeightString + + ' rTextureCoord=' + rTextureCoord); + + uploadImageToTexture(image, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight); + var rCoordLocation = gl.getUniformLocation(program, 'uRCoord'); + if (!rCoordLocation) { + testFailed('Shader incorrectly set up; couldn\'t find uRCoord uniform'); + return; + } + gl.uniform1f(rCoordLocation, rTextureCoord); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + + var cases = [ + // No UNPACK_IMAGE_HEIGHT specified. + { flipY: false, sourceSubRectangle: [0, 0, 2, 2], depth: 2, rTextureCoord: 0.0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [0, 0, 2, 2], depth: 2, rTextureCoord: 1.0, + topColor: blueColor, bottomColor: blueColor }, + { flipY: true, sourceSubRectangle: [0, 0, 2, 2], depth: 2, rTextureCoord: 0.0, + topColor: blueColor, bottomColor: blueColor }, + { flipY: true, sourceSubRectangle: [0, 0, 2, 2], depth: 2, rTextureCoord: 1.0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [2, 0, 2, 2], depth: 2, rTextureCoord: 0.0, + topColor: greenColor, bottomColor: greenColor }, + { flipY: false, sourceSubRectangle: [2, 0, 2, 2], depth: 2, rTextureCoord: 1.0, + topColor: cyanColor, bottomColor: cyanColor }, + { flipY: true, sourceSubRectangle: [2, 0, 2, 2], depth: 2, rTextureCoord: 0.0, + topColor: cyanColor, bottomColor: cyanColor }, + { flipY: true, sourceSubRectangle: [2, 0, 2, 2], depth: 2, rTextureCoord: 1.0, + topColor: greenColor, bottomColor: greenColor }, + + // Use UNPACK_IMAGE_HEIGHT to skip some pixels. + { flipY: false, sourceSubRectangle: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0, + topColor: blueColor, bottomColor: blueColor }, + { flipY: true, sourceSubRectangle: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0, + topColor: blueColor, bottomColor: blueColor }, + { flipY: true, sourceSubRectangle: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0, + topColor: greenColor, bottomColor: greenColor }, + { flipY: false, sourceSubRectangle: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0, + topColor: cyanColor, bottomColor: cyanColor }, + { flipY: true, sourceSubRectangle: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0, + topColor: cyanColor, bottomColor: cyanColor }, + { flipY: true, sourceSubRectangle: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0, + topColor: greenColor, bottomColor: greenColor }, + ]; + + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, false, cases[i].flipY, gl.TEXTURE_3D, + cases[i].depth, cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].topColor, cases[i].bottomColor, + program); + runOneIteration(image, true, cases[i].flipY, gl.TEXTURE_3D, + cases[i].depth, cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].topColor, cases[i].bottomColor, + program); + } + + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, false, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].depth, cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].topColor, cases[i].bottomColor, + program); + runOneIteration(image, true, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].depth, cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].topColor, cases[i].bottomColor, + program); + } + } + + function runTest(imageMap) + { + runRedGreenTest(imageMap[imageURLs[0]]); + runRedGreenBlueCyanTest(imageMap[imageURLs[1]]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-svg-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-svg-image.js new file mode 100644 index 0000000000..09a108dee8 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-svg-image.js @@ -0,0 +1,104 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imgCanvas; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking SVG image elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + wtu.loadTexture(gl, resourcePath + "red-green.svg", runTest); + } + + function runOneIteration(image, flipY, topColor, bottomColor, bindingTarget, program) + { + debug('Testing ' + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY')); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + // Upload the image into the texture + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], image.width, image.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, image.width, image.height, 1 /* depth */, + gl[pixelFormat], gl[pixelType], image); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + + function runTest(image) + { + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + runTestOnBindingTarget(image, gl.TEXTURE_3D, program); + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + runTestOnBindingTarget(image, gl.TEXTURE_2D_ARRAY, program); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + function runTestOnBindingTarget(image, bindingTarget, program) { + var cases = [ + { flipY: true, topColor: redColor, bottomColor: greenColor }, + { flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + for (var i in cases) { + runOneIteration(image, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + bindingTarget, program); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js new file mode 100644 index 0000000000..0c2c40e8a5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js @@ -0,0 +1,244 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This block needs to be outside the onload handler in order for this +// test to run reliably in WebKit's test harness (at least the +// Chromium port). https://bugs.webkit.org/show_bug.cgi?id=87448 +initTestingHarness(); + +var old = debug; +var debug = function(msg) { + bufferedLogToConsole(msg); + old(msg); +}; + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + // Test each format separately because many browsers implement each + // differently. Some might be GPU accelerated, some might not. Etc... + var videos = [ + { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, + { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, + { src: resourcePath + "red-green.webmvp8.webm", type: 'video/webm; codecs="vp8, vorbis"', }, + { src: resourcePath + "red-green.theora.ogv", type: 'video/ogg; codecs="theora, vorbis"', }, + ]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking video elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function runOneIteration(videoElement, flipY, useTexSubImage3D, topColor, bottomColor, program, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight, rTextureCoord) + { + debug('Testing ' + + (useTexSubImage3D ? "texSubImage3D" : "texImage3D") + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + (sourceSubRectangle ? ', sourceSubRectangle=' + sourceSubRectangle : '') + + (unpackImageHeight ? ', unpackImageHeight=' + unpackImageHeight : '') + + ', depth=' + depth + + ', rTextureCoord=' + rTextureCoord); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + var uploadWidth = videoElement.width; + var uploadHeight = videoElement.height; + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + uploadWidth = sourceSubRectangle[2]; + uploadHeight = sourceSubRectangle[3]; + } + if (unpackImageHeight) { + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); + } + // Upload the videoElement into the texture + if (useTexSubImage3D) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], + uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, + uploadWidth, uploadHeight, depth, + gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage3D(bindingTarget, 0, gl[internalFormat], + uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], videoElement); + } + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0); + + var c = document.createElement("canvas"); + c.width = 16; + c.height = 16; + c.style.border = "1px solid black"; + var ctx = c.getContext("2d"); + ctx.drawImage(videoElement, 0, 0, 16, 16); + document.body.appendChild(c); + + var rCoordLocation = gl.getUniformLocation(program, 'uRCoord'); + if (!rCoordLocation) { + testFailed('Shader incorrectly set up; couldn\'t find uRCoord uniform'); + return; + } + gl.uniform1f(rCoordLocation, rTextureCoord); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + const tolerance = 6; + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor, tolerance); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor, tolerance); + } + + function runTest(videoElement) + { + var cases = [ + // No UNPACK_IMAGE_HEIGHT specified. + { flipY: false, sourceSubRectangle: [32, 16, 16, 16], depth: 5, rTextureCoord: 0, + topColor: redColor, bottomColor: redColor }, + // Note that an rTextureCoord of 4.0 satisfies the need to + // have it be >= 1.0 for the TEXTURE_3D case, and also its + // use as an index in the TEXTURE_2D_ARRAY case. + { flipY: false, sourceSubRectangle: [32, 16, 16, 16], depth: 5, rTextureCoord: 4, + topColor: greenColor, bottomColor: greenColor }, + { flipY: false, sourceSubRectangle: [24, 48, 32, 32], depth: 1, rTextureCoord: 0, + topColor: greenColor, bottomColor: redColor }, + { flipY: true, sourceSubRectangle: [24, 48, 32, 32], depth: 1, rTextureCoord: 0, + topColor: redColor, bottomColor: greenColor }, + + // Use UNPACK_IMAGE_HEIGHT to skip some pixels. + { flipY: false, sourceSubRectangle: [32, 16, 16, 16], depth: 2, unpackImageHeight: 64, rTextureCoord: 0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [32, 16, 16, 16], depth: 2, unpackImageHeight: 64, rTextureCoord: 1, + topColor: greenColor, bottomColor: greenColor }, + ]; + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_3D) { + program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + resolve("SUCCESS"); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.type); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, runTest); + } + function runTest() { + for (var i in cases) { + runOneIteration(video, cases[i].flipY, false, + cases[i].topColor, cases[i].bottomColor, + program, bindingTarget, cases[i].depth, + cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, + cases[i].rTextureCoord); + runOneIteration(video, cases[i].flipY, true, + cases[i].topColor, cases[i].bottomColor, + program, bindingTarget, cases[i].depth, + cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, + cases[i].rTextureCoord); + } + runNextVideo(); + } + runNextVideo(); + }); + } + + runTexImageTest(gl.TEXTURE_3D).then(function(val) { + runTexImageTest(gl.TEXTURE_2D_ARRAY).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-webgl-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-webgl-canvas.js new file mode 100644 index 0000000000..fe14b0c8eb --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-webgl-canvas.js @@ -0,0 +1,212 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking webgl canvas elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + + ctx.viewport(0, 0, width, height); + + ctx.enable(ctx.SCISSOR_TEST); + ctx.scissor(0, 0, width, halfHeight); + ctx.clearColor(1.0, 0, 0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.scissor(0, halfHeight, width, height - halfHeight); + ctx.clearColor(0.0, 1.0, 0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.disable(ctx.SCISSOR_TEST); + } + + function setCanvasTo257x257(ctx, bindingTarget) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + function setCanvasToMin(ctx, bindingTarget) { + ctx.canvas.width = 1; + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function runOneIteration(canvas, flipY, program, bindingTarget, opt_texture) + { + var objType = 'canvas'; + if (canvas.transferToImageBitmap) + objType = 'OffscreenCanvas'; + else if (canvas.parentNode) + objType = 'canvas attached to DOM'; + debug('Testing flipY=' + flipY + ' object type: ' + objType + + ' bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + ' canvas size: ' + canvas.width + 'x' + canvas.height + ' with red-green'); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (!opt_texture) { + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + var texture = opt_texture; + } + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + + // Upload the image into the texture + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], canvas.width, canvas.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, canvas.width, canvas.height, 1 /* depth */, + gl[pixelFormat], gl[pixelType], canvas); + + var width = gl.canvas.width; + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + var top = flipY ? (height - halfHeight) : 0; + var bottom = flipY ? 0 : (height - halfHeight); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); + + // Check the top and bottom halves and make sure they have the right color. + debug("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, 0, bottom, width, halfHeight, redColor, "shouldBe " + redColor); + debug("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, 0, top, width, halfHeight, greenColor, "shouldBe " + greenColor); + + if (false) { + var ma = wtu.makeImageFromCanvas(canvas); + document.getElementById("console").appendChild(ma); + + var m = wtu.makeImageFromCanvas(gl.canvas); + document.getElementById("console").appendChild(m); + document.getElementById("console").appendChild(document.createElement("hr")); + } + + return texture; + } + + function runTest() + { + var ctx = wtu.create3DContext(); + var canvas = ctx.canvas; + // Note: We use preserveDrawingBuffer:true to prevent canvas + // visibility from interfering with the tests. + var visibleCtx = wtu.create3DContext(null, { preserveDrawingBuffer:true }); + var visibleCanvas = visibleCtx.canvas; + var descriptionNode = document.getElementById("description"); + document.body.insertBefore(visibleCanvas, descriptionNode); + + var cases = [ + { flipY: true, ctx: ctx, init: setCanvasToMin }, + { flipY: false, ctx: ctx }, + { flipY: true, ctx: ctx, init: setCanvasTo257x257 }, + { flipY: false, ctx: ctx }, + { flipY: true, ctx: visibleCtx, init: setCanvasToMin}, + { flipY: false, ctx: visibleCtx }, + ]; + + if (window.OffscreenCanvas) { + var offscreen = new OffscreenCanvas(1, 1); + var offscreenCtx = wtu.create3DContext(offscreen); + cases = cases.concat([ + { flipY: true, ctx: offscreenCtx, init: setCanvasToMin }, + { flipY: false, ctx: offscreenCtx }, + ]); + } + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_3D) { + program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var count = 4; + var caseNdx = 0; + var texture = undefined; + function runNextTest() { + var c = cases[caseNdx]; + if (c.init) { + c.init(c.ctx, bindingTarget); + } + texture = runOneIteration(c.ctx.canvas, c.flipY, program, bindingTarget, texture); + // for the first 2 iterations always make a new texture. + if (count < 2) { + gl.deleteTexture(texture); + texture = undefined; + } + ++caseNdx; + if (caseNdx == cases.length) { + caseNdx = 0; + --count; + if (!count) { + resolve("SUCCESS"); + return; + } + } + wtu.waitForComposite(runNextTest); + } + runNextTest(); + }); + } + + runTexImageTest(gl.TEXTURE_3D).then(function(val) { + runTexImageTest(gl.TEXTURE_2D_ARRAY).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-utils.js new file mode 100644 index 0000000000..f37f12fe91 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-utils.js @@ -0,0 +1,865 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +var TexImageUtils = (function() { + + "use strict"; + + var wtu = WebGLTestUtils; + + /** + * A vertex shader for a single texture. + * @type {string} + */ + var simpleTextureVertexShaderES3 = [ + '#version 300 es', + 'in vec4 vPosition;', + 'in vec2 texCoord0;', + 'out vec2 texCoord;', + 'void main() {', + ' gl_Position = vPosition;', + ' texCoord = texCoord0;', + '}'].join('\n'); + + /** + * A fragment shader for a single unsigned integer texture. + * @type {string} + */ + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simpleUintTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump usampler2D tex;', + 'in vec2 texCoord;', + 'out vec4 fragData;', + 'void main() {', + ' uvec4 data = texture(tex, texCoord);', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single signed integer texture. + * @type {string} + */ + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simpleIntTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump isampler2D tex;', + 'in vec2 texCoord;', + 'out vec4 fragData;', + 'void main() {', + ' ivec4 data = texture(tex, texCoord);', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single cube map unsigned integer texture. + * @type {string} + */ + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simpleCubeMapUintTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump usamplerCube tex;', + 'uniform highp int face;', + 'in vec2 texCoord;', + 'out vec4 fragData;', + 'void main() {', + // Transform [0, 1] -> [-1, 1] + ' vec2 texC2 = (texCoord * 2.) - 1.;', + // Transform 2d tex coord. to each face of TEXTURE_CUBE_MAP coord. + ' vec3 texCube = vec3(0., 0., 0.);', + ' if (face == 34069) {', // TEXTURE_CUBE_MAP_POSITIVE_X + ' texCube = vec3(1., -texC2.y, -texC2.x);', + ' } else if (face == 34070) {', // TEXTURE_CUBE_MAP_NEGATIVE_X + ' texCube = vec3(-1., -texC2.y, texC2.x);', + ' } else if (face == 34071) {', // TEXTURE_CUBE_MAP_POSITIVE_Y + ' texCube = vec3(texC2.x, 1., texC2.y);', + ' } else if (face == 34072) {', // TEXTURE_CUBE_MAP_NEGATIVE_Y + ' texCube = vec3(texC2.x, -1., -texC2.y);', + ' } else if (face == 34073) {', // TEXTURE_CUBE_MAP_POSITIVE_Z + ' texCube = vec3(texC2.x, -texC2.y, 1.);', + ' } else if (face == 34074) {', // TEXTURE_CUBE_MAP_NEGATIVE_Z + ' texCube = vec3(-texC2.x, -texC2.y, -1.);', + ' }', + ' uvec4 data = texture(tex, texCube);', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single cube map signed integer texture. + * @type {string} + */ + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simpleCubeMapIntTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump isamplerCube tex;', + 'uniform highp int face;', + 'in vec2 texCoord;', + 'out vec4 fragData;', + 'void main() {', + // Transform [0, 1] -> [-1, 1] + ' vec2 texC2 = (texCoord * 2.) - 1.;', + // Transform 2d tex coord. to each face of TEXTURE_CUBE_MAP coord. + ' vec3 texCube = vec3(0., 0., 0.);', + ' if (face == 34069) {', // TEXTURE_CUBE_MAP_POSITIVE_X + ' texCube = vec3(1., -texC2.y, -texC2.x);', + ' } else if (face == 34070) {', // TEXTURE_CUBE_MAP_NEGATIVE_X + ' texCube = vec3(-1., -texC2.y, texC2.x);', + ' } else if (face == 34071) {', // TEXTURE_CUBE_MAP_POSITIVE_Y + ' texCube = vec3(texC2.x, 1., texC2.y);', + ' } else if (face == 34072) {', // TEXTURE_CUBE_MAP_NEGATIVE_Y + ' texCube = vec3(texC2.x, -1., -texC2.y);', + ' } else if (face == 34073) {', // TEXTURE_CUBE_MAP_POSITIVE_Z + ' texCube = vec3(texC2.x, -texC2.y, 1.);', + ' } else if (face == 34074) {', // TEXTURE_CUBE_MAP_NEGATIVE_Z + ' texCube = vec3(-texC2.x, -texC2.y, -1.);', + ' }', + ' ivec4 data = texture(tex, texCube);', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 3D texture. + * @type {string} + */ + // Note that the tex coordinate r (the uniform uRCoord) is set to 0.0 by default. + var simple3DTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump sampler3D tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' fragData = vec4(texture(tex, vec3(texCoord, uRCoord)).rgb, 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 3D unsigned integer texture. + * @type {string} + */ + // Note that the tex coordinate r (the uniform uRCoord) is set to 0.0 by default. + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simple3DUintTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump usampler3D tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' uvec4 data = texture(tex, vec3(texCoord, uRCoord));', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 3D signed integer texture. + * @type {string} + */ + // Note that the tex coordinate r (the uniform uRCoord) is set to 0.0 by default. + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simple3DIntTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump isampler3D tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' ivec4 data = texture(tex, vec3(texCoord, uRCoord));', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 2D_ARRAY texture. + * @type {string} + */ + // Note that the first image in the array (selected by the uniform + // uRCoord) is used by default. + var simple2DArrayTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump sampler2DArray tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' fragData = vec4(texture(tex, vec3(texCoord, uRCoord)).rgb, 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 2D_ARRAY unsigned integer texture. + * @type {string} + */ + // Note that the first image in the array (selected by the uniform + // uRCoord) is used by default. + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simple2DArrayUintTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump usampler2DArray tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' uvec4 data = texture(tex, vec3(texCoord, uRCoord));', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 2D_ARRAY signed integer texture. + * @type {string} + */ + // Note that the first image in the array (selected by the uniform + // uRCoord) is used by default. + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simple2DArrayIntTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump isampler2DArray tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' ivec4 data = texture(tex, vec3(texCoord, uRCoord));', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + + /** + * Creates a simple texture vertex shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleTextureVertexShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleTextureVertexShaderES3, gl.VERTEX_SHADER); + }; + + /** + * Creates a simple unsigned integer texture fragment shader. + * Output is scaled by 1/255 to bring the result into normalized float range. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleUintTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleUintTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple signed integer texture fragment shader. + * Output is scaled by 1/255 to bring the result into normalized float range. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleIntTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleIntTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple cube map unsigned integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleCubeMapUintTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleCubeMapUintTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple cube map signed integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleCubeMapIntTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleCubeMapIntTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 3D texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple3DTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple3DTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 3D unsigned integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple3DUintTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple3DUintTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 3D signed integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple3DIntTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple3DIntTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 2D_ARRAY texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple2DArrayTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple2DArrayTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 2D_ARRAY integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple2DArrayUintTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple2DArrayUintTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple unsigned integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimpleUintTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimpleUintTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple signed integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimpleIntTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimpleIntTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple cube map unsigned integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimpleCubeMapUintTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl); + var fs = setupSimpleCubeMapUintTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple cube map signed integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimpleCubeMapIntTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl); + var fs = setupSimpleCubeMapIntTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 3D texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple3DTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple3DTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 3D unsigned integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple3DUintTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple3DUintTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 3D signed integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple3DIntTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple3DIntTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 2D_ARRAY texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple2DArrayTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple2DArrayTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 2D_ARRAY unsigned integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple2DArrayUintTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple2DArrayUintTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 2D_ARRAY signed integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple2DArrayIntTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple2DArrayIntTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a program and buffers for rendering a unsigned integer textured quad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLProgram} + */ + var setupUintTexturedQuad = function(gl) { + var program = setupSimpleUintTextureProgram(gl); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Creates a program and buffers for rendering a signed integer textured quad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLProgram} + */ + var setupIntTexturedQuad = function(gl) { + var program = setupSimpleIntTextureProgram(gl); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Creates a program and buffers for rendering a textured quad with + * a cube map unsigned integer texture. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLProgram} + */ + var setupUintTexturedQuadWithCubeMap = function(gl) + { + var program = setupSimpleCubeMapUintTextureProgram(gl); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Creates a program and buffers for rendering a textured quad with + * a cube map signed integer texture. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLProgram} + */ + var setupIntTexturedQuadWithCubeMap = function(gl) + { + var program = setupSimpleCubeMapIntTextureProgram(gl); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Does the GL internal format represent an unsigned integer format + * texture? + * @return {boolean} + */ + var isUintFormat = function(internalFormat) + { + return (internalFormat == "R8UI" || internalFormat == "RG8UI" || internalFormat == "RGB8UI" || internalFormat == "RGBA8UI" || + internalFormat == "R16UI" || internalFormat == "RG16UI" || internalFormat == "RGB16UI" || internalFormat == "RGBA16UI" || + internalFormat == "R32UI" || internalFormat == "RG32UI" || internalFormat == "RGB32UI" || internalFormat == "RGBA32UI"); + }; + + /** + * Does the GL internal format represent an signed integer format + * texture? + * @return {boolean} + */ + var isIntFormat = function(internalFormat) + { + return (internalFormat == "R8I" || internalFormat == "RG8I" || internalFormat == "RGB8I" || internalFormat == "RGBA8I" || + internalFormat == "R16I" || internalFormat == "RG16I" || internalFormat == "RGB16I" || internalFormat == "RGBA16I" || + internalFormat == "R32I" || internalFormat == "RG32I" || internalFormat == "RGB32I" || internalFormat == "RGBA32I"); + }; + + /** + * Createa a program and buffers for rendering a textured quad for + * tex-image-and-sub-image tests. Handle selection of correct + * program to handle texture format. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} internalFormat The internal format for texture to be tested. + */ + var setupTexturedQuad = function(gl, internalFormat) + { + if (isUintFormat(internalFormat)) + return setupUintTexturedQuad(gl); + if (isIntFormat(internalFormat)) + return setupIntTexturedQuad(gl); + return wtu.setupTexturedQuad(gl); + }; + + /** + * Createa a program and buffers for rendering a textured quad with + * a cube map for tex-image-and-sub-image tests. Handle selection of + * correct program to handle texture format. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} internalFormat The internal format for texture to be tested. + */ + function setupTexturedQuadWithCubeMap(gl, internalFormat) + { + if (isUintFormat(internalFormat)) + return setupUintTexturedQuadWithCubeMap(gl); + if (isIntFormat(internalFormat)) + return setupIntTexturedQuadWithCubeMap(gl); + return wtu.setupTexturedQuadWithCubeMap(gl); + } + + /** + * Createa a program and buffers for rendering a textured quad with a 3D texture + * for tex-image-and-sub-image tests. Handle selection of correct + * program to handle texture format. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} internalFormat The internal format for texture to be tested. + */ + var setupTexturedQuadWith3D = function(gl, internalFormat) + { + var program; + if (isUintFormat(internalFormat)) + program = setupSimple3DUintTextureProgram(gl); + else if (isIntFormat(internalFormat)) + program = setupSimple3DIntTextureProgram(gl); + else + program = setupSimple3DTextureProgram(gl); + var uRCoordLoc = gl.getUniformLocation(program, 'uRCoord'); + gl.uniform1f(uRCoordLoc, 0.0); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Createa a program and buffers for rendering a textured quad with a 2D_ARRAY + * texture for tex-image-and-sub-image tests. Handle selection of correct + * program to handle texture format. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} internalFormat The internal format for texture to be tested. + */ + var setupTexturedQuadWith2DArray = function(gl, internalFormat) + { + var program; + if (isUintFormat(internalFormat)) + program = setupSimple2DArrayUintTextureProgram(gl); + else if (isIntFormat(internalFormat)) + program = setupSimple2DArrayIntTextureProgram(gl); + else + program = setupSimple2DArrayTextureProgram(gl); + var uRCoordLoc = gl.getUniformLocation(program, 'uRCoord'); + gl.uniform1f(uRCoordLoc, 0.0); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Return a list of unpack color spaces to test, supported by the specified + * WebGLRenderingContext. + */ + var unpackColorSpacesToTest = function(gl) + { + if ('unpackColorSpace' in gl) + return ['srgb', 'display-p3']; + else + return [undefined]; + } + + /** + * For each entry in unpackColorSpaces, duplicate all of cases, adding an + * unpackColorSpace key with its value set to that entry to each case. + */ + var crossProductTestCasesWithUnpackColorSpaces = function(testCaseList, unpackColorSpaces) + { + var testCaseWithUnpackColorSpace = function(testCase, colorSpace) + { + return {...testCase, ...{unpackColorSpace:colorSpace}}; + } + var listOfTestCaseLists = unpackColorSpaces.map(colorSpace => + testCaseList.map(testCase => testCaseWithUnpackColorSpace(testCase, colorSpace))); + return listOfTestCaseLists.flat(); + } + + /** + * Given given an internalformat, format, and type, return the tolerance + * that should be used when comparing an input 8-bit value to one that has + * been truncated through the specified formats. + */ + var tolerance = function(internalformat, format, type) { + function typeTolerance(type) { + switch(type) { + case 'UNSIGNED_SHORT_5_6_5': + case 'UNSIGNED_SHORT_5_5_5_1': + return 255 / 31; + case 'UNSIGNED_SHORT_4_4_4_4': + return 255 / 15; + break; + default: + return 1; + } + }; + function formatTolerance(format) { + switch(format) { + case 'RGB565': + case 'RGB5_A1': + return 255/31; + case 'RGBA4': + return 255/15; + default: + return 1; + } + }; + return Math.max(formatTolerance(internalformat), + formatTolerance(format), + typeTolerance(type)); + } + + return { + setupTexturedQuad: setupTexturedQuad, + setupTexturedQuadWithCubeMap: setupTexturedQuadWithCubeMap, + setupTexturedQuadWith3D: setupTexturedQuadWith3D, + setupTexturedQuadWith2DArray: setupTexturedQuadWith2DArray, + unpackColorSpacesToTest: unpackColorSpacesToTest, + crossProductTestCasesWithUnpackColorSpaces: crossProductTestCasesWithUnpackColorSpaces, + tolerance: tolerance + }; + +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-with-image-bitmap-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-with-image-bitmap-utils.js new file mode 100644 index 0000000000..8faedf9eaa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-with-image-bitmap-utils.js @@ -0,0 +1,435 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + + +function runOneIterationImageBitmapTest(useTexSubImage, bindingTarget, program, bitmap, flipY, premultiplyAlpha, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance) +{ + var halfRed = [128, 0, 0]; + var halfGreen = [0, 128, 0]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var blackColor = [0, 0, 0]; + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + halfGreen = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + halfRed = [128, 128, 128]; + halfGreen = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + halfRed = [0, 0, 0]; + halfGreen = [0, 0, 0]; + break; + default: + break; + } + + switch (gl[internalFormat]) { + case gl.SRGB8: + case gl.SRGB8_ALPHA8: + halfRed = wtu.sRGBToLinear(halfRed); + halfGreen = wtu.sRGBToLinear(halfGreen); + break; + default: + break; + } + + var str; + if (optionsVal.is3D) { + str = 'Testing ' + (useTexSubImage ? 'texSubImage3D' : 'texImage3D') + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY'); + } else { + str = 'Testing ' + (useTexSubImage ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP'); + } + debug(str); + bufferedLogToConsole(str); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + var targets = [bindingTarget]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + + bufferedLogToConsole("Start uploading the image into a texture"); + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (optionsVal.is3D) { + gl.texImage3D(targets[tt], 0, gl[internalFormat], bitmap.width, bitmap.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(targets[tt], 0, 0, 0, 0, bitmap.width, bitmap.height, 1, + gl[pixelFormat], gl[pixelType], bitmap); + } else { + if (useTexSubImage) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], bitmap.width, bitmap.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], bitmap); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], bitmap); + } + } + } + bufferedLogToConsole("Uploading into texture completed"); + + var width = gl.canvas.width; + var halfWidth = Math.floor(width / 2); + var quarterWidth = Math.floor(halfWidth / 2); + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + var quarterHeight = Math.floor(halfHeight / 2); + + var top = flipY ? quarterHeight : (height - halfHeight + quarterHeight); + var bottom = flipY ? (height - halfHeight + quarterHeight) : quarterHeight; + var left = quarterWidth; + var right = halfWidth + quarterWidth / 2; + + var tl = redColor; + var tr = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfRed : (optionsVal.alpha == 1) ? redColor : blackColor) : redColor; + var bl = greenColor; + var br = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfGreen : (optionsVal.alpha == 1) ? greenColor : blackColor) : greenColor; + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + // Check the top pixel and bottom pixel and make sure they have + // the right color. + let skipAlphaTests = (premultiplyAlpha === undefined && optionsVal.alpha != 1.0); + let skipStr = " (Skipping checking right pixel since premultiplyAlpha was undefined and alpha != 1.0)"; + bufferedLogToConsole("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, left, bottom, 2, 2, tl, "shouldBe " + tl + " +/-" + tolerance, tolerance); + if (skipAlphaTests) { + bufferedLogToConsole(skipStr); + } else { + wtu.checkCanvasRect(gl, right, bottom, 2, 2, tr, "shouldBe " + tr + " +/-" + tolerance, tolerance); + } + bufferedLogToConsole("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, left, top, 2, 2, bl, "shouldBe " + bl + " +/-" + tolerance, tolerance); + if (skipAlphaTests) { + bufferedLogToConsole(skipStr); + } else { + wtu.checkCanvasRect(gl, right, top, 2, 2, br, "shouldBe " + br + " +/-" + tolerance, tolerance); + } + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); +} + +function resetUnpackParams(gl) +{ + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0); + gl.pixelStorei(gl.UNPACK_ROW_LENGTH, 0); + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0); +} + +function runOneIterationImageBitmapTestSubSource(useTexSubImage, bindingTarget, program, bitmap, flipY, premultiplyAlpha, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance) +{ + var halfRed = [128, 0, 0]; + var halfGreen = [0, 128, 0]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var blackColor = [0, 0, 0]; + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + halfGreen = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + halfRed = [128, 128, 128]; + halfGreen = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + halfRed = [0, 0, 0]; + halfGreen = [0, 0, 0]; + break; + default: + break; + } + + switch (gl[internalFormat]) { + case gl.SRGB8: + case gl.SRGB8_ALPHA8: + halfRed = wtu.sRGBToLinear(halfRed); + halfGreen = wtu.sRGBToLinear(halfGreen); + break; + default: + break; + } + + var str; + if (optionsVal.is3D) { + str = 'Testing ' + (useTexSubImage ? 'texSubImage3D' : 'texImage3D') + '[SubSource]' + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=TEXTURE_3D'; + } else { + str = 'Testing ' + (useTexSubImage ? 'texSubImage2D' : 'texImage2D') + '[SubSource]' + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=TEXTURE_2D'; + } + debug(str); + bufferedLogToConsole(str); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + var srcTL = redColor; + var srcTR = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfRed : (optionsVal.alpha == 1) ? redColor : blackColor) : redColor; + var srcBL = greenColor; + var srcBR = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfGreen : (optionsVal.alpha == 1) ? greenColor : blackColor) : greenColor; + + var tl, tr, bl, br; + + bufferedLogToConsole("Start uploading the image into a texture"); + // Upload the image into the texture + if (optionsVal.is3D) { + if (useTexSubImage) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + // Only upload the left half image to the right half texture. + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0); + gl.texSubImage3D(bindingTarget, 0, bitmap.width / 2, 0, 0, bitmap.width / 2, bitmap.height, 1, + gl[pixelFormat], gl[pixelType], bitmap); + tl = blackColor; + tr = srcTL; + bl = blackColor; + br = srcBL; + } else { + // Only upload the bottom middle quarter image + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, bitmap.height / 2); + gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0); + gl.texImage3D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height / 2, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], bitmap); + if (!flipY) { + tl = srcBL; + tr = srcBR; + bl = srcBL; + br = srcBR; + } else { + tl = srcTL; + tr = srcTR; + bl = srcTL; + br = srcTR; + } + } + } else { + if (useTexSubImage) { + // Initialize the texture to black first + gl.texImage2D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height, 0, + gl[pixelFormat], gl[pixelType], null); + // Only upload the left half image to the right half texture. + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.texSubImage2D(bindingTarget, 0, bitmap.width / 2, 0, bitmap.width / 2, bitmap.height, + gl[pixelFormat], gl[pixelType], bitmap); + tl = blackColor; + tr = srcTL; + bl = blackColor; + br = srcBL; + } else { + // Only upload the right bottom image. + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, bitmap.width / 2); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, bitmap.height / 2); + gl.texImage2D(bindingTarget, 0, gl[internalFormat], bitmap.width / 2, bitmap.height / 2, 0, + gl[pixelFormat], gl[pixelType], bitmap); + resetUnpackParams(gl); + if (!flipY) { + tl = srcBR; + tr = srcBR; + bl = srcBR; + br = srcBR; + } else { + tl = srcTR; + tr = srcTR; + bl = srcTR; + br = srcTR; + } + } + } + bufferedLogToConsole("Uploading into texture completed"); + + var width = gl.canvas.width; + var halfWidth = Math.floor(width / 2); + var quarterWidth = Math.floor(halfWidth / 2); + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + var quarterHeight = Math.floor(halfHeight / 2); + + var top = flipY ? quarterHeight : (height - halfHeight + quarterHeight); + var bottom = flipY ? (height - halfHeight + quarterHeight) : quarterHeight; + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + // Check the top pixel and bottom pixel and make sure they have + // the right color. + // For right side, check pixels closer to left to avoid border in the video tests. + let skipAlphaTests = (premultiplyAlpha === undefined && optionsVal.alpha != 1.0); + let skipStr = " (Skipping checking right pixel since premultiplyAlpha was undefined and alpha != 1.0)"; + bufferedLogToConsole("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, quarterWidth, bottom, 2, 2, tl, "shouldBe " + tl + " +/-" + tolerance, tolerance); + if (skipAlphaTests) { + bufferedLogToConsole(skipStr); + } else { + wtu.checkCanvasRect(gl, halfWidth + quarterWidth / 2, bottom, 2, 2, tr, "shouldBe " + tr + " +/-" + tolerance, tolerance); + } + bufferedLogToConsole("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, quarterWidth, top, 2, 2, bl, "shouldBe " + bl + " +/-" + tolerance, tolerance); + if (skipAlphaTests) { + bufferedLogToConsole(skipStr); + } else { + wtu.checkCanvasRect(gl, halfWidth + quarterWidth / 2, top, 2, 2, br, "shouldBe " + br + " +/-" + tolerance, tolerance); + } + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); +} + +function runTestOnBindingTargetImageBitmap(bindingTarget, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance) +{ + cases.forEach(x => { + runOneIterationImageBitmapTest(x.sub, bindingTarget, program, x.bitmap, + x.bitmap.flipY, x.bitmap.premultiply, optionsVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + }); + + if (wtu.getDefault3DContextVersion() <= 1 || + (bindingTarget == gl.TEXTURE_CUBE_MAP || bindingTarget == gl.TEXTURE_2D_ARRAY)) + { + // Skip testing source sub region on TEXTURE_CUBE_MAP and TEXTURE_2D_ARRAY on WebGL2 to save + // running time. + return; + } + + cases.forEach(x => { + runOneIterationImageBitmapTestSubSource(x.sub, bindingTarget, program, x.bitmap, + x.bitmap.flipY, x.bitmap.premultiply, optionsVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + }); +} + +function runImageBitmapTestInternal(bitmaps, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, tolerance) +{ + var cases = []; + bitmaps.forEach(bitmap => { + cases.push({bitmap: bitmap, sub: false}); + cases.push({bitmap: bitmap, sub: true}); + }); + + var optionsVal = {alpha: alphaVal, is3D: is3D}; + var program; + if (is3D) { + program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + runTestOnBindingTargetImageBitmap(gl.TEXTURE_3D, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + } else { + program = tiu.setupTexturedQuad(gl, internalFormat); + runTestOnBindingTargetImageBitmap(gl.TEXTURE_2D, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + } + + // cube map texture must be square + if (bitmaps[0].width == bitmaps[0].height) { + if (is3D) { + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + runTestOnBindingTargetImageBitmap(gl.TEXTURE_2D_ARRAY, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + runTestOnBindingTargetImageBitmap(gl.TEXTURE_CUBE_MAP, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + } + } +} + +function runImageBitmapTest(source, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, opt_tolerance) +{ + if (opt_tolerance === undefined) { + opt_tolerance = 10; + } + var p1 = createImageBitmap(source, {imageOrientation: "none", premultiplyAlpha: "premultiply"}) + .then(cur => { cur.flipY = false; cur.premultiply = true; return cur; }); + var p2 = createImageBitmap(source, {imageOrientation: "none", premultiplyAlpha: "none"}) + .then(cur => { cur.flipY = false; cur.premultiply = false; return cur; }); + var p3 = createImageBitmap(source, {imageOrientation: "flipY", premultiplyAlpha: "premultiply"}) + .then(cur => { cur.flipY = true; cur.premultiply = true; return cur; }); + var p4 = createImageBitmap(source, {imageOrientation: "flipY", premultiplyAlpha: "none"}) + .then(cur => { cur.flipY = true; cur.premultiply = false; return cur; }); + return Promise.all([p1, p2, p3, p4]) + .catch( () => { + testPassed("createImageBitmap with options may be rejected if it is not supported. Retrying without options."); + // The ImageBitmap's premultiplyAlpha setting will implicitly be + // "default", and per spec: + // https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting + // this value is implementation-dependent (either premultiplied or + // not). Skip testing the quadrants which have alpha != 1.0. + var p = createImageBitmap(source) + .then(cur => { cur.flipY = false; cur.premultiply = undefined; return cur; }); + return Promise.all([p]); + }).then( bitmaps => { + bufferedLogToConsole("All createImageBitmap promises are resolved"); + runImageBitmapTestInternal(bitmaps, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, opt_tolerance); + }, (e) => { + // This will fail here when running from file:// instead of https://. + testFailed("createImageBitmap(source) failed: \"" + e.message + "\""); + }); +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-input-validation.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-input-validation.js new file mode 100644 index 0000000000..22261afdb4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-input-validation.js @@ -0,0 +1,563 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +description("Validate tex functions input parameters"); + +var wtu = WebGLTestUtils; +var gl = null; +var tex = null; +var error = 0; + +shouldBeNonNull("gl = wtu.create3DContext(undefined, undefined, contextVersion)"); +shouldBeNonNull("tex = gl.createTexture()"); +gl.bindTexture(gl.TEXTURE_2D, tex); +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +function enumToString(value) { + return wtu.glEnumToString(gl, value); +} + +function testTexParameter(testCase) { + var msg = "paramName: " + enumToString(testCase.pname); + error = testCase.expectedError; + gl.texParameteri(testCase.target, testCase.pname, testCase.param); + wtu.glErrorShouldBe(gl, error, msg); + gl.texParameterf(testCase.target, testCase.pname, testCase.param); + wtu.glErrorShouldBe(gl, error, msg); +} + +function testGetTexParameter(testCase) { + var msg = "paramName: " + enumToString(testCase.pname); + error = testCase.expectedError; + gl.getTexParameter(testCase.target, testCase.pname); + wtu.glErrorShouldBe(gl, error, msg); +} + +function testTexImage2D(testCase) { + var level = 0; + var width = 16; + var height = 16; + var msg = " internalFormat: " + enumToString(testCase.internalFormat) + + " target: " + enumToString(testCase.target) + + " format: " + enumToString(testCase.format) + + " type: " + enumToString(testCase.type) + + " border: " + testCase.border; + + gl.texImage2D(testCase.target, level, testCase.internalFormat, width, height, testCase.border, testCase.format, testCase.type, null); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testTexSubImage2D(testCase) { + var level = 0; + var xoffset = 0; + var yoffset = 0; + var width = 16; + var height = 16; + var msg = " format: " + enumToString(testCase.format) + + " type: " + enumToString(testCase.type); + var array = new Uint8Array(width * height * 4); + + gl.texSubImage2D(testCase.target, level, xoffset, yoffset, width, height, testCase.format, testCase.type, array); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testCopyTexImage2D(testCase) { + var level = 0; + var x = 0; + var y = 0; + var width = 16; + var height = 16; + var msg = " colorBufferFormat: " + enumToString(testCase.colorBufferFormat) + + " internalFormat: " + enumToString(testCase.internalFormat) + + " target: " + enumToString(testCase.target) + + " border: " + testCase.border; + + gl.renderbufferStorage(gl.RENDERBUFFER, testCase.colorBufferFormat, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + gl.copyTexImage2D(testCase.target, level, testCase.internalFormat, x, y, width, height, testCase.border); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testCopyTexSubImage2D(testCase) { + var level = 0; + var x = 0; + var y = 0; + var width = 16; + var height = 16; + var xoffset = 0; + var yoffset = 0; + var border = 0; + var type = gl.UNSIGNED_BYTE; + var msg = " colorBufferFormat: " + enumToString(testCase.colorBufferFormat) + + " internalFormat: " + enumToString(testCase.internalFormat) + + " target: " + enumToString(testCase.target); + + gl.renderbufferStorage(gl.RENDERBUFFER, testCase.colorBufferFormat, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + gl.texImage2D(testCase.target, level, testCase.internalFormat, xoffset + width, yoffset + height, border, testCase.internalFormat, type, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + gl.copyTexSubImage2D(testCase.target, level, xoffset, yoffset, x, y, width, height); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testCopyFromInternalFBO(testCase) { + var target = gl.TEXTURE_2D; + var level = 0; + var x = 0; + var y = 0; + var width = 16; + var height = 16; + var xoffset = 0; + var yoffset = 0; + var border = 0; + var type = gl.UNSIGNED_BYTE; + var msg = " colorBufferFormat: " + enumToString(testCase.contextAlpha ? gl.RGBA : gl.RGB) + + " internalFormat: " + enumToString(testCase.internalFormat); + + if (testCase.contextAlpha) { + gl = wtu.create3DContext(null, { alpha: true }, contextVersion); + } else { + gl = wtu.create3DContext(null, { alpha: false }, contextVersion); + } + shouldBeNonNull("gl"); + shouldBeNonNull("tex = gl.createTexture()"); + gl.bindTexture(target, tex); + if (testCase.subImage) { + gl.texImage2D(target, level, testCase.internalFormat, xoffset + width, yoffset + height, border, testCase.internalFormat, type, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } else { + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.copyTexImage2D(target, level, testCase.internalFormat, x, y, width, height, border); + } + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +// Only for WebGL2.0. +function testTexImage3D(testCase) { + var level = 0; + var width = 16; + var height = 16; + var depth = 16; + var msg = " internalFormat: " + enumToString(testCase.internalFormat) + + " target: " + enumToString(testCase.target) + + " format: " + enumToString(testCase.format) + + " type: " + enumToString(testCase.type) + + " border: " + testCase.border; + + gl.texImage3D(testCase.target, level, testCase.internalFormat, width, height, depth, testCase.border, testCase.format, testCase.type, null); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testTexSubImage3D(testCase) { + var level = 0; + var xoffset = 0; + var yoffset = 0; + var zoffset = 0; + var width = 16; + var height = 16; + var depth = 16; + var msg = " format: " + enumToString(testCase.format) + + " type: " + enumToString(testCase.type); + var array = new Uint8Array(width * height * depth * 4); + + gl.texSubImage3D(testCase.target, level, xoffset, yoffset, zoffset, width, height, depth, testCase.format, testCase.type, array); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + + +// Start checking. + +debug(""); +debug("Checking TexParameter: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: 0x0DE0, // GL_TEXTURE_1D + pname: gl.TEXTURE_WRAP_T, + param: gl.REPEAT, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + pname: gl.TEXTURE_WRAP_T, + param: 0x2900, // GL_CLAMP + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + pname: gl.TEXTURE_WRAP_T, + param: gl.REPEAT, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion < 2) { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + pname: 0x813A, // GL_TEXTURE_MIN_LOD + param: 0, + expectedError: gl.INVALID_ENUM } + ]); +} else { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + pname: 0x8E42, // GL_TEXTURE_SWIZZLE_R + param: 0x1903, // GL_RED + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + pname: 0x8072, // GL_TEXTURE_WRAP_R + param: 0x2900, // GL_CLAMP + expectedError: gl.INVALID_ENUM } + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexParameter(testCases[ii]); +} + +debug(""); +debug("Checking GetTexParameter: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: 0x0DE0, // GL_TEXTURE_1D + pname: gl.TEXTURE_WRAP_T, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + pname: gl.TEXTURE_WRAP_T, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion < 2) { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + pname: 0x813A, // GL_TEXTURE_MIN_LOD + expectedError: gl.INVALID_ENUM } + ]); +} else { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + pname: 0x8E42, // GL_TEXTURE_SWIZZLE_R + expectedError: gl.INVALID_ENUM } + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testGetTexParameter(testCases[ii]); +} + +debug(""); +debug("Checking TexImage2D: a set of inputs that are valid in GL but invalid in WebGL"); + +var testCases = [ + { target: 0x8064, // GL_PROXY_TEXTURE_2D + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + internalFormat: 0x1903, // GL_RED + border: 0, + format: 0x1903, // GL_RED + type: gl.UNSIGNED_BYTE, + expectedError: [gl.INVALID_ENUM, gl.INVALID_VALUE, gl.INVALID_OPERATION] }, + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 1, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_VALUE }, + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGB, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion < 2) { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.BYTE, + expectedError: gl.INVALID_ENUM } + ]); +} else { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM } + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexImage2D(testCases[ii]); +} + +debug(""); +debug("Checking TexSubImage2D: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: gl.TEXTURE_2D, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion < 2) { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + format: 0x1903, // GL_RED + type: gl.UNSIGNED_BYTE, + expectedError: [gl.INVALID_ENUM, gl.INVALID_OPERATION] }, + { target: gl.TEXTURE_2D, + format: gl.RGBA, + type: gl.BYTE, + expectedError: [gl.INVALID_ENUM, gl.INVALID_OPERATION] } + ]); +} else { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + format: gl.RED, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_2D, + format: gl.RGBA, + type: gl.BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_3D, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexSubImage2D(testCases[ii]); +} + +debug(""); +debug("Checking CopyTexImage2D: a set of inputs that are valid in GL but invalid in WebGL"); + +var colorBuffer = null; +var fbo = null; + +shouldBeNonNull("fbo = gl.createFramebuffer()"); +gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); +shouldBeNonNull("colorBuffer = gl.createRenderbuffer()"); +gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer); +gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer); +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +testCases = [ + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: 0x8054, // GL_RGB16 + border: 0, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: gl.RGBA, + border: 1, + expectedError: gl.INVALID_VALUE }, + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: gl.RGBA, + border: 0, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: gl.RGB, + border: 0, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion > 1) { + testCases = testCases.concat([ + { target: gl.TEXTURE_3D, + colorBufferFormat: gl.RGB5_A1, + internalFormat: gl.RGBA, + border: 0, + expectedError: gl.INVALID_ENUM } + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testCopyTexImage2D(testCases[ii]); +} + +debug(""); +debug("Checking CopyTexSubImage2D: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB5_A1, + internalFormat: gl.RGBA, + expectedError: gl.NO_ERROR }, + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: gl.RGBA, + expectedError: gl.INVALID_OPERATION } +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testCopyTexSubImage2D(testCases[ii]); +} + +debug(""); +debug("Checking CopyTex{Sub}Image2D: copy from WebGL internal framebuffer"); + +testCases = [ + { contextAlpha: true, + internalFormat: gl.RGBA, + subImage: false, + expectedError: gl.NO_ERROR }, + { contextAlpha: false, + internalFormat: gl.RGBA, + subImage: false, + expectedError: gl.INVALID_OPERATION }, + { contextAlpha: true, + internalFormat: gl.RGBA, + subImage: true, + expectedError: gl.NO_ERROR }, + { contextAlpha: false, + internalFormat: gl.RGBA, + subImage: true, + expectedError: gl.INVALID_OPERATION } +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testCopyFromInternalFBO(testCases[ii]); +} + +if (contextVersion > 1) { +// Create new texture for testing api of WebGL 2.0. +shouldBeNonNull("tex = gl.createTexture()"); +gl.bindTexture(gl.TEXTURE_3D, tex); +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +debug(""); +debug("Checking TexImage3D: a set of inputs that are valid in GL but invalid in WebGL"); + +var testCases = [ + { target: 0x8070, // GL_PROXY_TEXTURE_3D + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGB, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.BYTE, + expectedError: gl.INVALID_OPERATION}, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.NO_ERROR } +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexImage3D(testCases[ii]); +} + +debug(""); +debug("Checking TexImage3D: bad target, internalformats, formats, types"); + +var testCases = [ + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RG, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: [gl.INVALID_VALUE, gl.INVALID_OPERATION]}, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RG8, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.INT, + expectedError: gl.INVALID_OPERATION}, +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexImage3D(testCases[ii]); +} + +debug(""); +debug("Checking TexSubImage3D: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: gl.TEXTURE_3D, + format: 0x80E0, // GL_BGR + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + format: gl.RGBA, + type: 0x8032, // GL_UNSIGNED_BYTE_3_3_2 + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.NO_ERROR } +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexSubImage3D(testCases[ii]); +} + +} + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/texture-corner-case-videos.js b/dom/canvas/test/webgl-conf/checkout/js/tests/texture-corner-case-videos.js new file mode 100644 index 0000000000..a80da8023d --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/texture-corner-case-videos.js @@ -0,0 +1,299 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This block needs to be outside the onload handler in order for this +// test to run reliably in WebKit's test harness (at least the +// Chromium port). https://bugs.webkit.org/show_bug.cgi?id=87448 +initTestingHarness(); + +var old = debug; +var debug = function(msg) { + bufferedLogToConsole(msg); + old(msg); +}; + +function generateTest(desc, + internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion, + videos) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var c2d = null; + var successfullyParsed = false; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var currentTolerance = 0; + + function init() + { + description(desc + ' (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + // Subsume 2D canvas tests into this test case since they usually go down similar code paths and + // these tests are usually already set up to run with hardware accelerated video decoding. + c2d = document.getElementById("c2d").getContext("2d"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runAllTests(); + } + + function runOneIteration(videoElement, useTexSubImage2D, flipY, topColor, bottomColor, sourceSubRectangle, program, bindingTarget) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; + } + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Handle the source sub-rectangle if specified (WebGL 2.0 only) + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + } + // Upload the videoElement into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (sourceSubRectangle) { + // Initialize the texture to black first + if (useTexSubImage2D) { + // Skip sub-rectangle tests for cube map textures for the moment. + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + continue; + } + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], videoElement); + } + } else { + // Initialize the texture to black first + if (useTexSubImage2D) { + var width = videoElement.videoWidth; + var height = videoElement.videoHeight; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + width = Math.max(width, height); + height = width; + } + gl.texImage2D(targets[tt], 0, gl[internalFormat], + width, height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], videoElement); + } + } + } + + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + } + + var c = document.createElement("canvas"); + c.width = 16; + c.height = 16; + c.style.border = "1px solid black"; + var ctx = c.getContext("2d"); + ctx.drawImage(videoElement, 0, 0, 16, 16); + document.body.appendChild(c); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + var tolerance = currentTolerance; + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor, tolerance); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor, tolerance); + + // Expose bug http://crbug.com/733172. + if (sourceSubRectangle) { + // Skip sub-rectangle tests for cube map textures for the moment. + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + continue; + } + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], videoElement); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + } + } + + function runCanvas2DTest(videoElement, topColor, bottomColor) + { + debug('Testing with 2D canvas'); + + var canvas = c2d.canvas; + + // Draw the video to the 2D canvas context. + c2d.drawImage(videoElement, 0, 0, canvas.width, canvas.height); + + // Check a few pixels near the top and bottom and make sure they have + // the right color. + // Origin is upper left in 2D canvas context. + var tolerance = currentTolerance; + debug("Checking lower left corner"); + wtu.checkCanvasRect(c2d, 4, canvas.height - 8, 2, 2, bottomColor, + "shouldBe " + bottomColor, tolerance); + debug("Checking upper left corner"); + wtu.checkCanvasRect(c2d, 4, 4, 2, 2, topColor, + "shouldBe " + topColor, tolerance); + } + + function runAllTests() + { + var cases = [ + { sub: false, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: redColor }, + { sub: true, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_2D) { + program = tiu.setupTexturedQuad(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + resolve("SUCCESS"); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.comment); + debug("video type: " + info.type); + // Default to tolerance of 5. + currentTolerance = info.tolerance || 5; + debug("tolerance: " + currentTolerance); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported; skipping test"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, runTest); + } + function runTest() { + for (var i in cases) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // Cube map texture must be square but video is not square. + if (!cases[i].sub) { + break; + } + // Skip sub-rectangle tests for cube map textures for the moment. + if (cases[i].sourceSubRectangle) { + break; + } + } + runOneIteration(video, cases[i].sub, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + cases[i].sourceSubRectangle, + program, bindingTarget); + } + runCanvas2DTest(video, redColor, greenColor); + runNextVideo(); + } + runNextVideo(); + }); + } + + runTexImageTest(gl.TEXTURE_2D).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-test-cases.js b/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-test-cases.js new file mode 100644 index 0000000000..2e1b79a677 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-test-cases.js @@ -0,0 +1,73 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// The "name" attribute is a concession to browsers which don't +// implement the "name" property on function objects. +var testCases = + [ {name: "Float32Array", + unsigned: false, + integral: false, + elementSizeInBytes: 4, + testValues: [ -500.5, 500.5 ], + expectedValues: [ -500.5, 500.5 ] + }, + {name: "Float64Array", + unsigned: false, + integral: false, + elementSizeInBytes: 8, + testValues: [ -500.5, 500.5 ], + expectedValues: [ -500.5, 500.5 ] + }, + {name: "Int8Array", + unsigned: false, + integral: true, + elementSizeInBytes: 1, + testValues: [ -128, 127, -129, 128 ], + expectedValues: [ -128, 127, 127, -128 ] + }, + {name: "Int16Array", + unsigned: false, + integral: true, + elementSizeInBytes: 2, + testValues: [ -32768, 32767, -32769, 32768 ], + expectedValues: [ -32768, 32767, 32767, -32768 ] + }, + {name: "Int32Array", + unsigned: false, + integral: true, + elementSizeInBytes: 4, + testValues: [ -2147483648, 2147483647, -2147483649, 2147483648 ], + expectedValues: [ -2147483648, 2147483647, 2147483647, -2147483648 ] + }, + {name: "Uint8Array", + unsigned: true, + integral: true, + elementSizeInBytes: 1, + testValues: [ 0, 255, -1, 256 ], + expectedValues: [ 0, 255, 255, 0 ] + }, + {name: "Uint8ClampedArray", + unsigned: true, + integral: true, + elementSizeInBytes: 1, + testValues: [ 0, 255, -1, 256 ], + expectedValues: [ 0, 255, 0, 255 ] + }, + {name: "Uint16Array", + unsigned: true, + integral: true, + elementSizeInBytes: 2, + testValues: [ 0, 65535, -1, 65536 ], + expectedValues: [ 0, 65535, 65535, 0 ] + }, + {name: "Uint32Array", + unsigned: true, + integral: true, + elementSizeInBytes: 4, + testValues: [ 0, 4294967295, -1, 4294967296 ], + expectedValues: [ 0, 4294967295, 4294967295, 0 ] + } + ]; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-worker.js b/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-worker.js new file mode 100644 index 0000000000..c361a0e8a2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-worker.js @@ -0,0 +1,72 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function constructTypedArray(type, data) { + if (type == 'Int8Array') { + return new Int8Array(data); + } else if (type == 'Uint8Array') { + return new Uint8Array(data); + } else if (type == 'Uint8ClampedArray') { + return new Uint8ClampedArray(data); + } else if (type == 'Int16Array') { + return new Int16Array(data); + } else if (type == 'Uint16Array') { + return new Uint16Array(data); + } else if (type == 'Int32Array') { + return new Int32Array(data); + } else if (type == 'Uint32Array') { + return new Uint32Array(data); + } else if (type == 'Float32Array') { + return new Float32Array(data); + } else if (type == 'Float64Array') { + return new Float64Array(data); + } +} + +function constructDataView(subType, elementSizeInBytes, data) { + var setter = "set" + subType; + var byteOffset = 0; + var buffer = new ArrayBuffer(elementSizeInBytes * data.length); + var dataView = new DataView(buffer); + for (var ii = 0; ii < data.length; ++ii) { + dataView[setter](byteOffset, data[ii]); + byteOffset += elementSizeInBytes; + } + return dataView; +} + +onmessage = function(event) { + var message = event.data; + if (message.command == 'copy' || + message.command == 'transfer' || + message.command == 'copyBuffer' || + message.command == 'transferBuffer') { + var view; + if (message.type != 'DataView') { + view = constructTypedArray(message.type, message.data); + } else { + view = constructDataView(message.subType, message.elementSizeInBytes, message.data); + } + var valueToSend; + if (message.command == 'copy' || + message.command == 'transfer') { + valueToSend = view; + } else { + valueToSend = view.buffer; + } + var transferablesToSend = undefined; + if (message.command == 'transfer' || + message.command == 'transferBuffer') { + transferablesToSend = [ view.buffer ]; + } + postMessage(valueToSend, transferablesToSend); + } else if (message.command == 'pong') { + postMessage(message.data, message.transferables); + } else if (message.command == 'ignore') { + } else { + postMessage('error: unknown message'); + } +}; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-compressed-texture-size-limit.js b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-compressed-texture-size-limit.js new file mode 100644 index 0000000000..6fad5520f2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-compressed-texture-size-limit.js @@ -0,0 +1,226 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; + +var runCompressedTextureSizeLimitTest = function(maxArrayBufferSizeBytes, positiveCubeMapMaxSize) { + + function numLevelsFromSize(size) { + var levels = 0; + while ((size >> levels) > 0) { + ++levels; + } + return levels; + } + + // More formats can be added here when more texture compression extensions are enabled in WebGL. + var validFormats = { + COMPRESSED_RGB_S3TC_DXT1_EXT : 0x83F0, + COMPRESSED_RGBA_S3TC_DXT1_EXT : 0x83F1, + COMPRESSED_RGBA_S3TC_DXT3_EXT : 0x83F2, + COMPRESSED_RGBA_S3TC_DXT5_EXT : 0x83F3, + }; + + // format specific restrictions for COMPRESSED_RGB_S3TC_DXT1_EXT and COMPRESSED_RGBA_S3TC_DXT1_EXT + // on the byteLength of the ArrayBufferView, pixels + function func1 (width, height) + { + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8; + } + + // format specific restrictions for COMPRESSED_RGBA_S3TC_DXT3_EXT and COMPRESSED_RGBA_S3TC_DXT5_EXT + // on the byteLength of the ArrayBufferView, pixels + function func2 (width, height) + { + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16; + } + + var wtu = WebGLTestUtils; + var gl = wtu.create3DContext("example"); + var tests = [ + // More tests can be added here when more texture compression extensions are enabled in WebGL. + // Level 0 image width and height must be a multiple of the sizeStep. + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGB_S3TC_DXT1_EXT, dataType: Uint8Array, func: func1, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT1_EXT, dataType: Uint8Array, func: func1, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT3_EXT, dataType: Uint8Array, func: func2, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT5_EXT, dataType: Uint8Array, func: func2, sizeStep: 4}, + ]; + + // Note: We expressly only use 2 textures because first a texture will be defined + // using all mip levels of 1 format, then for a moment it will have mixed formats which + // may uncover bugs. + var targets = [ + { target: gl.TEXTURE_2D, + maxSize: gl.getParameter(gl.MAX_TEXTURE_SIZE), + tex: gl.createTexture(), + targets: [gl.TEXTURE_2D] + }, + { target: gl.TEXTURE_CUBE_MAP, + maxSize: gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE), + tex: gl.createTexture(), + targets: [ + gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z + ] + } + ]; + + function getSharedArrayBufferSize() { + var sharedArrayBufferSize = 0; + for (var tt = 0; tt < tests.length; ++tt) { + var test = tests[tt]; + for (var trg = 0; trg < targets.length; ++trg) { + var t = targets[trg]; + var bufferSizeNeeded; + if (t.target === gl.TEXTURE_CUBE_MAP) { + var positiveTestSize = Math.min(2048, t.maxSize); + bufferSizeNeeded = test.func(positiveTestSize, positiveTestSize); + } else { + bufferSizeNeeded = test.func(t.maxSize, test.sizeStep); + } + if (bufferSizeNeeded > sharedArrayBufferSize) { + sharedArrayBufferSize = bufferSizeNeeded; + } + bufferSizeNeeded = test.func(t.maxSize + test.sizeStep, t.maxSize + test.sizeStep); + // ArrayBuffers can be at most 4GB (minus 1 byte). + if (bufferSizeNeeded > sharedArrayBufferSize && bufferSizeNeeded <= maxArrayBufferSizeBytes) { + sharedArrayBufferSize = bufferSizeNeeded; + } + } + } + return sharedArrayBufferSize; + } + + // Share an ArrayBuffer among tests to avoid too many large allocations + var sharedArrayBuffer = new ArrayBuffer(getSharedArrayBufferSize()); + + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); + + var trg = 0; + var tt = 0; + runNextTest(); + + function runNextTest() { + var t = targets[trg]; + + if (tt == 0) { + var tex = t.tex; + gl.bindTexture(t.target, tex); + + debug(""); + debug("max size for " + wtu.glEnumToString(gl, t.target) + ": " + t.maxSize); + } + + var test = tests[tt]; + testFormatType(t, test); + ++tt; + if (tt == tests.length) { + tt = 0; + ++trg; + if (trg == targets.length) { + finishTest(); + return; + } + } + wtu.dispatchPromise(runNextTest); + } + + function testFormatType(t, test) { + var positiveTestSize = t.maxSize; + var positiveTestOtherDimension = test.sizeStep; + if (t.target === gl.TEXTURE_CUBE_MAP) { + // Can't always test the maximum size since that can cause OOM: + positiveTestSize = Math.min(positiveCubeMapMaxSize, t.maxSize); + // Cube map textures need to be square: + positiveTestOtherDimension = positiveTestSize; + } + var positiveTestLevels = numLevelsFromSize(positiveTestSize); + var numLevels = numLevelsFromSize(t.maxSize); + debug(""); + debug("num levels: " + numLevels + ", levels used in positive test: " + positiveTestLevels); + + debug(""); + + // Query the extension and store globally so shouldBe can access it + var ext = wtu.getExtensionWithKnownPrefixes(gl, test.extension); + if (ext) { + + testPassed("Successfully enabled " + test.extension + " extension"); + + for (var j = 0; j < t.targets.length; ++j) { + var target = t.targets[j]; + debug(""); + debug(wtu.glEnumToString(gl, target) + " " + wtu.glEnumToString(ext, test.format)); + + // positive test + var size = positiveTestSize; + var otherDimension = positiveTestOtherDimension; + for (var i = 0; i < positiveTestLevels; i++) { + var pixels = new test.dataType(sharedArrayBuffer, 0, test.func(size, otherDimension)); + gl.compressedTexImage2D(target, i, test.format, size, otherDimension, 0, pixels); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture should generate NO_ERROR." + + "level is " + i + ", size is " + size + "x" + otherDimension); + size /= 2; + otherDimension /= 2; + if (otherDimension < 1) { + otherDimension = 1; + } + } + + var numLevels = numLevelsFromSize(t.maxSize); + + // out of bounds tests + + // width or height out of bounds + if (t.target != gl.TEXTURE_CUBE_MAP) { + // only width out of bounds + var wideAndShortDataSize = test.func(t.maxSize + test.sizeStep, test.sizeStep); + var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, wideAndShortDataSize); + gl.compressedTexImage2D(target, 0, test.format, t.maxSize + test.sizeStep, test.sizeStep, 0, pixelsNegativeTest1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width out of bounds: should generate INVALID_VALUE." + + " level is 0, size is " + (t.maxSize + test.sizeStep) + "x" + (test.sizeStep)); + + // only height out of bounds + var narrowAndTallDataSize = test.func(test.sizeStep, t.maxSize + test.sizeStep); + var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, narrowAndTallDataSize); + gl.compressedTexImage2D(target, 0, test.format, test.sizeStep, t.maxSize + test.sizeStep, 0, pixelsNegativeTest1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "height out of bounds: should generate INVALID_VALUE." + + " level is 0, size is " + (test.sizeStep) + "x" + (t.maxSize + test.sizeStep)); + } + + // both width and height out of the maximum bounds simultaneously + var squareDataSize = test.func(t.maxSize + test.sizeStep, t.maxSize + test.sizeStep); + // this check assumes that each element is 1 byte + if (squareDataSize > sharedArrayBuffer.byteLength) { + testPassed("Unable to test square texture larger than maximum size due to ArrayBuffer size limitations -- this is legal"); + } else { + var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, squareDataSize); + gl.compressedTexImage2D(target, 0, test.format, t.maxSize + test.sizeStep, t.maxSize + test.sizeStep, 0, pixelsNegativeTest1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width and height out of bounds: should generate INVALID_VALUE." + + " level is 0, size is " + (t.maxSize + test.sizeStep) + "x" + (t.maxSize + test.sizeStep)); + } + + // level out of bounds + var pixelsNegativeTest2 = new test.dataType(sharedArrayBuffer, 0, test.func(256, 256)); + gl.compressedTexImage2D(target, numLevels, test.format, 256, 256, 0, pixelsNegativeTest2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "level out of bounds: should generate INVALID_VALUE." + + " level is " + numLevels + ", size is 256x256"); + //width and height out of bounds for specified level + gl.compressedTexImage2D(target, numLevels - 1, test.format, 256, 256, 0, pixelsNegativeTest2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width or height out of bounds for specified level: should generate INVALID_VALUE." + + " level is " + (numLevels - 1) + ", size is 256x256"); + } + } + else { + testPassed("No " + test.extension + " extension support -- this is legal"); + } + } + +}; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-draw-buffers-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-draw-buffers-utils.js new file mode 100644 index 0000000000..ebd0c7ba68 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-draw-buffers-utils.js @@ -0,0 +1,69 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This file contains utilities shared between tests for the WEBGL_draw_buffers extension and multiple draw buffers functionality in WebGL 2.0. + +'use strict'; + +var WebGLDrawBuffersUtils = function(gl, ext) { + + var getMaxUsableColorAttachments = function() { + var maxDrawingBuffers; + var maxColorAttachments; + if (ext) { + // EXT_draw_buffers + maxDrawingBuffers = gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL); + maxColorAttachments = gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL); + } else { + // WebGL 2.0 + maxDrawingBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS); + maxColorAttachments = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS); + } + var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); + return Math.min(maxDrawingBuffers, maxColorAttachments, maxUniformVectors); + }; + + var makeColorAttachmentArray = function(size) { + var array = [] + for (var ii = 0; ii < size; ++ii) { + array.push(gl.COLOR_ATTACHMENT0 + ii); + } + return array; + } + + var checkProgram = wtu.setupTexturedQuad(gl); + + var checkAttachmentsForColorFn = function(attachments, colorFn) { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.useProgram(checkProgram); + attachments.forEach(function(attachment, index) { + gl.bindTexture(gl.TEXTURE_2D, attachment.texture); + wtu.clearAndDrawUnitQuad(gl); + var expectedColor = colorFn(attachment, index); + var tolerance = 0; + expectedColor.forEach(function(v) { + if (v != 0 && v != 255) { + tolerance = 8; + } + }); + wtu.checkCanvas(gl, expectedColor, "attachment " + index + " should be " + expectedColor.toString(), tolerance); + }); + debug(""); + }; + + var checkAttachmentsForColor = function(attachments, color) { + checkAttachmentsForColorFn(attachments, function(attachment, index) { + return color || attachment.color; + }); + }; + + return { + getMaxUsableColorAttachments: getMaxUsableColorAttachments, + makeColorAttachmentArray: makeColorAttachmentArray, + checkAttachmentsForColorFn: checkAttachmentsForColorFn, + checkAttachmentsForColor: checkAttachmentsForColor + }; +}; diff --git a/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js b/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js new file mode 100644 index 0000000000..f48d9d2ad7 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js @@ -0,0 +1,642 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This is a test harness for running javascript tests in the browser. +// The only identifier exposed by this harness is WebGLTestHarnessModule. +// +// To use it make an HTML page with an iframe. Then call the harness like this +// +// function reportResults(type, msg, success) { +// ... +// return true; +// } +// +// var fileListURL = '00_test_list.txt'; +// var testHarness = new WebGLTestHarnessModule.TestHarness( +// iframe, +// fileListURL, +// reportResults, +// options); +// +// The harness will load the fileListURL and parse it for the URLs, one URL +// per line preceded by options, see below. URLs should be on the same domain +// and at the same folder level or below the main html file. If any URL ends +// in .txt it will be parsed as well so you can nest .txt files. URLs inside a +// .txt file should be relative to that text file. +// +// During startup, for each page found the reportFunction will be called with +// WebGLTestHarnessModule.TestHarness.reportType.ADD_PAGE and msg will be +// the URL of the test. +// +// Each test is required to call testHarness.reportResults. This is most easily +// accomplished by storing that value on the main window with +// +// window.webglTestHarness = testHarness +// +// and then adding these to functions to your tests. +// +// function reportTestResultsToHarness(success, msg) { +// if (window.parent.webglTestHarness) { +// window.parent.webglTestHarness.reportResults(success, msg); +// } +// } +// +// function notifyFinishedToHarness() { +// if (window.parent.webglTestHarness) { +// window.parent.webglTestHarness.notifyFinished(); +// } +// } +// +// This way your tests will still run without the harness and you can use +// any testing framework you want. +// +// Each test should call reportTestResultsToHarness with true for success if it +// succeeded and false if it fail followed and any message it wants to +// associate with the test. If your testing framework supports checking for +// timeout you can call it with success equal to undefined in that case. +// +// To run the tests, call testHarness.runTests(options); +// +// For each test run, before the page is loaded the reportFunction will be +// called with WebGLTestHarnessModule.TestHarness.reportType.START_PAGE and msg +// will be the URL of the test. You may return false if you want the test to be +// skipped. +// +// For each test completed the reportFunction will be called with +// with WebGLTestHarnessModule.TestHarness.reportType.TEST_RESULT, +// success = true on success, false on failure, undefined on timeout +// and msg is any message the test choose to pass on. +// +// When all the tests on the page have finished your page must call +// notifyFinishedToHarness. If notifyFinishedToHarness is not called +// the harness will assume the test timed out. +// +// When all the tests on a page have finished OR the page as timed out the +// reportFunction will be called with +// WebGLTestHarnessModule.TestHarness.reportType.FINISH_PAGE +// where success = true if the page has completed or undefined if the page timed +// out. +// +// Finally, when all the tests have completed the reportFunction will be called +// with WebGLTestHarnessModule.TestHarness.reportType.FINISHED_ALL_TESTS. +// +// Harness Options +// +// These are passed in to the TestHarness as a JavaScript object +// +// version: (required!) +// +// Specifies a version used to filter tests. Tests marked as requiring +// a version greater than this version will not be included. +// +// example: new TestHarness(...., {version: "3.1.2"}); +// +// minVersion: +// +// Specifies the minimum version a test must require to be included. +// This basically flips the filter so that only tests marked with +// --min-version will be included if they are at this minVersion or +// greater. +// +// example: new TestHarness(...., {minVersion: "2.3.1"}); +// +// maxVersion: +// +// Specifies the maximum version a test must require to be included. +// This basically flips the filter so that only tests marked with +// --max-version will be included if they are at this maxVersion or +// less. +// +// example: new TestHarness(...., {maxVersion: "2.3.1"}); +// +// fast: +// +// Specifies to skip any tests marked as slow. +// +// example: new TestHarness(..., {fast: true}); +// +// Test Options: +// +// Any test URL or .txt file can be prefixed by the following options +// +// min-version: +// +// Sets the minimum version required to include this test. A version is +// passed into the harness options. Any test marked as requiring a +// min-version greater than the version passed to the harness is skipped. +// This allows you to add new tests to a suite of tests for a future +// version of the suite without including the test in the current version. +// If no -min-version is specified it is inheriited from the .txt file +// including it. The default is 1.0.0 +// +// example: --min-version 2.1.3 sometest.html +// +// max-version: +// +// Sets the maximum version required to include this test. A version is +// passed into the harness options. Any test marked as requiring a +// max-version less than the version passed to the harness is skipped. +// This allows you to test functionality that has been removed from later +// versions of the suite. +// If no -max-version is specified it is inherited from the .txt file +// including it. +// +// example: --max-version 1.9.9 sometest.html +// +// slow: +// +// Marks a test as slow. Slow tests can be skipped by passing fastOnly: true +// to the TestHarness. Of course you need to pass all tests but sometimes +// you'd like to test quickly and run only the fast subset of tests. +// +// example: --slow some-test-that-takes-2-mins.html +// + +WebGLTestHarnessModule = function() { + +/** + * Wrapped logging function. + */ +var log = function(msg) { + if (window.console && window.console.log) { + window.console.log(msg); + } +}; + +/** + * Loads text from an external file. This function is synchronous. + * @param {string} url The url of the external file. + * @param {!function(bool, string): void} callback that is sent a bool for + * success and the string. + */ +var loadTextFileAsynchronous = function(url, callback) { + log ("loading: " + url); + var error = 'loadTextFileSynchronous failed to load url "' + url + '"'; + var request; + if (window.XMLHttpRequest) { + request = new XMLHttpRequest(); + if (request.overrideMimeType) { + request.overrideMimeType('text/plain'); + } + } else { + throw 'XMLHttpRequest is disabled'; + } + try { + request.open('GET', url, true); + request.onreadystatechange = function() { + if (request.readyState == 4) { + var text = ''; + // HTTP reports success with a 200 status. The file protocol reports + // success with zero. HTTP does not use zero as a status code (they + // start at 100). + // https://developer.mozilla.org/En/Using_XMLHttpRequest + var success = request.status == 200 || request.status == 0; + if (success) { + text = request.responseText; + } + log("loaded: " + url); + callback(success, text); + } + }; + request.send(null); + } catch (e) { + log("failed to load: " + url); + callback(false, ''); + } +}; + +/** + * @param {string} versionString WebGL version string. + * @return {number} Integer containing the WebGL major version. + */ +var getMajorVersion = function(versionString) { + if (!versionString) { + return 1; + } + return parseInt(versionString.split(" ")[0].split(".")[0], 10); +}; + +/** + * @param {string} url Base URL of the test. + * @param {map} options Map of options to append to the URL's query string. + * @return {string} URL that will run the test with the given WebGL version. + */ +var getURLWithOptions = function(url, options) { + var queryArgs = 0; + + for (i in options) { + url += queryArgs ? "&" : "?"; + url += i + "=" + options[i]; + queryArgs++; + } + + return url; +}; + +/** + * Compare version strings. + */ +var greaterThanOrEqualToVersion = function(have, want) { + have = have.split(" ")[0].split("."); + want = want.split(" ")[0].split("."); + + //have 1.2.3 want 1.1 + //have 1.1.1 want 1.1 + //have 1.0.9 want 1.1 + //have 1.1 want 1.1.1 + + for (var ii = 0; ii < want.length; ++ii) { + var wantNum = parseInt(want[ii]); + var haveNum = have[ii] ? parseInt(have[ii]) : 0 + if (haveNum > wantNum) { + return true; // 2.0.0 is greater than 1.2.3 + } + if (haveNum < wantNum) { + return false; + } + } + return true; +}; + +/** + * Reads a file, recursively adding files referenced inside. + * + * Each line of URL is parsed, comments starting with '#' or ';' + * or '//' are stripped. + * + * arguments beginning with -- are extracted + * + * lines that end in .txt are recursively scanned for more files + * other lines are added to the list of files. + * + * @param {string} url The url of the file to read. + * @param {function(boolean, !Array.<string>):void} callback + * Callback that is called with true for success and an + * array of filenames. + * @param {Object} options Optional options + * + * Options: + * version: {string} The version of the conformance test. + * Tests with the argument --min-version <version> will + * be ignored version is less then <version> + * + */ +var getFileList = function(url, callback, options) { + var files = []; + + var copyObject = function(obj) { + return JSON.parse(JSON.stringify(obj)); + }; + + var toCamelCase = function(str) { + return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase() }); + }; + + var globalOptions = copyObject(options); + globalOptions.defaultVersion = "1.0"; + globalOptions.defaultMaxVersion = null; + + var getFileListImpl = function(prefix, line, lineNum, hierarchicalOptions, callback) { + var files = []; + + var args = line.split(/\s+/); + var nonOptions = []; + var useTest = true; + var testOptions = {}; + for (var jj = 0; jj < args.length; ++jj) { + var arg = args[jj]; + if (arg[0] == '-') { + if (arg[1] != '-') { + throw ("bad option at in " + url + ":" + lineNum + ": " + arg); + } + var option = arg.substring(2); + switch (option) { + // no argument options. + case 'slow': + testOptions[toCamelCase(option)] = true; + break; + // one argument options. + case 'min-version': + case 'max-version': + ++jj; + testOptions[toCamelCase(option)] = args[jj]; + break; + default: + throw ("bad unknown option '" + option + "' at in " + url + ":" + lineNum + ": " + arg); + } + } else { + nonOptions.push(arg); + } + } + var url = prefix + nonOptions.join(" "); + + if (url.substr(url.length - 4) != '.txt') { + var minVersion = testOptions.minVersion; + if (!minVersion) { + minVersion = hierarchicalOptions.defaultVersion; + } + var maxVersion = testOptions.maxVersion; + if (!maxVersion) { + maxVersion = hierarchicalOptions.defaultMaxVersion; + } + var slow = testOptions.slow; + if (!slow) { + slow = hierarchicalOptions.defaultSlow; + } + + if (globalOptions.fast && slow) { + useTest = false; + } else if (globalOptions.minVersion) { + useTest = greaterThanOrEqualToVersion(minVersion, globalOptions.minVersion); + } else if (globalOptions.maxVersion && maxVersion) { + useTest = greaterThanOrEqualToVersion(globalOptions.maxVersion, maxVersion); + } else { + useTest = greaterThanOrEqualToVersion(globalOptions.version, minVersion); + if (maxVersion) { + useTest = useTest && greaterThanOrEqualToVersion(maxVersion, globalOptions.version); + } + } + } + + if (!useTest) { + callback(true, []); + return; + } + + if (url.substr(url.length - 4) == '.txt') { + // If a version was explicity specified pass it down. + if (testOptions.minVersion) { + hierarchicalOptions.defaultVersion = testOptions.minVersion; + } + if (testOptions.maxVersion) { + hierarchicalOptions.defaultMaxVersion = testOptions.maxVersion; + } + if (testOptions.slow) { + hierarchicalOptions.defaultSlow = testOptions.slow; + } + loadTextFileAsynchronous(url, function() { + return function(success, text) { + if (!success) { + callback(false, ''); + return; + } + var lines = text.split('\n'); + var prefix = ''; + var lastSlash = url.lastIndexOf('/'); + if (lastSlash >= 0) { + prefix = url.substr(0, lastSlash + 1); + } + var fail = false; + var count = 1; + var index = 0; + for (var ii = 0; ii < lines.length; ++ii) { + var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + if (str.length > 4 && + str[0] != '#' && + str[0] != ";" && + str.substr(0, 2) != "//") { + ++count; + getFileListImpl(prefix, str, ii + 1, copyObject(hierarchicalOptions), function(index) { + return function(success, new_files) { + //log("got files: " + new_files.length); + if (success) { + files[index] = new_files; + } + finish(success); + }; + }(index++)); + } + } + finish(true); + + function finish(success) { + if (!success) { + fail = true; + } + --count; + //log("count: " + count); + if (!count) { + callback(!fail, files); + } + } + } + }()); + } else { + files.push(url); + callback(true, files); + } + }; + + getFileListImpl('', url, 1, globalOptions, function(success, files) { + // flatten + var flat = []; + flatten(files); + function flatten(files) { + for (var ii = 0; ii < files.length; ++ii) { + var value = files[ii]; + if (typeof(value) == "string") { + flat.push(value); + } else { + flatten(value); + } + } + } + callback(success, flat); + }); +}; + +var FilterURL = (function() { + var prefix = window.location.pathname; + prefix = prefix.substring(0, prefix.lastIndexOf("/") + 1); + return function(url) { + if (url.substring(0, prefix.length) == prefix) { + url = url.substring(prefix.length); + } + return url; + }; +}()); + +var TestFile = function(url) { + this.url = url; +}; + +var Test = function(file) { + this.file = file; +}; + +var TestHarness = function(iframe, filelistUrl, reportFunc, options) { + this.window = window; + this.iframes = iframe.length ? iframe : [iframe]; + this.reportFunc = reportFunc; + this.timeoutDelay = 20000; + this.files = []; + this.allowSkip = options.allowSkip; + this.webglVersion = getMajorVersion(options.version); + this.dumpShaders = options.dumpShaders; + this.quiet = options.quiet; + + var that = this; + getFileList(filelistUrl, function() { + return function(success, files) { + that.addFiles_(success, files); + }; + }(), options); + +}; + +TestHarness.reportType = { + ADD_PAGE: 1, + READY: 2, + START_PAGE: 3, + TEST_RESULT: 4, + FINISH_PAGE: 5, + FINISHED_ALL_TESTS: 6 +}; + +TestHarness.prototype.addFiles_ = function(success, files) { + if (!success) { + this.reportFunc( + TestHarness.reportType.FINISHED_ALL_TESTS, + '', + 'Unable to load tests. Are you running locally?\n' + + 'You need to run from a server or configure your\n' + + 'browser to allow access to local files (not recommended).\n\n' + + 'Note: An easy way to run from a server:\n\n' + + '\tcd path_to_tests\n' + + '\tpython -m SimpleHTTPServer\n\n' + + 'then point your browser to ' + + '<a href="http://localhost:8000/webgl-conformance-tests.html">' + + 'http://localhost:8000/webgl-conformance-tests.html</a>', + false) + return; + } + log("total files: " + files.length); + for (var ii = 0; ii < files.length; ++ii) { + log("" + ii + ": " + files[ii]); + this.files.push(new TestFile(files[ii])); + this.reportFunc(TestHarness.reportType.ADD_PAGE, '', files[ii], undefined); + } + this.reportFunc(TestHarness.reportType.READY, '', undefined, undefined); +} + +TestHarness.prototype.runTests = function(opt_options) { + var options = opt_options || { }; + options.start = options.start || 0; + options.count = options.count || this.files.length; + + this.idleIFrames = this.iframes.slice(0); + this.runningTests = {}; + var testsToRun = []; + for (var ii = 0; ii < options.count; ++ii) { + testsToRun.push(ii + options.start); + } + this.numTestsRemaining = options.count; + this.testsToRun = testsToRun; + this.startNextTest(); +}; + +TestHarness.prototype.setTimeout = function(test) { + var that = this; + test.timeoutId = this.window.setTimeout(function() { + that.timeout(test); + }, this.timeoutDelay); +}; + +TestHarness.prototype.clearTimeout = function(test) { + this.window.clearTimeout(test.timeoutId); +}; + +TestHarness.prototype.startNextTest = function() { + if (this.numTestsRemaining == 0) { + log("done"); + this.reportFunc(TestHarness.reportType.FINISHED_ALL_TESTS, + '', '', true); + } else { + while (this.testsToRun.length > 0 && this.idleIFrames.length > 0) { + var testId = this.testsToRun.shift(); + var iframe = this.idleIFrames.shift(); + this.startTest(iframe, this.files[testId], this.webglVersion); + } + } +}; + +TestHarness.prototype.startTest = function(iframe, testFile, webglVersion) { + var test = { + iframe: iframe, + testFile: testFile + }; + var url = testFile.url; + this.runningTests[url] = test; + log("loading: " + url); + if (this.reportFunc(TestHarness.reportType.START_PAGE, url, url, undefined)) { + iframe.src = getURLWithOptions(url, { + "webglVersion": webglVersion, + "dumpShaders": this.dumpShaders, + "quiet": this.quiet + }); + this.setTimeout(test); + } else { + this.reportResults(url, !!this.allowSkip, "skipped", true); + this.notifyFinished(url); + } +}; + +TestHarness.prototype.getTest = function(url) { + var test = this.runningTests[FilterURL(url)]; + if (!test) { + throw("unknown test:" + url); + } + return test; +}; + +TestHarness.prototype.reportResults = function(url, success, msg, skipped) { + url = FilterURL(url); + var test = this.getTest(url); + this.clearTimeout(test); + log((success ? "PASS" : "FAIL") + ": " + msg); + this.reportFunc(TestHarness.reportType.TEST_RESULT, url, msg, success, skipped); + // For each result we get, reset the timeout + this.setTimeout(test); +}; + +TestHarness.prototype.dequeTest = function(test) { + this.clearTimeout(test); + this.idleIFrames.push(test.iframe); + delete this.runningTests[test.testFile.url]; + --this.numTestsRemaining; +} + +TestHarness.prototype.notifyFinished = function(url) { + url = FilterURL(url); + var test = this.getTest(url); + log(url + ": finished"); + this.dequeTest(test); + this.reportFunc(TestHarness.reportType.FINISH_PAGE, url, url, true); + this.startNextTest(); +}; + +TestHarness.prototype.timeout = function(test) { + this.dequeTest(test); + var url = test.testFile.url; + log(url + ": timeout"); + this.reportFunc(TestHarness.reportType.FINISH_PAGE, url, url, undefined); + this.startNextTest(); +}; + +TestHarness.prototype.setTimeoutDelay = function(x) { + this.timeoutDelay = x; +}; + +return { + 'TestHarness': TestHarness, + 'getMajorVersion': getMajorVersion, + 'getURLWithOptions': getURLWithOptions + }; + +}(); + + + diff --git a/dom/canvas/test/webgl-conf/checkout/js/webgl-test-utils.js b/dom/canvas/test/webgl-conf/checkout/js/webgl-test-utils.js new file mode 100644 index 0000000000..f17f69fa30 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/webgl-test-utils.js @@ -0,0 +1,3677 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +var WebGLTestUtils = (function() { +"use strict"; + +/** + * Wrapped logging function. + * @param {string} msg The message to log. + */ +var log = function(msg) { + bufferedLogToConsole(msg); +}; + +/** + * Wrapped logging function. + * @param {string} msg The message to log. + */ +var error = function(msg) { + // For the time being, diverting this to window.console.log rather + // than window.console.error. If anyone cares enough they can + // generalize the mechanism in js-test-pre.js. + log(msg); +}; + +/** + * Turn off all logging. + */ +var loggingOff = function() { + log = function() {}; + error = function() {}; +}; + +const ENUM_NAME_REGEX = RegExp('[A-Z][A-Z0-9_]*'); +const ENUM_NAME_BY_VALUE = {}; +const ENUM_NAME_PROTOTYPES = new Map(); + +/** + * Converts a WebGL enum to a string. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} value The enum value. + * @return {string} The enum as a string. + */ +var glEnumToString = function(glOrExt, value) { + if (value === undefined) + throw new Error('glEnumToString: `value` must not be undefined'); + + const proto = glOrExt.__proto__; + if (!ENUM_NAME_PROTOTYPES.has(proto)) { + ENUM_NAME_PROTOTYPES.set(proto, true); + + for (const k in proto) { + if (!ENUM_NAME_REGEX.test(k)) continue; + + const v = glOrExt[k]; + if (ENUM_NAME_BY_VALUE[v] === undefined) { + ENUM_NAME_BY_VALUE[v] = k; + } else { + ENUM_NAME_BY_VALUE[v] += '/' + k; + } + } + } + + const key = ENUM_NAME_BY_VALUE[value]; + if (key !== undefined) return key; + + return "0x" + Number(value).toString(16); +}; + +var lastError = ""; + +/** + * Returns the last compiler/linker error. + * @return {string} The last compiler/linker error. + */ +var getLastError = function() { + return lastError; +}; + +/** + * Whether a haystack ends with a needle. + * @param {string} haystack String to search + * @param {string} needle String to search for. + * @param {boolean} True if haystack ends with needle. + */ +var endsWith = function(haystack, needle) { + return haystack.substr(haystack.length - needle.length) === needle; +}; + +/** + * Whether a haystack starts with a needle. + * @param {string} haystack String to search + * @param {string} needle String to search for. + * @param {boolean} True if haystack starts with needle. + */ +var startsWith = function(haystack, needle) { + return haystack.substr(0, needle.length) === needle; +}; + +/** + * A vertex shader for a single texture. + * @type {string} + */ +var simpleTextureVertexShader = [ + 'attribute vec4 vPosition;', + 'attribute vec2 texCoord0;', + 'varying vec2 texCoord;', + 'void main() {', + ' gl_Position = vPosition;', + ' texCoord = texCoord0;', + '}'].join('\n'); + +/** + * A vertex shader for a single texture. + * @type {string} + */ +var simpleTextureVertexShaderESSL300 = [ + '#version 300 es', + 'layout(location=0) in vec4 vPosition;', + 'layout(location=1) in vec2 texCoord0;', + 'out vec2 texCoord;', + 'void main() {', + ' gl_Position = vPosition;', + ' texCoord = texCoord0;', + '}'].join('\n'); + +/** + * A fragment shader for a single texture. + * @type {string} + */ +var simpleTextureFragmentShader = [ + 'precision mediump float;', + 'uniform sampler2D tex;', + 'varying vec2 texCoord;', + 'void main() {', + ' gl_FragData[0] = texture2D(tex, texCoord);', + '}'].join('\n'); + +/** + * A fragment shader for a single texture. + * @type {string} + */ +var simpleTextureFragmentShaderESSL300 = [ + '#version 300 es', + 'precision highp float;', + 'uniform highp sampler2D tex;', + 'in vec2 texCoord;', + 'out vec4 out_color;', + 'void main() {', + ' out_color = texture(tex, texCoord);', + '}'].join('\n'); + +/** + * A fragment shader for a single texture with high precision. + * @type {string} + */ +var simpleHighPrecisionTextureFragmentShader = [ + 'precision highp float;', + 'uniform highp sampler2D tex;', + 'varying vec2 texCoord;', + 'void main() {', + ' gl_FragData[0] = texture2D(tex, texCoord);', + '}'].join('\n'); + +/** + * A fragment shader for a single cube map texture. + * @type {string} + */ +var simpleCubeMapTextureFragmentShader = [ + 'precision mediump float;', + 'uniform samplerCube tex;', + 'uniform highp int face;', + 'varying vec2 texCoord;', + 'void main() {', + // Transform [0, 1] -> [-1, 1] + ' vec2 texC2 = (texCoord * 2.) - 1.;', + // Transform 2d tex coord. to each face of TEXTURE_CUBE_MAP coord. + ' vec3 texCube = vec3(0., 0., 0.);', + ' if (face == 34069) {', // TEXTURE_CUBE_MAP_POSITIVE_X + ' texCube = vec3(1., -texC2.y, -texC2.x);', + ' } else if (face == 34070) {', // TEXTURE_CUBE_MAP_NEGATIVE_X + ' texCube = vec3(-1., -texC2.y, texC2.x);', + ' } else if (face == 34071) {', // TEXTURE_CUBE_MAP_POSITIVE_Y + ' texCube = vec3(texC2.x, 1., texC2.y);', + ' } else if (face == 34072) {', // TEXTURE_CUBE_MAP_NEGATIVE_Y + ' texCube = vec3(texC2.x, -1., -texC2.y);', + ' } else if (face == 34073) {', // TEXTURE_CUBE_MAP_POSITIVE_Z + ' texCube = vec3(texC2.x, -texC2.y, 1.);', + ' } else if (face == 34074) {', // TEXTURE_CUBE_MAP_NEGATIVE_Z + ' texCube = vec3(-texC2.x, -texC2.y, -1.);', + ' }', + ' gl_FragData[0] = textureCube(tex, texCube);', + '}'].join('\n'); + +/** + * A vertex shader for a single texture. + * @type {string} + */ +var noTexCoordTextureVertexShader = [ + 'attribute vec4 vPosition;', + 'varying vec2 texCoord;', + 'void main() {', + ' gl_Position = vPosition;', + ' texCoord = vPosition.xy * 0.5 + 0.5;', + '}'].join('\n'); + +/** + * A vertex shader for a uniform color. + * @type {string} + */ +var simpleVertexShader = [ + 'attribute vec4 vPosition;', + 'void main() {', + ' gl_Position = vPosition;', + '}'].join('\n'); + +/** + * A vertex shader for a uniform color. + * @type {string} + */ +var simpleVertexShaderESSL300 = [ + '#version 300 es', + 'in vec4 vPosition;', + 'void main() {', + ' gl_Position = vPosition;', + '}'].join('\n'); + +/** + * A fragment shader for a uniform color. + * @type {string} + */ +var simpleColorFragmentShader = [ + 'precision mediump float;', + 'uniform vec4 u_color;', + 'void main() {', + ' gl_FragData[0] = u_color;', + '}'].join('\n'); + +/** + * A fragment shader for a uniform color. + * @type {string} + */ +var simpleColorFragmentShaderESSL300 = [ + '#version 300 es', + 'precision mediump float;', + 'out vec4 out_color;', + 'uniform vec4 u_color;', + 'void main() {', + ' out_color = u_color;', + '}'].join('\n'); + +/** + * A vertex shader for vertex colors. + * @type {string} + */ +var simpleVertexColorVertexShader = [ + 'attribute vec4 vPosition;', + 'attribute vec4 a_color;', + 'varying vec4 v_color;', + 'void main() {', + ' gl_Position = vPosition;', + ' v_color = a_color;', + '}'].join('\n'); + +/** + * A fragment shader for vertex colors. + * @type {string} + */ +var simpleVertexColorFragmentShader = [ + 'precision mediump float;', + 'varying vec4 v_color;', + 'void main() {', + ' gl_FragData[0] = v_color;', + '}'].join('\n'); + +/** + * Creates a program, attaches shaders, binds attrib locations, links the + * program and calls useProgram. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<!WebGLShader|string>} shaders The shaders to + * attach, or the source, or the id of a script to get + * the source from. + * @param {!Array.<string>} opt_attribs The attribs names. + * @param {!Array.<number>} opt_locations The locations for the attribs. + * @param {boolean} opt_logShaders Whether to log shader source. + */ +var setupProgram = function( + gl, shaders, opt_attribs, opt_locations, opt_logShaders) { + var realShaders = []; + var program = gl.createProgram(); + var shaderCount = 0; + for (var ii = 0; ii < shaders.length; ++ii) { + var shader = shaders[ii]; + var shaderType = undefined; + if (typeof shader == 'string') { + var element = document.getElementById(shader); + if (element) { + if (element.type != "x-shader/x-vertex" && element.type != "x-shader/x-fragment") + shaderType = ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER; + shader = loadShaderFromScript(gl, shader, shaderType, undefined, opt_logShaders); + } else if (endsWith(shader, ".vert")) { + shader = loadShaderFromFile(gl, shader, gl.VERTEX_SHADER, undefined, opt_logShaders); + } else if (endsWith(shader, ".frag")) { + shader = loadShaderFromFile(gl, shader, gl.FRAGMENT_SHADER, undefined, opt_logShaders); + } else { + shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER, undefined, opt_logShaders); + } + } else if (opt_logShaders) { + throw 'Shader source logging requested but no shader source provided'; + } + if (shader) { + ++shaderCount; + gl.attachShader(program, shader); + } + } + if (shaderCount != 2) { + error("Error in compiling shader"); + return null; + } + if (opt_attribs) { + for (var ii = 0; ii < opt_attribs.length; ++ii) { + gl.bindAttribLocation( + program, + opt_locations ? opt_locations[ii] : ii, + opt_attribs[ii]); + } + } + gl.linkProgram(program); + + // Check the link status + var linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + // something went wrong with the link + lastError = gl.getProgramInfoLog (program); + error("Error in program linking:" + lastError); + + gl.deleteProgram(program); + return null; + } + + gl.useProgram(program); + return program; +}; + +/** + * Creates a program, attaches shader, sets up trasnform feedback varyings, + * binds attrib locations, links the program and calls useProgram. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<!WebGLShader|string>} shaders The shaders to + * attach, or the source, or the id of a script to get + * the source from. + * @param {!Array.<string>} varyings The transform feedback varying names. + * @param {number} bufferMode The mode used to capture the varying variables. + * @param {!Array.<string>} opt_attribs The attribs names. + * @param {!Array.<number>} opt_locations The locations for the attribs. + * @param {boolean} opt_logShaders Whether to log shader source. + */ +var setupTransformFeedbackProgram = function( + gl, shaders, varyings, bufferMode, opt_attribs, opt_locations, opt_logShaders, opt_skipCompileStatus) { + var realShaders = []; + var program = gl.createProgram(); + var shaderCount = 0; + for (var ii = 0; ii < shaders.length; ++ii) { + var shader = shaders[ii]; + var shaderType = undefined; + if (typeof shader == 'string') { + var element = document.getElementById(shader); + if (element) { + if (element.type != "x-shader/x-vertex" && element.type != "x-shader/x-fragment") + shaderType = ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER; + shader = loadShaderFromScript(gl, shader, shaderType, undefined, opt_logShaders, opt_skipCompileStatus); + } else if (endsWith(shader, ".vert")) { + shader = loadShaderFromFile(gl, shader, gl.VERTEX_SHADER, undefined, opt_logShaders, opt_skipCompileStatus); + } else if (endsWith(shader, ".frag")) { + shader = loadShaderFromFile(gl, shader, gl.FRAGMENT_SHADER, undefined, opt_logShaders, opt_skipCompileStatus); + } else { + shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER, undefined, opt_logShaders, undefined, undefined, opt_skipCompileStatus); + } + } else if (opt_logShaders) { + throw 'Shader source logging requested but no shader source provided'; + } + if (shader) { + ++shaderCount; + gl.attachShader(program, shader); + } + } + if (shaderCount != 2) { + error("Error in compiling shader"); + return null; + } + + if (opt_attribs) { + for (var ii = 0; ii < opt_attribs.length; ++ii) { + gl.bindAttribLocation( + program, + opt_locations ? opt_locations[ii] : ii, + opt_attribs[ii]); + } + } + + gl.transformFeedbackVaryings(program, varyings, bufferMode); + + gl.linkProgram(program); + + // Check the link status + var linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + // something went wrong with the link + lastError = gl.getProgramInfoLog (program); + error("Error in program linking:" + lastError); + + gl.deleteProgram(program); + return null; + } + + gl.useProgram(program); + return program; +}; + +/** + * Creates a simple texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {WebGLProgram} + */ +var setupNoTexCoordTextureProgram = function(gl) { + return setupProgram(gl, + [noTexCoordTextureVertexShader, simpleTextureFragmentShader], + ['vPosition'], + [0]); +}; + +/** + * Creates a simple texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @param {string} opt_fragmentShaderOverride The alternative fragment shader to use. + * @return {WebGLProgram} + */ +var setupSimpleTextureProgram = function( + gl, opt_positionLocation, opt_texcoordLocation, opt_fragmentShaderOverride) { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + opt_fragmentShaderOverride = opt_fragmentShaderOverride || simpleTextureFragmentShader; + return setupProgram(gl, + [simpleTextureVertexShader, opt_fragmentShaderOverride], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); +}; + +/** + * Creates a simple texture program using glsl version 300. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @param {string} opt_fragmentShaderOverride The alternative fragment shader to use. + * @return {WebGLProgram} + */ +var setupSimpleTextureProgramESSL300 = function( + gl, opt_positionLocation, opt_texcoordLocation, opt_fragmentShaderOverride) { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + opt_fragmentShaderOverride = opt_fragmentShaderOverride || simpleTextureFragmentShaderESSL300; + return setupProgram(gl, + [simpleTextureVertexShaderESSL300, opt_fragmentShaderOverride], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); +}; + +/** + * Creates a simple cube map texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ +var setupSimpleCubeMapTextureProgram = function( + gl, opt_positionLocation, opt_texcoordLocation) { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + return setupProgram(gl, + [simpleTextureVertexShader, simpleCubeMapTextureFragmentShader], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); +}; + +/** + * Creates a simple vertex color program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_vertexColorLocation The attrib location + * for vertex colors. + * @return {WebGLProgram} + */ +var setupSimpleVertexColorProgram = function( + gl, opt_positionLocation, opt_vertexColorLocation) { + opt_positionLocation = opt_positionLocation || 0; + opt_vertexColorLocation = opt_vertexColorLocation || 1; + return setupProgram(gl, + [simpleVertexColorVertexShader, simpleVertexColorFragmentShader], + ['vPosition', 'a_color'], + [opt_positionLocation, opt_vertexColorLocation]); +}; + +/** + * Creates a simple color program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @return {WebGLProgram} + */ +var setupSimpleColorProgram = function(gl, opt_positionLocation) { + opt_positionLocation = opt_positionLocation || 0; + return setupProgram(gl, + [simpleVertexShader, simpleColorFragmentShader], + ['vPosition'], + [opt_positionLocation]); +}; + +/** + * Creates buffers for a textured unit quad and attaches them to vertex attribs. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @param {!Object} various options. See setupQuad for details. + * @return {!Array.<WebGLBuffer>} The buffer objects that were + * created. + */ +var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation, options) { + return setupQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ], + opt_positionLocation, opt_texcoordLocation, + options); +}; + +/** + * Creates buffers for a textured quad with specified lower left + * and upper right texture coordinates, and attaches them to vertex + * attribs. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner. + * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @param {!Object} various options. See setupQuad for details. + * @return {!Array.<WebGLBuffer>} The buffer objects that were + * created. + */ +var setupQuadWithTexCoords = function( + gl, lowerLeftTexCoords, upperRightTexCoords, + opt_positionLocation, opt_texcoordLocation, options) { + var defaultOptions = { + positionLocation: opt_positionLocation || 0, + texcoordLocation: opt_texcoordLocation || 1, + lowerLeftTexCoords: lowerLeftTexCoords, + upperRightTexCoords: upperRightTexCoords + }; + if (options) { + for (var prop in options) { + defaultOptions[prop] = options[prop] + } + } + return setupQuad(gl, defaultOptions); +}; + +/** + * Makes a quad with various options. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Object} options + * + * scale: scale to multiply unit quad values by. default 1.0. + * positionLocation: attribute location for position. + * texcoordLocation: attribute location for texcoords. + * If this does not exist no texture coords are created. + * lowerLeftTexCoords: an array of 2 values for the + * lowerLeftTexCoords. + * upperRightTexCoords: an array of 2 values for the + * upperRightTexCoords. + */ +var setupQuad = function(gl, options) { + var positionLocation = options.positionLocation || 0; + var scale = options.scale || 1; + + var objects = []; + + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 1.0 * scale , 1.0 * scale, + -1.0 * scale , 1.0 * scale, + -1.0 * scale , -1.0 * scale, + 1.0 * scale , 1.0 * scale, + -1.0 * scale , -1.0 * scale, + 1.0 * scale , -1.0 * scale]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(positionLocation); + gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); + objects.push(vertexObject); + + if (options.texcoordLocation !== undefined) { + var llx = options.lowerLeftTexCoords[0]; + var lly = options.lowerLeftTexCoords[1]; + var urx = options.upperRightTexCoords[0]; + var ury = options.upperRightTexCoords[1]; + + vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + urx, ury, + llx, ury, + llx, lly, + urx, ury, + llx, lly, + urx, lly]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(options.texcoordLocation); + gl.vertexAttribPointer(options.texcoordLocation, 2, gl.FLOAT, false, 0, 0); + objects.push(vertexObject); + } + + return objects; +}; + +/** + * Creates a program and buffers for rendering a textured quad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for + * position. Default = 0. + * @param {number} opt_texcoordLocation The attrib location for + * texture coords. Default = 1. + * @param {!Object} various options defined by setupQuad, plus an option + fragmentShaderOverride to specify a custom fragment shader. + * @return {!WebGLProgram} + */ +var setupTexturedQuad = function( + gl, opt_positionLocation, opt_texcoordLocation, options) { + var program = setupSimpleTextureProgram( + gl, opt_positionLocation, opt_texcoordLocation, options && options.fragmentShaderOverride); + setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation, options); + return program; +}; + +/** + * Creates a program and buffers for rendering a color quad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {!Object} various options. See setupQuad for details. + * @return {!WebGLProgram} + */ +var setupColorQuad = function(gl, opt_positionLocation, options) { + opt_positionLocation = opt_positionLocation || 0; + var program = setupSimpleColorProgram(gl, opt_positionLocation); + setupUnitQuad(gl, opt_positionLocation, 0, options); + return program; +}; + +/** + * Creates a program and buffers for rendering a textured quad with + * specified lower left and upper right texture coordinates. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner. + * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {!WebGLProgram} + */ +var setupTexturedQuadWithTexCoords = function( + gl, lowerLeftTexCoords, upperRightTexCoords, + opt_positionLocation, opt_texcoordLocation) { + var program = setupSimpleTextureProgram( + gl, opt_positionLocation, opt_texcoordLocation); + setupQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords, + opt_positionLocation, opt_texcoordLocation); + return program; +}; + +/** + * Creates a program and buffers for rendering a textured quad with + * a cube map texture. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for + * position. Default = 0. + * @param {number} opt_texcoordLocation The attrib location for + * texture coords. Default = 1. + * @return {!WebGLProgram} + */ +var setupTexturedQuadWithCubeMap = function( + gl, opt_positionLocation, opt_texcoordLocation) { + var program = setupSimpleCubeMapTextureProgram( + gl, opt_positionLocation, opt_texcoordLocation); + setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation, undefined); + return program; +}; + +/** + * Creates a unit quad with only positions of a given resolution. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} gridRes The resolution of the mesh grid, + * expressed in the number of quads across and down. + * @param {number} opt_positionLocation The attrib location for position. + */ +var setupIndexedQuad = function ( + gl, gridRes, opt_positionLocation, opt_flipOddTriangles) { + return setupIndexedQuadWithOptions(gl, + { gridRes: gridRes, + positionLocation: opt_positionLocation, + flipOddTriangles: opt_flipOddTriangles + }); +}; + +/** + * Creates a quad with various options. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Object} options The options. See below. + * @return {!Array.<WebGLBuffer>} The created buffers. + * [positions, <colors>, indices] + * + * Options: + * gridRes: number of quads across and down grid. + * positionLocation: attrib location for position + * flipOddTriangles: reverse order of vertices of every other + * triangle + * positionOffset: offset added to each vertex + * positionMult: multipier for each vertex + * colorLocation: attrib location for vertex colors. If + * undefined no vertex colors will be created. + */ +var setupIndexedQuadWithOptions = function (gl, options) { + var positionLocation = options.positionLocation || 0; + var objects = []; + + var gridRes = options.gridRes || 1; + var positionOffset = options.positionOffset || 0; + var positionMult = options.positionMult || 1; + var vertsAcross = gridRes + 1; + var numVerts = vertsAcross * vertsAcross; + var positions = new Float32Array(numVerts * 3); + var indices = new Uint16Array(6 * gridRes * gridRes); + var poffset = 0; + + for (var yy = 0; yy <= gridRes; ++yy) { + for (var xx = 0; xx <= gridRes; ++xx) { + positions[poffset + 0] = (-1 + 2 * xx / gridRes) * positionMult + positionOffset; + positions[poffset + 1] = (-1 + 2 * yy / gridRes) * positionMult + positionOffset; + positions[poffset + 2] = 0; + + poffset += 3; + } + } + + var buf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buf); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + gl.enableVertexAttribArray(positionLocation); + gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); + objects.push(buf); + + if (options.colorLocation !== undefined) { + var colors = new Float32Array(numVerts * 4); + poffset = 0; + for (var yy = 0; yy <= gridRes; ++yy) { + for (var xx = 0; xx <= gridRes; ++xx) { + if (options.color !== undefined) { + colors[poffset + 0] = options.color[0]; + colors[poffset + 1] = options.color[1]; + colors[poffset + 2] = options.color[2]; + colors[poffset + 3] = options.color[3]; + } else { + colors[poffset + 0] = xx / gridRes; + colors[poffset + 1] = yy / gridRes; + colors[poffset + 2] = (xx / gridRes) * (yy / gridRes); + colors[poffset + 3] = (yy % 2) * 0.5 + 0.5; + } + poffset += 4; + } + } + + buf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buf); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + gl.enableVertexAttribArray(options.colorLocation); + gl.vertexAttribPointer(options.colorLocation, 4, gl.FLOAT, false, 0, 0); + objects.push(buf); + } + + var tbase = 0; + for (var yy = 0; yy < gridRes; ++yy) { + var index = yy * vertsAcross; + for (var xx = 0; xx < gridRes; ++xx) { + indices[tbase + 0] = index + 0; + indices[tbase + 1] = index + 1; + indices[tbase + 2] = index + vertsAcross; + indices[tbase + 3] = index + vertsAcross; + indices[tbase + 4] = index + 1; + indices[tbase + 5] = index + vertsAcross + 1; + + if (options.flipOddTriangles) { + indices[tbase + 4] = index + vertsAcross + 1; + indices[tbase + 5] = index + 1; + } + + index += 1; + tbase += 6; + } + } + + buf = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + objects.push(buf); + + return objects; +}; + +/** + * Returns the constructor for a typed array that corresponds to the given + * WebGL type. + * @param {!WebGLRenderingContext} gl A WebGLRenderingContext. + * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE) + * @return {!Constructor} The typed array constructor that + * corresponds to the given type. + */ +var glTypeToTypedArrayType = function(gl, type) { + switch (type) { + case gl.BYTE: + return window.Int8Array; + case gl.UNSIGNED_BYTE: + return window.Uint8Array; + case gl.SHORT: + return window.Int16Array; + case gl.UNSIGNED_SHORT: + case gl.UNSIGNED_SHORT_5_6_5: + case gl.UNSIGNED_SHORT_4_4_4_4: + case gl.UNSIGNED_SHORT_5_5_5_1: + return window.Uint16Array; + case gl.INT: + return window.Int32Array; + case gl.UNSIGNED_INT: + case gl.UNSIGNED_INT_5_9_9_9_REV: + case gl.UNSIGNED_INT_10F_11F_11F_REV: + case gl.UNSIGNED_INT_2_10_10_10_REV: + case gl.UNSIGNED_INT_24_8: + return window.Uint32Array; + case gl.HALF_FLOAT: + case 0x8D61: // HALF_FLOAT_OES + return window.Uint16Array; + case gl.FLOAT: + return window.Float32Array; + default: + throw 'unknown gl type ' + glEnumToString(gl, type); + } +}; + +/** + * Returns the number of bytes per component for a given WebGL type. + * @param {!WebGLRenderingContext} gl A WebGLRenderingContext. + * @param {GLenum} type The WebGL type (eg, gl.UNSIGNED_BYTE) + * @return {number} The number of bytes per component. + */ +var getBytesPerComponent = function(gl, type) { + switch (type) { + case gl.BYTE: + case gl.UNSIGNED_BYTE: + return 1; + case gl.SHORT: + case gl.UNSIGNED_SHORT: + case gl.UNSIGNED_SHORT_5_6_5: + case gl.UNSIGNED_SHORT_4_4_4_4: + case gl.UNSIGNED_SHORT_5_5_5_1: + case gl.HALF_FLOAT: + case 0x8D61: // HALF_FLOAT_OES + return 2; + case gl.INT: + case gl.UNSIGNED_INT: + case gl.UNSIGNED_INT_5_9_9_9_REV: + case gl.UNSIGNED_INT_10F_11F_11F_REV: + case gl.UNSIGNED_INT_2_10_10_10_REV: + case gl.UNSIGNED_INT_24_8: + case gl.FLOAT: + return 4; + default: + throw 'unknown gl type ' + glEnumToString(gl, type); + } +}; + +/** + * Returns the number of typed array elements per pixel for a given WebGL + * format/type combination. The corresponding typed array type can be determined + * by calling glTypeToTypedArrayType. + * @param {!WebGLRenderingContext} gl A WebGLRenderingContext. + * @param {GLenum} format The WebGL format (eg, gl.RGBA) + * @param {GLenum} type The WebGL type (eg, gl.UNSIGNED_BYTE) + * @return {number} The number of typed array elements per pixel. + */ +var getTypedArrayElementsPerPixel = function(gl, format, type) { + switch (type) { + case gl.UNSIGNED_SHORT_5_6_5: + case gl.UNSIGNED_SHORT_4_4_4_4: + case gl.UNSIGNED_SHORT_5_5_5_1: + return 1; + case gl.UNSIGNED_BYTE: + break; + default: + throw 'not a gl type for color information ' + glEnumToString(gl, type); + } + + switch (format) { + case gl.RGBA: + return 4; + case gl.RGB: + return 3; + case gl.LUMINANCE_ALPHA: + return 2; + case gl.LUMINANCE: + case gl.ALPHA: + return 1; + default: + throw 'unknown gl format ' + glEnumToString(gl, format); + } +}; + +/** + * Fills the given texture with a solid color. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!WebGLTexture} tex The texture to fill. + * @param {number} width The width of the texture to create. + * @param {number} height The height of the texture to create. + * @param {!Array.<number>} color The color to fill with. + * where each element is in the range 0 to 255. + * @param {number} opt_level The level of the texture to fill. Default = 0. + * @param {number} opt_format The format for the texture. + * @param {number} opt_internalFormat The internal format for the texture. + */ +var fillTexture = function(gl, tex, width, height, color, opt_level, opt_format, opt_type, opt_internalFormat) { + opt_level = opt_level || 0; + opt_format = opt_format || gl.RGBA; + opt_type = opt_type || gl.UNSIGNED_BYTE; + opt_internalFormat = opt_internalFormat || opt_format; + var pack = gl.getParameter(gl.UNPACK_ALIGNMENT); + var numComponents = color.length; + var bytesPerComponent = getBytesPerComponent(gl, opt_type); + var rowSize = numComponents * width * bytesPerComponent; + // See equation 3.10 in ES 2.0 spec and equation 3.13 in ES 3.0 spec for paddedRowLength calculation. + // k is paddedRowLength. + // n is numComponents. + // l is width. + // a is pack. + // s is bytesPerComponent. + var paddedRowLength; + if (bytesPerComponent >= pack) + paddedRowLength = numComponents * width; + else + paddedRowLength = Math.floor((rowSize + pack - 1) / pack) * pack / bytesPerComponent; + var size = width * numComponents + (height - 1) * paddedRowLength; + var buf = new (glTypeToTypedArrayType(gl, opt_type))(size); + for (var yy = 0; yy < height; ++yy) { + var off = yy * paddedRowLength; + for (var xx = 0; xx < width; ++xx) { + for (var jj = 0; jj < numComponents; ++jj) { + buf[off++] = color[jj]; + } + } + } + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D( + gl.TEXTURE_2D, opt_level, opt_internalFormat, width, height, 0, + opt_format, opt_type, buf); +}; + +/** + * Creates a texture and fills it with a solid color. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} width The width of the texture to create. + * @param {number} height The height of the texture to create. + * @param {!Array.<number>} color The color to fill with. A 4 element array + * where each element is in the range 0 to 255. + * @return {!WebGLTexture} + */ +var createColoredTexture = function(gl, width, height, color) { + var tex = gl.createTexture(); + fillTexture(gl, tex, width, height, color); + return tex; +}; + +var ubyteToFloat = function(c) { + return c / 255; +}; + +var ubyteColorToFloatColor = function(color) { + var floatColor = []; + for (var ii = 0; ii < color.length; ++ii) { + floatColor[ii] = ubyteToFloat(color[ii]); + } + return floatColor; +}; + +/** + * Sets the "u_color" uniform of the current program to color. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<number>} color 4 element array of 0-1 color + * components. + */ +var setFloatDrawColor = function(gl, color) { + var program = gl.getParameter(gl.CURRENT_PROGRAM); + var colorLocation = gl.getUniformLocation(program, "u_color"); + gl.uniform4fv(colorLocation, color); +}; + +/** + * Sets the "u_color" uniform of the current program to color. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<number>} color 4 element array of 0-255 color + * components. + */ +var setUByteDrawColor = function(gl, color) { + setFloatDrawColor(gl, ubyteColorToFloatColor(color)); +}; + +/** + * Draws a previously setup quad in the given color. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<number>} color The color to draw with. A 4 + * element array where each element is in the range 0 to + * 1. + */ +var drawFloatColorQuad = function(gl, color) { + var program = gl.getParameter(gl.CURRENT_PROGRAM); + var colorLocation = gl.getUniformLocation(program, "u_color"); + gl.uniform4fv(colorLocation, color); + gl.drawArrays(gl.TRIANGLES, 0, 6); +}; + + +/** + * Draws a previously setup quad in the given color. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<number>} color The color to draw with. A 4 + * element array where each element is in the range 0 to + * 255. + */ +var drawUByteColorQuad = function(gl, color) { + drawFloatColorQuad(gl, ubyteColorToFloatColor(color)); +}; + +/** + * Draws a previously setupUnitQuad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + */ +var drawUnitQuad = function(gl) { + gl.drawArrays(gl.TRIANGLES, 0, 6); +}; + +var dummySetProgramAndDrawNothing = function(gl) { + if (!gl._wtuDummyProgram) { + gl._wtuDummyProgram = setupProgram(gl, [ + "void main() { gl_Position = vec4(0.0); }", + "void main() { gl_FragColor = vec4(0.0); }" + ], [], []); + } + gl.useProgram(gl._wtuDummyProgram); + gl.drawArrays(gl.TRIANGLES, 0, 3); +}; + +/** + * Clears then Draws a previously setupUnitQuad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!Array.<number>} opt_color The color to fill clear with before + * drawing. A 4 element array where each element is in the range 0 to + * 255. Default [255, 255, 255, 255] + */ +var clearAndDrawUnitQuad = function(gl, opt_color) { + opt_color = opt_color || [255, 255, 255, 255]; + gl.clearColor( + opt_color[0] / 255, + opt_color[1] / 255, + opt_color[2] / 255, + opt_color[3] / 255); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + drawUnitQuad(gl); +}; + +/** + * Draws a quad previously setup with setupIndexedQuad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} gridRes Resolution of grid. + */ +var drawIndexedQuad = function(gl, gridRes) { + gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0); +}; + +/** + * Draws a previously setupIndexedQuad + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} gridRes Resolution of grid. + * @param {!Array.<number>} opt_color The color to fill clear with before + * drawing. A 4 element array where each element is in the range 0 to + * 255. Default [255, 255, 255, 255] + */ +var clearAndDrawIndexedQuad = function(gl, gridRes, opt_color) { + opt_color = opt_color || [255, 255, 255, 255]; + gl.clearColor( + opt_color[0] / 255, + opt_color[1] / 255, + opt_color[2] / 255, + opt_color[3] / 255); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + drawIndexedQuad(gl, gridRes); +}; + +/** + * Clips a range to min, max + * (Eg. clipToRange(-5,7,0,20) would return {value:0,extent:2} + * @param {number} value start of range + * @param {number} extent extent of range + * @param {number} min min. + * @param {number} max max. + * @return {!{value:number,extent:number}} The clipped value. + */ +var clipToRange = function(value, extent, min, max) { + if (value < min) { + extent -= min - value; + value = min; + } + var end = value + extent; + if (end > max) { + extent -= end - max; + } + if (extent < 0) { + value = max; + extent = 0; + } + return {value:value, extent: extent}; +}; + +/** + * Determines if the passed context is an instance of a WebGLRenderingContext + * or later variant (like WebGL2RenderingContext) + * @param {CanvasRenderingContext} ctx The context to check. + */ +var isWebGLContext = function(ctx) { + if (ctx instanceof WebGLRenderingContext) + return true; + + if ('WebGL2RenderingContext' in window && ctx instanceof WebGL2RenderingContext) + return true; + + return false; +}; + +/** + * Creates a check rect is used by checkCanvasRects. + * @param {number} x left corner of region to check. + * @param {number} y bottom corner of region to check in case of checking from + * a GL context or top corner in case of checking from a 2D context. + * @param {number} width width of region to check. + * @param {number} height width of region to check. + * @param {!Array.<number>} color The color expected. A 4 element array where + * each element is in the range 0 to 255. + * @param {string} opt_msg Message to associate with success. Eg + * ("should be red"). + * @param {number} opt_errorRange Optional. Acceptable error in + * color checking. 0 by default. + */ +var makeCheckRect = function(x, y, width, height, color, msg, errorRange) { + var rect = { + 'x': x, 'y': y, + 'width': width, 'height': height, + 'color': color, 'msg': msg, + 'errorRange': errorRange, + + 'checkRect': function (buf, l, b, w) { + for (var px = (x - l) ; px < (x + width - l) ; ++px) { + for (var py = (y - b) ; py < (y + height - b) ; ++py) { + var offset = (py * w + px) * 4; + for (var j = 0; j < color.length; ++j) { + if (Math.abs(buf[offset + j] - color[j]) > errorRange) { + testFailed(msg); + var was = buf[offset + 0].toString(); + for (j = 1; j < color.length; ++j) { + was += "," + buf[offset + j]; + } + debug('at (' + px + ', ' + py + + ') expected: ' + color + ' was ' + was); + return; + } + } + } + } + testPassed(msg); + } + } + return rect; +}; + +/** + * Checks that a portions of a canvas or the currently attached framebuffer is 1 color. + * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The + * WebGLRenderingContext or 2D context to use. + * @param {!Array.<checkRect>} array of rects to check for matching color. + */ +var checkCanvasRects = function(gl, rects) { + if (rects.length > 0) { + var left = rects[0].x; + var right = rects[0].x + rects[1].width; + var bottom = rects[0].y; + var top = rects[0].y + rects[0].height; + for (var i = 1; i < rects.length; ++i) { + left = Math.min(left, rects[i].x); + right = Math.max(right, rects[i].x + rects[i].width); + bottom = Math.min(bottom, rects[i].y); + top = Math.max(top, rects[i].y + rects[i].height); + } + var width = right - left; + var height = top - bottom; + var buf = new Uint8Array(width * height * 4); + gl.readPixels(left, bottom, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); + for (var i = 0; i < rects.length; ++i) { + rects[i].checkRect(buf, left, bottom, width); + } + } +}; + +/** + * Checks that a portion of a canvas or the currently attached framebuffer is 1 color. + * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The + * WebGLRenderingContext or 2D context to use. + * @param {number} x left corner of region to check. + * @param {number} y bottom corner of region to check in case of checking from + * a GL context or top corner in case of checking from a 2D context. + * @param {number} width width of region to check. + * @param {number} height width of region to check. + * @param {!Array.<number>} color The color expected. A 4 element array where + * each element is in the range 0 to 255. + * @param {number} opt_errorRange Optional. Acceptable error in + * color checking. 0 by default. + * @param {!function()} sameFn Function to call if all pixels + * are the same as color. + * @param {!function()} differentFn Function to call if a pixel + * is different than color + * @param {!function()} logFn Function to call for logging. + * @param {TypedArray} opt_readBackBuf optional buffer to read back into. + * Typically passed to either reuse buffer, or support readbacks from + * floating-point/norm16 framebuffers. + * @param {GLenum} opt_readBackType optional read back type, defaulting to + * gl.UNSIGNED_BYTE. Can be used to support readback from floating-point + * /norm16 framebuffers. + * @param {GLenum} opt_readBackFormat optional read back format, defaulting to + * gl.RGBA. Can be used to support readback from norm16 + * framebuffers. + */ +var checkCanvasRectColor = function(gl, x, y, width, height, color, opt_errorRange, sameFn, differentFn, logFn, opt_readBackBuf, opt_readBackType, opt_readBackFormat) { + if (isWebGLContext(gl) && !gl.getParameter(gl.FRAMEBUFFER_BINDING)) { + // We're reading the backbuffer so clip. + var xr = clipToRange(x, width, 0, gl.canvas.width); + var yr = clipToRange(y, height, 0, gl.canvas.height); + if (!xr.extent || !yr.extent) { + logFn("checking rect: effective width or height is zero"); + sameFn(); + return; + } + x = xr.value; + y = yr.value; + width = xr.extent; + height = yr.extent; + } + var errorRange = opt_errorRange || 0; + if (!errorRange.length) { + errorRange = [errorRange, errorRange, errorRange, errorRange] + } + var buf; + if (isWebGLContext(gl)) { + buf = opt_readBackBuf ? opt_readBackBuf : new Uint8Array(width * height * 4); + var readBackType = opt_readBackType ? opt_readBackType : gl.UNSIGNED_BYTE; + var readBackFormat = opt_readBackFormat ? opt_readBackFormat : gl.RGBA; + gl.readPixels(x, y, width, height, readBackFormat, readBackType, buf); + } else { + buf = gl.getImageData(x, y, width, height).data; + } + for (var i = 0; i < width * height; ++i) { + var offset = i * 4; + for (var j = 0; j < color.length; ++j) { + if (Math.abs(buf[offset + j] - color[j]) > errorRange[j]) { + var was = buf[offset + 0].toString(); + for (j = 1; j < color.length; ++j) { + was += "," + buf[offset + j]; + } + differentFn('at (' + (x + (i % width)) + ', ' + (y + Math.floor(i / width)) + + ') expected: ' + color + ' was ' + was, buf); + return; + } + } + } + sameFn(); +}; + +/** + * Checks that a portion of a canvas or the currently attached framebuffer is 1 color. + * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The + * WebGLRenderingContext or 2D context to use. + * @param {number} x left corner of region to check. + * @param {number} y bottom corner of region to check in case of checking from + * a GL context or top corner in case of checking from a 2D context. + * @param {number} width width of region to check. + * @param {number} height width of region to check. + * @param {!Array.<number>} color The color expected. A 4 element array where + * each element is in the range 0 to 255. + * @param {string} opt_msg Message to associate with success or failure. Eg + * ("should be red"). + * @param {number} opt_errorRange Optional. Acceptable error in + * color checking. 0 by default. + * @param {TypedArray} opt_readBackBuf optional buffer to read back into. + * Typically passed to either reuse buffer, or support readbacks from + * floating-point/norm16 framebuffers. + * @param {GLenum} opt_readBackType optional read back type, defaulting to + * gl.UNSIGNED_BYTE. Can be used to support readback from floating-point + * /norm16 framebuffers. + * @param {GLenum} opt_readBackFormat optional read back format, defaulting to + * gl.RGBA. Can be used to support readback from floating-point + * /norm16 framebuffers. + */ +var checkCanvasRect = function(gl, x, y, width, height, color, opt_msg, opt_errorRange, opt_readBackBuf, opt_readBackType, opt_readBackFormat) { + checkCanvasRectColor( + gl, x, y, width, height, color, opt_errorRange, + function() { + var msg = opt_msg; + if (msg === undefined) + msg = "should be " + color.toString(); + testPassed(msg); + }, + function(differentMsg) { + var msg = opt_msg; + if (msg === undefined) + msg = "should be " + color.toString(); + testFailed(msg + "\n" + differentMsg); + }, + debug, + opt_readBackBuf, + opt_readBackType, + opt_readBackFormat); +}; + +/** + * Checks that an entire canvas or the currently attached framebuffer is 1 color. + * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The + * WebGLRenderingContext or 2D context to use. + * @param {!Array.<number>} color The color expected. A 4 element array where + * each element is in the range 0 to 255. + * @param {string} msg Message to associate with success. Eg ("should be red"). + * @param {number} errorRange Optional. Acceptable error in + * color checking. 0 by default. + */ +var checkCanvas = function(gl, color, msg, errorRange) { + checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, errorRange); +}; + +/** + * Checks a rectangular area both inside the area and outside + * the area. + * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The + * WebGLRenderingContext or 2D context to use. + * @param {number} x left corner of region to check. + * @param {number} y bottom corner of region to check in case of checking from + * a GL context or top corner in case of checking from a 2D context. + * @param {number} width width of region to check. + * @param {number} height width of region to check. + * @param {!Array.<number>} innerColor The color expected inside + * the area. A 4 element array where each element is in the + * range 0 to 255. + * @param {!Array.<number>} outerColor The color expected + * outside. A 4 element array where each element is in the + * range 0 to 255. + * @param {!number} opt_edgeSize: The number of pixels to skip + * around the edges of the area. Defaut 0. + * @param {!{width:number, height:number}} opt_outerDimensions + * The outer dimensions. Default the size of gl.canvas. + */ +var checkAreaInAndOut = function(gl, x, y, width, height, innerColor, outerColor, opt_edgeSize, opt_outerDimensions) { + var outerDimensions = opt_outerDimensions || { width: gl.canvas.width, height: gl.canvas.height }; + var edgeSize = opt_edgeSize || 0; + checkCanvasRect(gl, x + edgeSize, y + edgeSize, width - edgeSize * 2, height - edgeSize * 2, innerColor); + checkCanvasRect(gl, 0, 0, x - edgeSize, outerDimensions.height, outerColor); + checkCanvasRect(gl, x + width + edgeSize, 0, outerDimensions.width - x - width - edgeSize, outerDimensions.height, outerColor); + checkCanvasRect(gl, 0, 0, outerDimensions.width, y - edgeSize, outerColor); + checkCanvasRect(gl, 0, y + height + edgeSize, outerDimensions.width, outerDimensions.height - y - height - edgeSize, outerColor); +}; + +/** + * Checks that an entire buffer matches the floating point values provided. + * (WebGL 2.0 only) + * @param {!WebGL2RenderingContext} gl The WebGL2RenderingContext to use. + * @param {number} target The buffer target to bind to. + * @param {!Array.<number>} expected The values expected. + * @param {string} opt_msg Optional. Message to associate with success. Eg ("should be red"). + * @param {number} opt_errorRange Optional. Acceptable error in value checking. 0.001 by default. + */ +var checkFloatBuffer = function(gl, target, expected, opt_msg, opt_errorRange) { + if (opt_msg === undefined) + opt_msg = "buffer should match expected values"; + + if (opt_errorRange === undefined) + opt_errorRange = 0.001; + + var floatArray = new Float32Array(expected.length); + gl.getBufferSubData(target, 0, floatArray); + + for (var i = 0; i < expected.length; i++) { + if (Math.abs(floatArray[i] - expected[i]) > opt_errorRange) { + testFailed(opt_msg); + debug('at [' + i + '] expected: ' + expected[i] + ' was ' + floatArray[i]); + return; + } + } + testPassed(opt_msg); +}; + +/** + * Loads a texture, calls callback when finished. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} url URL of image to load + * @param {function(!Image): void} callback Function that gets called after + * image has loaded + * @return {!WebGLTexture} The created texture. + */ +var loadTexture = function(gl, url, callback) { + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + var image = new Image(); + image.onload = function() { + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + callback(image); + }; + image.src = url; + return texture; +}; + +/** + * Checks whether the bound texture has expected dimensions. One corner pixel + * of the texture will be changed as a side effect. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!WebGLTexture} texture The texture to check. + * @param {number} width Expected width. + * @param {number} height Expected height. + * @param {GLenum} opt_format The texture's format. Defaults to RGBA. + * @param {GLenum} opt_type The texture's type. Defaults to UNSIGNED_BYTE. + */ +var checkTextureSize = function(gl, width, height, opt_format, opt_type) { + opt_format = opt_format || gl.RGBA; + opt_type = opt_type || gl.UNSIGNED_BYTE; + + var numElements = getTypedArrayElementsPerPixel(gl, opt_format, opt_type); + var buf = new (glTypeToTypedArrayType(gl, opt_type))(numElements); + + var errors = 0; + gl.texSubImage2D(gl.TEXTURE_2D, 0, width - 1, height - 1, 1, 1, opt_format, opt_type, buf); + if (gl.getError() != gl.NO_ERROR) { + testFailed("Texture was smaller than the expected size " + width + "x" + height); + ++errors; + } + gl.texSubImage2D(gl.TEXTURE_2D, 0, width - 1, height, 1, 1, opt_format, opt_type, buf); + if (gl.getError() == gl.NO_ERROR) { + testFailed("Texture was taller than " + height); + ++errors; + } + gl.texSubImage2D(gl.TEXTURE_2D, 0, width, height - 1, 1, 1, opt_format, opt_type, buf); + if (gl.getError() == gl.NO_ERROR) { + testFailed("Texture was wider than " + width); + ++errors; + } + if (errors == 0) { + testPassed("Texture had the expected size " + width + "x" + height); + } +}; + +/** + * Makes a shallow copy of an object. + * @param {!Object} src Object to copy + * @return {!Object} The copy of src. + */ +var shallowCopyObject = function(src) { + var dst = {}; + for (var attr in src) { + if (src.hasOwnProperty(attr)) { + dst[attr] = src[attr]; + } + } + return dst; +}; + +/** + * Checks if an attribute exists on an object case insensitive. + * @param {!Object} obj Object to check + * @param {string} attr Name of attribute to look for. + * @return {string?} The name of the attribute if it exists, + * undefined if not. + */ +var hasAttributeCaseInsensitive = function(obj, attr) { + var lower = attr.toLowerCase(); + for (var key in obj) { + if (obj.hasOwnProperty(key) && key.toLowerCase() == lower) { + return key; + } + } +}; + +/** + * Returns a map of URL querystring options + * @return {Object?} Object containing all the values in the URL querystring + */ +var getUrlOptions = (function() { + var _urlOptionsParsed = false; + var _urlOptions = {}; + return function() { + if (!_urlOptionsParsed) { + var s = window.location.href; + var q = s.indexOf("?"); + var e = s.indexOf("#"); + if (e < 0) { + e = s.length; + } + var query = s.substring(q + 1, e); + var pairs = query.split("&"); + for (var ii = 0; ii < pairs.length; ++ii) { + var keyValue = pairs[ii].split("="); + var key = keyValue[0]; + var value = decodeURIComponent(keyValue[1]); + _urlOptions[key] = value; + } + _urlOptionsParsed = true; + } + + return _urlOptions; + } +})(); + +var default3DContextVersion = 1; + +/** + * Set the default context version for create3DContext. + * Initially the default version is 1. + * @param {number} Default version of WebGL contexts. + */ +var setDefault3DContextVersion = function(version) { + default3DContextVersion = version; +}; + +/** + * Get the default contex version for create3DContext. + * First it looks at the URI option |webglVersion|. If it does not exist, + * then look at the global default3DContextVersion variable. + */ +var getDefault3DContextVersion = function() { + return parseInt(getUrlOptions().webglVersion, 10) || default3DContextVersion; +}; + +/** + * Creates a webgl context. + * @param {!Canvas|string} opt_canvas The canvas tag to get + * context from. If one is not passed in one will be + * created. If it's a string it's assumed to be the id of a + * canvas. + * @param {Object} opt_attributes Context attributes. + * @param {!number} opt_version Version of WebGL context to create. + * The default version can be set by calling setDefault3DContextVersion. + * @return {!WebGLRenderingContext} The created context. + */ +var create3DContext = function(opt_canvas, opt_attributes, opt_version) { + if (window.initTestingHarness) { + window.initTestingHarness(); + } + var attributes = shallowCopyObject(opt_attributes || {}); + if (!hasAttributeCaseInsensitive(attributes, "antialias")) { + attributes.antialias = false; + } + + const parseString = v => v; + const parseBoolean = v => v.toLowerCase().startsWith('t') || parseFloat(v) > 0; + const params = new URLSearchParams(window.location.search); + for (const [key, parseFn] of Object.entries({ + alpha: parseBoolean, + antialias: parseBoolean, + depth: parseBoolean, + desynchronized: parseBoolean, + failIfMajorPerformanceCaveat: parseBoolean, + powerPreference: parseString, + premultipliedAlpha: parseBoolean, + preserveDrawingBuffer: parseBoolean, + stencil: parseBoolean, + })) { + const value = params.get(key); + if (value) { + const v = parseFn(value); + attributes[key] = v; + debug(`setting context attribute: ${key} = ${v}`); + } + } + + if (!opt_version) { + opt_version = getDefault3DContextVersion(); + } + opt_canvas = opt_canvas || document.createElement("canvas"); + if (typeof opt_canvas == 'string') { + opt_canvas = document.getElementById(opt_canvas); + } + var context = null; + + var names; + switch (opt_version) { + case 2: + names = ["webgl2"]; break; + default: + names = ["webgl", "experimental-webgl"]; break; + } + + for (var i = 0; i < names.length; ++i) { + try { + context = opt_canvas.getContext(names[i], attributes); + } catch (e) { + } + if (context) { + break; + } + } + if (!context) { + testFailed("Unable to fetch WebGL rendering context for Canvas"); + } else { + if (!window._wtu_contexts) { + window._wtu_contexts = [] + } + window._wtu_contexts.push(context); + } + + if (params.get('showRenderer')) { + const ext = context.getExtension('WEBGL_debug_renderer_info'); + debug(`RENDERER: ${context.getParameter(ext ? ext.UNMASKED_RENDERER_WEBGL : context.RENDERER)}`); + } + + return context; +}; + +/** + * Indicates whether the given context is WebGL 2.0 or greater. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {boolean} True if the given context is WebGL 2.0 or greater. + */ +var isWebGL2 = function(gl) { + // Duck typing is used so that the conformance suite can be run + // against libraries emulating WebGL 1.0 on top of WebGL 2.0. + return !!gl.drawArraysInstanced; +}; + +/** + * Defines the exception type for a GL error. + * @constructor + * @param {string} message The error message. + * @param {number} error GL error code + */ +function GLErrorException (message, error) { + this.message = message; + this.name = "GLErrorException"; + this.error = error; +}; + +/** + * Wraps a WebGL function with a function that throws an exception if there is + * an error. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} fname Name of function to wrap. + * @return {function()} The wrapped function. + */ +var createGLErrorWrapper = function(context, fname) { + return function() { + var rv = context[fname].apply(context, arguments); + var err = context.getError(); + if (err != context.NO_ERROR) { + var msg = "GL error " + glEnumToString(context, err) + " in " + fname; + throw new GLErrorException(msg, err); + } + return rv; + }; +}; + +/** + * Creates a WebGL context where all functions are wrapped to throw an exception + * if there is an error. + * @param {!Canvas} canvas The HTML canvas to get a context from. + * @param {Object} opt_attributes Context attributes. + * @param {!number} opt_version Version of WebGL context to create + * @return {!Object} The wrapped context. + */ +function create3DContextWithWrapperThatThrowsOnGLError(canvas, opt_attributes, opt_version) { + var context = create3DContext(canvas, opt_attributes, opt_version); + var wrap = {}; + for (var i in context) { + try { + if (typeof context[i] == 'function') { + wrap[i] = createGLErrorWrapper(context, i); + } else { + wrap[i] = context[i]; + } + } catch (e) { + error("createContextWrapperThatThrowsOnGLError: Error accessing " + i); + } + } + wrap.getError = function() { + return context.getError(); + }; + return wrap; +}; + +/** + * Tests that an evaluated expression generates a specific GL error. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number|Array.<number>} glErrors The expected gl error or an array of expected errors. + * @param {string} evalStr The string to evaluate. + */ +var shouldGenerateGLError = function(gl, glErrors, evalStr, opt_msg) { + var exception; + try { + eval(evalStr); + } catch (e) { + exception = e; + } + if (exception) { + testFailed(evalStr + " threw exception " + exception); + return -1; + } else { + if (!opt_msg) { + opt_msg = "after evaluating: " + evalStr; + } + return glErrorShouldBe(gl, glErrors, opt_msg); + } +}; + +/** + * Tests that an evaluated expression does not generate a GL error. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} evalStr The string to evaluate. + */ +var failIfGLError = function(gl, evalStr) { + var exception; + try { + eval(evalStr); + } catch (e) { + exception = e; + } + if (exception) { + testFailed(evalStr + " threw exception " + exception); + } else { + glErrorShouldBeImpl(gl, gl.NO_ERROR, false, "after evaluating: " + evalStr); + } +}; + +/** + * Tests that the first error GL returns is the specified error. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number|Array.<number>} glErrors The expected gl error or an array of expected errors. + * @param {string} opt_msg Optional additional message. + */ +var glErrorShouldBe = function(gl, glErrors, opt_msg) { + return glErrorShouldBeImpl(gl, glErrors, true, opt_msg); +}; + +const glErrorAssert = function(gl, glErrors, opt_msg) { + return glErrorShouldBeImpl(gl, glErrors, false, opt_msg); +}; + +/** + * Tests that the given framebuffer has a specific status + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number|Array.<number>} glStatuses The expected gl + * status or an array of expected statuses. + * @param {string} opt_msg Optional additional message. + */ +var framebufferStatusShouldBe = function(gl, target, glStatuses, opt_msg) { + if (!glStatuses.length) { + glStatuses = [glStatuses]; + } + opt_msg = opt_msg || ""; + const status = gl.checkFramebufferStatus(target); + const ndx = glStatuses.indexOf(status); + const expected = glStatuses.map((status) => { + return glEnumToString(gl, status); + }).join(' or '); + if (ndx < 0) { + let msg = "checkFramebufferStatus expected" + ((glStatuses.length > 1) ? " one of: " : ": ") + + expected + ". Was " + glEnumToString(gl, status); + if (opt_msg) { + msg += ": " + opt_msg; + } + testFailed(msg); + return false; + } + let msg = `checkFramebufferStatus was ${glEnumToString(gl, status)}`; + if (glStatuses.length > 1) { + msg += `, one of: ${expected}`; + } + if (opt_msg) { + msg += ": " + opt_msg; + } + testPassed(msg); + return [status]; +} + +/** + * Tests that the first error GL returns is the specified error. Allows suppression of successes. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number|Array.<number>} glErrors The expected gl error or an array of expected errors. + * @param {boolean} reportSuccesses Whether to report successes as passes, or to silently pass. + * @param {string} opt_msg Optional additional message. + */ +var glErrorShouldBeImpl = function(gl, glErrors, reportSuccesses, opt_msg) { + if (!glErrors.length) { + glErrors = [glErrors]; + } + opt_msg = opt_msg || ""; + + const fnErrStr = function(errVal) { + if (errVal == 0) return "NO_ERROR"; + return glEnumToString(gl, errVal); + }; + + var err = gl.getError(); + var ndx = glErrors.indexOf(err); + var errStrs = []; + for (var ii = 0; ii < glErrors.length; ++ii) { + errStrs.push(fnErrStr(glErrors[ii])); + } + var expected = errStrs.join(" or "); + if (ndx < 0) { + var msg = "getError expected" + ((glErrors.length > 1) ? " one of: " : ": "); + testFailed(msg + expected + ". Was " + fnErrStr(err) + " : " + opt_msg); + } else if (reportSuccesses) { + var msg = "getError was " + ((glErrors.length > 1) ? "one of: " : "expected value: "); + testPassed(msg + expected + " : " + opt_msg); + } + return err; +}; + +/** + * Tests that a function throws or not. + * @param {!WebGLContext} gl The WebGLContext to use. + * @param throwType Type of thrown error (e.g. TypeError), or false. + * @param {string} info Info on what's being tested + * @param {function} func The func to test. + */ +var shouldThrow = function(gl, throwType, info, func) { + while (gl.getError()) {} + + var shouldThrow = (throwType != false); + + try { + func(); + + if (shouldThrow) { + testFailed("Should throw a " + throwType.name + ": " + info); + } else { + testPassed("Should not have thrown: " + info); + } + } catch (e) { + if (shouldThrow) { + if (e instanceof throwType) { + testPassed("Should throw a " + throwType.name + ": " + info); + } else { + testFailed("Should throw a " + throwType.name + ", threw " + e.name + ": " + info); + } + } else { + testFailed("Should not have thrown: " + info); + } + + if (gl.getError()) { + testFailed("Should not generate an error when throwing: " + info); + } + } + + while (gl.getError()) {} +}; + +/** + * Links a WebGL program, throws if there are errors. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!WebGLProgram} program The WebGLProgram to link. + * @param {function(string): void} opt_errorCallback callback for errors. + */ +var linkProgram = function(gl, program, opt_errorCallback) { + var errFn = opt_errorCallback || testFailed; + // Link the program + gl.linkProgram(program); + + // Check the link status + var linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + // something went wrong with the link + var error = gl.getProgramInfoLog (program); + + errFn("Error in program linking:" + error); + + gl.deleteProgram(program); + } +}; + +/** + * Loads text from an external file. This function is asynchronous. + * @param {string} url The url of the external file. + * @param {!function(bool, string): void} callback that is sent a bool for + * success and the string. + */ +var loadTextFileAsync = function(url, callback) { + log ("loading: " + url); + var error = 'loadTextFileAsync failed to load url "' + url + '"'; + var request; + if (window.XMLHttpRequest) { + request = new XMLHttpRequest(); + if (request.overrideMimeType) { + request.overrideMimeType('text/plain'); + } + } else { + throw 'XMLHttpRequest is disabled'; + } + try { + request.open('GET', url, true); + request.onreadystatechange = function() { + if (request.readyState == 4) { + var text = ''; + // HTTP reports success with a 200 status. The file protocol reports + // success with zero. HTTP does not use zero as a status code (they + // start at 100). + // https://developer.mozilla.org/En/Using_XMLHttpRequest + var success = request.status == 200 || request.status == 0; + if (success) { + text = request.responseText; + log("completed load request: " + url); + } else { + log("loading " + url + " resulted in unexpected status: " + request.status + " " + request.statusText); + } + callback(success, text); + } + }; + request.onerror = function(errorEvent) { + log("error occurred loading " + url); + callback(false, ''); + }; + request.send(null); + } catch (err) { + log("failed to load: " + url + " with exception " + err.message); + callback(false, ''); + } +}; + +/** + * Recursively loads a file as a list. Each line is parsed for a relative + * path. If the file ends in .txt the contents of that file is inserted in + * the list. + * + * @param {string} url The url of the external file. + * @param {!function(bool, Array<string>): void} callback that is sent a bool + * for success and the array of strings. + */ +var getFileListAsync = function(url, callback) { + var files = []; + + var getFileListImpl = function(url, callback) { + var files = []; + if (url.substr(url.length - 4) == '.txt') { + loadTextFileAsync(url, function() { + return function(success, text) { + if (!success) { + callback(false, ''); + return; + } + var lines = text.split('\n'); + var prefix = ''; + var lastSlash = url.lastIndexOf('/'); + if (lastSlash >= 0) { + prefix = url.substr(0, lastSlash + 1); + } + var fail = false; + var count = 1; + var index = 0; + for (var ii = 0; ii < lines.length; ++ii) { + var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + if (str.length > 4 && + str[0] != '#' && + str[0] != ";" && + str.substr(0, 2) != "//") { + var names = str.split(/ +/); + var new_url = prefix + str; + if (names.length == 1) { + new_url = prefix + str; + ++count; + getFileListImpl(new_url, function(index) { + return function(success, new_files) { + log("got files: " + new_files.length); + if (success) { + files[index] = new_files; + } + finish(success); + }; + }(index++)); + } else { + var s = ""; + var p = ""; + for (var jj = 0; jj < names.length; ++jj) { + s += p + prefix + names[jj]; + p = " "; + } + files[index++] = s; + } + } + } + finish(true); + + function finish(success) { + if (!success) { + fail = true; + } + --count; + log("count: " + count); + if (!count) { + callback(!fail, files); + } + } + } + }()); + + } else { + files.push(url); + callback(true, files); + } + }; + + getFileListImpl(url, function(success, files) { + // flatten + var flat = []; + flatten(files); + function flatten(files) { + for (var ii = 0; ii < files.length; ++ii) { + var value = files[ii]; + if (typeof(value) == "string") { + flat.push(value); + } else { + flatten(value); + } + } + } + callback(success, flat); + }); +}; + +/** + * Gets a file from a file/URL. + * @param {string} file the URL of the file to get. + * @return {string} The contents of the file. + */ +var readFile = function(file) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", file, false); + xhr.overrideMimeType("text/plain"); + xhr.send(); + return xhr.responseText.replace(/\r/g, ""); +}; + +var readFileList = function(url) { + var files = []; + if (url.substr(url.length - 4) == '.txt') { + var lines = readFile(url).split('\n'); + var prefix = ''; + var lastSlash = url.lastIndexOf('/'); + if (lastSlash >= 0) { + prefix = url.substr(0, lastSlash + 1); + } + for (var ii = 0; ii < lines.length; ++ii) { + var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + if (str.length > 4 && + str[0] != '#' && + str[0] != ";" && + str.substr(0, 2) != "//") { + var names = str.split(/ +/); + if (names.length == 1) { + var new_url = prefix + str; + files = files.concat(readFileList(new_url)); + } else { + var s = ""; + var p = ""; + for (var jj = 0; jj < names.length; ++jj) { + s += p + prefix + names[jj]; + p = " "; + } + files.push(s); + } + } + } + } else { + files.push(url); + } + return files; +}; + +/** + * Loads a shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} shaderSource The shader source. + * @param {number} shaderType The type of shader. + * @param {function(string): void} opt_errorCallback callback for errors. + * @param {boolean} opt_logShaders Whether to log shader source. + * @param {string} opt_shaderLabel Label that identifies the shader source in + * the log. + * @param {string} opt_url URL from where the shader source was loaded from. + * If opt_logShaders is set, then a link to the source file will also be + * added. + * @param {boolean} Skip compilation status check. Default = false. + * @return {!WebGLShader} The created shader. + */ +var loadShader = function( + gl, shaderSource, shaderType, opt_errorCallback, opt_logShaders, + opt_shaderLabel, opt_url, opt_skipCompileStatus) { + var errFn = opt_errorCallback || error; + // Create the shader object + var shader = gl.createShader(shaderType); + if (shader == null) { + errFn("*** Error: unable to create shader '"+shaderSource+"'"); + return null; + } + + // Load the shader source + gl.shaderSource(shader, shaderSource); + + // Compile the shader + gl.compileShader(shader); + + if (opt_logShaders) { + var label = shaderType == gl.VERTEX_SHADER ? 'vertex shader' : 'fragment_shader'; + if (opt_shaderLabel) { + label = opt_shaderLabel + ' ' + label; + } + addShaderSources( + gl, document.getElementById('console'), label, shader, shaderSource, opt_url); + } + + // Check the compile status + if (!opt_skipCompileStatus) { + var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); + if (!compiled) { + // Something went wrong during compilation; get the error + lastError = gl.getShaderInfoLog(shader); + errFn("*** Error compiling " + glEnumToString(gl, shaderType) + " '" + shader + "':" + lastError); + gl.deleteShader(shader); + return null; + } + } + + return shader; +} + +/** + * Loads a shader from a URL. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {file} file The URL of the shader source. + * @param {number} type The type of shader. + * @param {function(string): void} opt_errorCallback callback for errors. + * @param {boolean} opt_logShaders Whether to log shader source. + * @param {boolean} Skip compilation status check. Default = false. + * @return {!WebGLShader} The created shader. + */ +var loadShaderFromFile = function( + gl, file, type, opt_errorCallback, opt_logShaders, opt_skipCompileStatus) { + var shaderSource = readFile(file); + return loadShader(gl, shaderSource, type, opt_errorCallback, + opt_logShaders, undefined, file, opt_skipCompileStatus); +}; + +var loadShaderFromFileAsync = function( + gl, file, type, opt_errorCallback, opt_logShaders, opt_skipCompileStatus, callback) { + loadTextFileAsync(file, function(gl, type, opt_errorCallback, opt_logShaders, file, opt_skipCompileStatus){ + return function(success, shaderSource) { + if (success) { + var shader = loadShader(gl, shaderSource, type, opt_errorCallback, + opt_logShaders, undefined, file, opt_skipCompileStatus); + callback(true, shader); + } else { + callback(false, null); + } + } + }(gl, type, opt_errorCallback, opt_logShaders, file, opt_skipCompileStatus)); +}; + +/** + * Gets the content of script. + * @param {string} scriptId The id of the script tag. + * @return {string} The content of the script. + */ +var getScript = function(scriptId) { + var shaderScript = document.getElementById(scriptId); + if (!shaderScript) { + throw("*** Error: unknown script element " + scriptId); + } + return shaderScript.text; +}; + +/** + * Loads a shader from a script tag. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} scriptId The id of the script tag. + * @param {number} opt_shaderType The type of shader. If not passed in it will + * be derived from the type of the script tag. + * @param {function(string): void} opt_errorCallback callback for errors. + * @param {boolean} opt_logShaders Whether to log shader source. + * @param {boolean} Skip compilation status check. Default = false. + * @return {!WebGLShader} The created shader. + */ +var loadShaderFromScript = function( + gl, scriptId, opt_shaderType, opt_errorCallback, opt_logShaders, opt_skipCompileStatus) { + var shaderSource = ""; + var shaderScript = document.getElementById(scriptId); + if (!shaderScript) { + throw("*** Error: unknown script element " + scriptId); + } + shaderSource = shaderScript.text.trim(); + + if (!opt_shaderType) { + if (shaderScript.type == "x-shader/x-vertex") { + opt_shaderType = gl.VERTEX_SHADER; + } else if (shaderScript.type == "x-shader/x-fragment") { + opt_shaderType = gl.FRAGMENT_SHADER; + } else { + throw("*** Error: unknown shader type"); + return null; + } + } + + return loadShader(gl, shaderSource, opt_shaderType, opt_errorCallback, + opt_logShaders, undefined, undefined, opt_skipCompileStatus); +}; + +var loadStandardProgram = function(gl) { + var program = gl.createProgram(); + gl.attachShader(program, loadStandardVertexShader(gl)); + gl.attachShader(program, loadStandardFragmentShader(gl)); + gl.bindAttribLocation(program, 0, "a_vertex"); + gl.bindAttribLocation(program, 1, "a_normal"); + linkProgram(gl, program); + return program; +}; + +var loadStandardProgramAsync = function(gl, callback) { + loadStandardVertexShaderAsync(gl, function(gl) { + return function(success, vs) { + if (success) { + loadStandardFragmentShaderAsync(gl, function(vs) { + return function(success, fs) { + if (success) { + var program = gl.createProgram(); + gl.attachShader(program, vs); + gl.attachShader(program, fs); + gl.bindAttribLocation(program, 0, "a_vertex"); + gl.bindAttribLocation(program, 1, "a_normal"); + linkProgram(gl, program); + callback(true, program); + } else { + callback(false, null); + } + }; + }(vs)); + } else { + callback(false, null); + } + }; + }(gl)); +}; + +/** + * Loads shaders from files, creates a program, attaches the shaders and links. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} vertexShaderPath The URL of the vertex shader. + * @param {string} fragmentShaderPath The URL of the fragment shader. + * @param {function(string): void} opt_errorCallback callback for errors. + * @return {!WebGLProgram} The created program. + */ +var loadProgramFromFile = function( + gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) { + var program = gl.createProgram(); + var vs = loadShaderFromFile( + gl, vertexShaderPath, gl.VERTEX_SHADER, opt_errorCallback); + var fs = loadShaderFromFile( + gl, fragmentShaderPath, gl.FRAGMENT_SHADER, opt_errorCallback); + if (vs && fs) { + gl.attachShader(program, vs); + gl.attachShader(program, fs); + linkProgram(gl, program, opt_errorCallback); + } + if (vs) { + gl.deleteShader(vs); + } + if (fs) { + gl.deleteShader(fs); + } + return program; +}; + +/** + * Loads shaders from script tags, creates a program, attaches the shaders and + * links. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} vertexScriptId The id of the script tag that contains the + * vertex shader. + * @param {string} fragmentScriptId The id of the script tag that contains the + * fragment shader. + * @param {function(string): void} opt_errorCallback callback for errors. + * @return {!WebGLProgram} The created program. + */ +var loadProgramFromScript = function loadProgramFromScript( + gl, vertexScriptId, fragmentScriptId, opt_errorCallback) { + var program = gl.createProgram(); + gl.attachShader( + program, + loadShaderFromScript( + gl, vertexScriptId, gl.VERTEX_SHADER, opt_errorCallback)); + gl.attachShader( + program, + loadShaderFromScript( + gl, fragmentScriptId, gl.FRAGMENT_SHADER, opt_errorCallback)); + linkProgram(gl, program, opt_errorCallback); + return program; +}; + +/** + * Loads shaders from source, creates a program, attaches the shaders and + * links. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!WebGLShader} vertexShader The vertex shader. + * @param {!WebGLShader} fragmentShader The fragment shader. + * @param {function(string): void} opt_errorCallback callback for errors. + * @return {!WebGLProgram} The created program. + */ +var createProgram = function(gl, vertexShader, fragmentShader, opt_errorCallback) { + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + linkProgram(gl, program, opt_errorCallback); + return program; +}; + +/** + * Loads shaders from source, creates a program, attaches the shaders and + * links. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} vertexShader The vertex shader source. + * @param {string} fragmentShader The fragment shader source. + * @param {function(string): void} opt_errorCallback callback for errors. + * @param {boolean} opt_logShaders Whether to log shader source. + * @return {!WebGLProgram} The created program. + */ +var loadProgram = function( + gl, vertexShader, fragmentShader, opt_errorCallback, opt_logShaders) { + var program; + var vs = loadShader( + gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback, opt_logShaders); + var fs = loadShader( + gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback, opt_logShaders); + if (vs && fs) { + program = createProgram(gl, vs, fs, opt_errorCallback) + } + if (vs) { + gl.deleteShader(vs); + } + if (fs) { + gl.deleteShader(fs); + } + return program; +}; + +/** + * Loads shaders from source, creates a program, attaches the shaders and + * links but expects error. + * + * GLSL 1.0.17 10.27 effectively says that compileShader can + * always succeed as long as linkProgram fails so we can't + * rely on compileShader failing. This function expects + * one of the shader to fail OR linking to fail. + * + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} vertexShaderScriptId The vertex shader. + * @param {string} fragmentShaderScriptId The fragment shader. + * @return {WebGLProgram} The created program. + */ +var loadProgramFromScriptExpectError = function( + gl, vertexShaderScriptId, fragmentShaderScriptId) { + var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId); + if (!vertexShader) { + return null; + } + var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId); + if (!fragmentShader) { + return null; + } + var linkSuccess = true; + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + linkSuccess = true; + linkProgram(gl, program, function() { + linkSuccess = false; + }); + return linkSuccess ? program : null; +}; + + +var getActiveMap = function(gl, program, typeInfo) { + var numVariables = gl.getProgramParameter(program, gl[typeInfo.param]); + var variables = {}; + for (var ii = 0; ii < numVariables; ++ii) { + var info = gl[typeInfo.activeFn](program, ii); + variables[info.name] = { + name: info.name, + size: info.size, + type: info.type, + location: gl[typeInfo.locFn](program, info.name) + }; + } + return variables; +}; + +/** + * Returns a map of attrib names to info about those + * attribs. + * + * eg: + * { "attrib1Name": + * { + * name: "attrib1Name", + * size: 1, + * type: gl.FLOAT_MAT2, + * location: 0 + * }, + * "attrib2Name[0]": + * { + * name: "attrib2Name[0]", + * size: 4, + * type: gl.FLOAT, + * location: 1 + * }, + * } + * + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {WebGLProgram} The program to query for attribs. + * @return the map. + */ +var getAttribMap = function(gl, program) { + return getActiveMap(gl, program, { + param: "ACTIVE_ATTRIBUTES", + activeFn: "getActiveAttrib", + locFn: "getAttribLocation" + }); +}; + +/** + * Returns a map of uniform names to info about those uniforms. + * + * eg: + * { "uniform1Name": + * { + * name: "uniform1Name", + * size: 1, + * type: gl.FLOAT_MAT2, + * location: WebGLUniformLocation + * }, + * "uniform2Name[0]": + * { + * name: "uniform2Name[0]", + * size: 4, + * type: gl.FLOAT, + * location: WebGLUniformLocation + * }, + * } + * + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {WebGLProgram} The program to query for uniforms. + * @return the map. + */ +var getUniformMap = function(gl, program) { + return getActiveMap(gl, program, { + param: "ACTIVE_UNIFORMS", + activeFn: "getActiveUniform", + locFn: "getUniformLocation" + }); +}; + +var basePath; +var getResourcePath = function() { + if (!basePath) { + var expectedBase = "js/webgl-test-utils.js"; + var scripts = document.getElementsByTagName('script'); + for (var script, i = 0; script = scripts[i]; i++) { + var src = script.src; + var l = src.length; + if (src.substr(l - expectedBase.length) == expectedBase) { + basePath = src.substr(0, l - expectedBase.length); + } + } + } + return basePath + "resources/"; +}; + +var loadStandardVertexShader = function(gl) { + return loadShaderFromFile( + gl, getResourcePath() + "vertexShader.vert", gl.VERTEX_SHADER); +}; +var loadStandardVertexShaderAsync = function(gl, callback) { + loadShaderFromFileAsync(gl, getResourcePath() + "vertexShader.vert", gl.VERTEX_SHADER, undefined, undefined, undefined, callback); +}; + +var loadStandardFragmentShader = function(gl) { + return loadShaderFromFile( + gl, getResourcePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER); +}; +var loadStandardFragmentShaderAsync = function(gl, callback) { + loadShaderFromFileAsync(gl, getResourcePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER, undefined, undefined, undefined, callback); +}; + +var loadUniformBlockProgram = function(gl) { + var program = gl.createProgram(); + gl.attachShader(program, loadUniformBlockVertexShader(gl)); + gl.attachShader(program, loadUniformBlockFragmentShader(gl)); + gl.bindAttribLocation(program, 0, "a_vertex"); + gl.bindAttribLocation(program, 1, "a_normal"); + linkProgram(gl, program); + return program; +}; + +var loadUniformBlockVertexShader = function(gl) { + return loadShaderFromFile( + gl, getResourcePath() + "uniformBlockShader.vert", gl.VERTEX_SHADER); +}; + +var loadUniformBlockFragmentShader = function(gl) { + return loadShaderFromFile( + gl, getResourcePath() + "uniformBlockShader.frag", gl.FRAGMENT_SHADER); +}; + +/** + * Loads an image asynchronously. + * @param {string} url URL of image to load. + * @param {!function(!Element): void} callback Function to call + * with loaded image. + */ +var loadImageAsync = function(url, callback) { + var img = document.createElement('img'); + img.onload = function() { + callback(img); + }; + img.src = url; +}; + +/** + * Loads an array of images. + * @param {!Array.<string>} urls URLs of images to load. + * @param {!function(!{string, img}): void} callback Callback + * that gets passed map of urls to img tags. + */ +var loadImagesAsync = function(urls, callback) { + var count = 1; + var images = { }; + function countDown() { + --count; + if (count == 0) { + log("loadImagesAsync: all images loaded"); + callback(images); + } + } + function imageLoaded(url) { + return function(img) { + images[url] = img; + log("loadImagesAsync: loaded " + url); + countDown(); + } + } + for (var ii = 0; ii < urls.length; ++ii) { + ++count; + loadImageAsync(urls[ii], imageLoaded(urls[ii])); + } + countDown(); +}; + +/** + * Returns a map of key=value values from url. + * @return {!Object.<string, number>} map of keys to values. + */ +var getUrlArguments = function() { + var args = {}; + try { + var s = window.location.href; + var q = s.indexOf("?"); + var e = s.indexOf("#"); + if (e < 0) { + e = s.length; + } + var query = s.substring(q + 1, e); + var pairs = query.split("&"); + for (var ii = 0; ii < pairs.length; ++ii) { + var keyValue = pairs[ii].split("="); + var key = keyValue[0]; + var value = decodeURIComponent(keyValue[1]); + args[key] = value; + } + } catch (e) { + throw "could not parse url"; + } + return args; +}; + +/** + * Makes an image from a src. + * @param {string} src Image source URL. + * @param {function()} onload Callback to call when the image has finised loading. + * @param {function()} onerror Callback to call when an error occurs. + * @return {!Image} The created image. + */ +var makeImage = function(src, onload, onerror) { + var img = document.createElement('img'); + if (onload) { + img.onload = onload; + } + if (onerror) { + img.onerror = onerror; + } else { + img.onerror = function() { + log("WARNING: creating image failed; src: " + this.src); + }; + } + if (src) { + img.src = src; + } + return img; +} + +/** + * Makes an image element from a canvas. + * @param {!HTMLCanvas} canvas Canvas to make image from. + * @param {function()} onload Callback to call when the image has finised loading. + * @param {string} imageFormat Image format to be passed to toDataUrl(). + * @return {!Image} The created image. + */ +var makeImageFromCanvas = function(canvas, onload, imageFormat) { + return makeImage(canvas.toDataURL(imageFormat), onload); +}; + +/** + * Makes a video element from a src. + * @param {string} src Video source URL. + * @param {function()} onerror Callback to call when an error occurs. + * @return {!Video} The created video. + */ +var makeVideo = function(src, onerror) { + var vid = document.createElement('video'); + vid.muted = true; + if (onerror) { + vid.onerror = onerror; + } else { + vid.onerror = function() { + log("WARNING: creating video failed; src: " + this.src); + }; + } + if (src) { + vid.src = src; + } + return vid; +} + +/** + * Inserts an image with a caption into 'element'. + * @param {!HTMLElement} element Element to append image to. + * @param {string} caption caption to associate with image. + * @param {!Image} img image to insert. + */ +var insertImage = function(element, caption, img) { + var div = document.createElement("div"); + var label = document.createElement("div"); + label.appendChild(document.createTextNode(caption)); + div.appendChild(label); + div.appendChild(img); + element.appendChild(div); +}; + +/** + * Inserts a 'label' that when clicked expands to the pre formatted text + * supplied by 'source'. + * @param {!HTMLElement} element element to append label to. + * @param {string} label label for anchor. + * @param {string} source preformatted text to expand to. + * @param {string} opt_url URL of source. If provided a link to the source file + * will also be added. + */ +var addShaderSource = function(element, label, source, opt_url) { + var div = document.createElement("div"); + var s = document.createElement("pre"); + s.className = "shader-source"; + s.style.display = "none"; + var ol = document.createElement("ol"); + //s.appendChild(document.createTextNode(source)); + var lines = source.split("\n"); + for (var ii = 0; ii < lines.length; ++ii) { + var line = lines[ii]; + var li = document.createElement("li"); + li.appendChild(document.createTextNode(line)); + ol.appendChild(li); + } + s.appendChild(ol); + var l = document.createElement("a"); + l.href = "show-shader-source"; + l.appendChild(document.createTextNode(label)); + l.addEventListener('click', function(event) { + if (event.preventDefault) { + event.preventDefault(); + } + s.style.display = (s.style.display == 'none') ? 'block' : 'none'; + return false; + }, false); + div.appendChild(l); + if (opt_url) { + var u = document.createElement("a"); + u.href = opt_url; + div.appendChild(document.createTextNode(" ")); + u.appendChild(document.createTextNode("(" + opt_url + ")")); + div.appendChild(u); + } + div.appendChild(s); + element.appendChild(div); +}; + +/** + * Inserts labels that when clicked expand to show the original source of the + * shader and also translated source of the shader, if that is available. + * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {!HTMLElement} element element to append label to. + * @param {string} label label for anchor. + * @param {WebGLShader} shader Shader to show the sources for. + * @param {string} shaderSource Original shader source. + * @param {string} opt_url URL of source. If provided a link to the source file + * will also be added. + */ +var addShaderSources = function( + gl, element, label, shader, shaderSource, opt_url) { + addShaderSource(element, label, shaderSource, opt_url); + + var debugShaders = gl.getExtension('WEBGL_debug_shaders'); + if (debugShaders && shader) { + var translatedSource = debugShaders.getTranslatedShaderSource(shader); + if (translatedSource != '') { + addShaderSource(element, label + ' translated for driver', translatedSource); + } + } +}; + +/** + * Sends shader information to the server to be dumped into text files + * when tests are run from within the test-runner harness. + * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} url URL of current. + * @param {string} passMsg Test description. + * @param {object} vInfo Object containing vertex shader information. + * @param {object} fInfo Object containing fragment shader information. + */ +var dumpShadersInfo = function(gl, url, passMsg, vInfo, fInfo) { + var shaderInfo = {}; + shaderInfo.url = url; + shaderInfo.testDescription = passMsg; + shaderInfo.vLabel = vInfo.label; + shaderInfo.vShouldCompile = vInfo.shaderSuccess; + shaderInfo.vSource = vInfo.source; + shaderInfo.fLabel = fInfo.label; + shaderInfo.fShouldCompile = fInfo.shaderSuccess; + shaderInfo.fSource = fInfo.source; + shaderInfo.vTranslatedSource = null; + shaderInfo.fTranslatedSource = null; + var debugShaders = gl.getExtension('WEBGL_debug_shaders'); + if (debugShaders) { + if (vInfo.shader) + shaderInfo.vTranslatedSource = debugShaders.getTranslatedShaderSource(vInfo.shader); + if (fInfo.shader) + shaderInfo.fTranslatedSource = debugShaders.getTranslatedShaderSource(fInfo.shader); + } + + var dumpShaderInfoRequest = new XMLHttpRequest(); + dumpShaderInfoRequest.open('POST', "/dumpShaderInfo", true); + dumpShaderInfoRequest.setRequestHeader("Content-Type", "text/plain"); + dumpShaderInfoRequest.send(JSON.stringify(shaderInfo)); +}; + +// Add your prefix here. +var browserPrefixes = [ + "", + "MOZ_", + "OP_", + "WEBKIT_" +]; + +/** + * Given an extension name like WEBGL_compressed_texture_s3tc + * returns the name of the supported version extension, like + * WEBKIT_WEBGL_compressed_teture_s3tc + * @param {string} name Name of extension to look for. + * @return {string} name of extension found or undefined if not + * found. + */ +var getSupportedExtensionWithKnownPrefixes = function(gl, name) { + var supported = gl.getSupportedExtensions(); + for (var ii = 0; ii < browserPrefixes.length; ++ii) { + var prefixedName = browserPrefixes[ii] + name; + if (supported.indexOf(prefixedName) >= 0) { + return prefixedName; + } + } +}; + +/** + * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} name Name of extension to look for. + * @param {boolean} extensionEnabled True if the extension was enabled successfully via gl.getExtension(). + */ +var runExtensionSupportedTest = function(gl, name, extensionEnabled) { + var prefixedName = getSupportedExtensionWithKnownPrefixes(gl, name); + if (prefixedName !== undefined) { + if (extensionEnabled) { + testPassed(name + " listed as supported and getExtension succeeded"); + } else { + testFailed(name + " listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed(name + " not listed as supported but getExtension succeeded"); + } else { + testPassed(name + " not listed as supported and getExtension failed -- this is legal"); + } + } +} + +/** + * Given an extension name like WEBGL_compressed_texture_s3tc + * returns the supported version extension, like + * WEBKIT_WEBGL_compressed_teture_s3tc + * @param {string} name Name of extension to look for. + * @return {WebGLExtension} The extension or undefined if not + * found. + */ +var getExtensionWithKnownPrefixes = function(gl, name) { + for (var ii = 0; ii < browserPrefixes.length; ++ii) { + var prefixedName = browserPrefixes[ii] + name; + var ext = gl.getExtension(prefixedName); + if (ext) { + return ext; + } + } +}; + +/** + * Returns possible prefixed versions of an extension's name. + * @param {string} name Name of extension. May already include a prefix. + * @return {Array.<string>} Variations of the extension name with known + * browser prefixes. + */ +var getExtensionPrefixedNames = function(name) { + var unprefix = function(name) { + for (var ii = 0; ii < browserPrefixes.length; ++ii) { + if (browserPrefixes[ii].length > 0 && + name.substring(0, browserPrefixes[ii].length).toLowerCase() === + browserPrefixes[ii].toLowerCase()) { + return name.substring(browserPrefixes[ii].length); + } + } + return name; + } + + var unprefixed = unprefix(name); + + var variations = []; + for (var ii = 0; ii < browserPrefixes.length; ++ii) { + variations.push(browserPrefixes[ii] + unprefixed); + } + + return variations; +}; + +var replaceRE = /\$\((\w+)\)/g; + +/** + * Replaces strings with property values. + * Given a string like "hello $(first) $(last)" and an object + * like {first:"John", last:"Smith"} will return + * "hello John Smith". + * @param {string} str String to do replacements in. + * @param {...} 1 or more objects containing properties. + */ +var replaceParams = function(str) { + var args = arguments; + return str.replace(replaceRE, function(str, p1, offset, s) { + for (var ii = 1; ii < args.length; ++ii) { + if (args[ii][p1] !== undefined) { + return args[ii][p1]; + } + } + throw "unknown string param '" + p1 + "'"; + }); +}; + +var upperCaseFirstLetter = function(str) { + return str.substring(0, 1).toUpperCase() + str.substring(1); +}; + +/** + * Gets a prefixed property. For example, + * + * var fn = getPrefixedProperty( + * window, + * "requestAnimationFrame"); + * + * Will return either: + * "window.requestAnimationFrame", + * "window.oRequestAnimationFrame", + * "window.msRequestAnimationFrame", + * "window.mozRequestAnimationFrame", + * "window.webKitRequestAnimationFrame", + * undefined + * + * the non-prefixed function is tried first. + */ +var propertyPrefixes = ["", "moz", "ms", "o", "webkit"]; +var getPrefixedProperty = function(obj, propertyName) { + for (var ii = 0; ii < propertyPrefixes.length; ++ii) { + var prefix = propertyPrefixes[ii]; + var name = prefix + propertyName; + log(name); + var property = obj[name]; + if (property) { + return property; + } + if (ii == 0) { + propertyName = upperCaseFirstLetter(propertyName); + } + } + return undefined; +}; + +var _requestAnimFrame; + +/** + * Provides requestAnimationFrame in a cross browser way. + */ +var requestAnimFrame = function(callback) { + if (!_requestAnimFrame) { + _requestAnimFrame = getPrefixedProperty(window, "requestAnimationFrame") || + function(callback, element) { + return window.setTimeout(callback, 1000 / 70); + }; + } + _requestAnimFrame.call(window, callback); +}; + +var _cancelAnimFrame; + +/** + * Provides cancelAnimationFrame in a cross browser way. + */ +var cancelAnimFrame = function(request) { + if (!_cancelAnimFrame) { + _cancelAnimFrame = getPrefixedProperty(window, "cancelAnimationFrame") || + window.clearTimeout; + } + _cancelAnimFrame.call(window, request); +}; + +/** + * Provides requestFullScreen in a cross browser way. + */ +var requestFullScreen = function(element) { + var fn = getPrefixedProperty(element, "requestFullScreen"); + if (fn) { + fn.call(element); + } +}; + +/** + * Provides cancelFullScreen in a cross browser way. + */ +var cancelFullScreen = function() { + var fn = getPrefixedProperty(document, "cancelFullScreen"); + if (fn) { + fn.call(document); + } +}; + +var fullScreenStateName; +(function() { + var fullScreenStateNames = [ + "isFullScreen", + "fullScreen" + ]; + for (var ii = 0; ii < fullScreenStateNames.length; ++ii) { + var propertyName = fullScreenStateNames[ii]; + for (var jj = 0; jj < propertyPrefixes.length; ++jj) { + var prefix = propertyPrefixes[jj]; + if (prefix.length) { + propertyName = upperCaseFirstLetter(propertyName); + fullScreenStateName = prefix + propertyName; + if (document[fullScreenStateName] !== undefined) { + return; + } + } + } + fullScreenStateName = undefined; + } +}()); + +/** + * @return {boolean} True if fullscreen mode is active. + */ +var getFullScreenState = function() { + log("fullscreenstatename:" + fullScreenStateName); + log(document[fullScreenStateName]); + return document[fullScreenStateName]; +}; + +/** + * @param {!HTMLElement} element The element to go fullscreen. + * @param {!function(boolean)} callback A function that will be called + * when entering/exiting fullscreen. It is passed true if + * entering fullscreen, false if exiting. + */ +var onFullScreenChange = function(element, callback) { + propertyPrefixes.forEach(function(prefix) { + var eventName = prefix + "fullscreenchange"; + log("addevent: " + eventName); + document.addEventListener(eventName, function(event) { + log("event: " + eventName); + callback(getFullScreenState()); + }); + }); +}; + +/** + * @param {!string} buttonId The id of the button that will toggle fullscreen + * mode. + * @param {!string} fullscreenId The id of the element to go fullscreen. + * @param {!function(boolean)} callback A function that will be called + * when entering/exiting fullscreen. It is passed true if + * entering fullscreen, false if exiting. + * @return {boolean} True if fullscreen mode is supported. + */ +var setupFullscreen = function(buttonId, fullscreenId, callback) { + if (!fullScreenStateName) { + return false; + } + + var fullscreenElement = document.getElementById(fullscreenId); + onFullScreenChange(fullscreenElement, callback); + + var toggleFullScreen = function(event) { + if (getFullScreenState()) { + cancelFullScreen(fullscreenElement); + } else { + requestFullScreen(fullscreenElement); + } + event.preventDefault(); + return false; + }; + + var buttonElement = document.getElementById(buttonId); + buttonElement.addEventListener('click', toggleFullScreen); + + return true; +}; + +/** + * Waits for the browser to composite the web page. + * @param {function()} callback A function to call after compositing has taken + * place. + */ +var waitForComposite = function(callback) { + var frames = 5; + var countDown = function() { + if (frames == 0) { + // TODO(kbr): unify with js-test-pre.js and enable these with + // verbose logging. + // log("waitForComposite: callback"); + callback(); + } else { + // log("waitForComposite: countdown(" + frames + ")"); + --frames; + requestAnimFrame.call(window, countDown); + } + }; + countDown(); +}; + +var setZeroTimeout = (function() { + // See https://dbaron.org/log/20100309-faster-timeouts + + var timeouts = []; + var messageName = "zero-timeout-message"; + + // Like setTimeout, but only takes a function argument. There's + // no time argument (always zero) and no arguments (you have to + // use a closure). + function setZeroTimeout(fn) { + timeouts.push(fn); + window.postMessage(messageName, "*"); + } + + function handleMessage(event) { + if (event.source == window && event.data == messageName) { + event.stopPropagation(); + if (timeouts.length > 0) { + var fn = timeouts.shift(); + fn(); + } + } + } + + window.addEventListener("message", handleMessage, true); + + return setZeroTimeout; +})(); + +function dispatchPromise(fn) { + return new Promise((fn_resolve, fn_reject) => { + setZeroTimeout(() => { + let val; + if (fn) { + val = fn(); + } + fn_resolve(val); + }); + }); +} + +/** + * Runs an array of functions, yielding to the browser between each step. + * If you want to know when all the steps are finished add a last step. + * @param {!Array.<function(): void>} steps Array of functions. + */ +var runSteps = function(steps) { + if (!steps.length) { + return; + } + + // copy steps so they can't be modifed. + var stepsToRun = steps.slice(); + var currentStep = 0; + var runNextStep = function() { + stepsToRun[currentStep++](); + if (currentStep < stepsToRun.length) { + setTimeout(runNextStep, 1); + } + }; + runNextStep(); +}; + +/** + * Starts playing a video and waits for it to be consumable. + * @param {!HTMLVideoElement} video An HTML5 Video element. + * @param {!function(!HTMLVideoElement): void} callback Function to call when + * video is ready. + */ +async function startPlayingAndWaitForVideo(video, callback) { + if (video.error) { + testFailed('Video failed to load: ' + video.error); + return; + } + + video.loop = true; + video.muted = true; + // See whether setting the preload flag de-flakes video-related tests. + video.preload = 'auto'; + + try { + await video.play(); + } catch (e) { + testFailed('video.play failed: ' + e); + return; + } + + if (video.requestVideoFrameCallback) { + await new Promise(go => video.requestVideoFrameCallback(go)); + } + + callback(video); +} + +var getHost = function(url) { + url = url.replace("\\", "/"); + var pos = url.indexOf("://"); + if (pos >= 0) { + url = url.substr(pos + 3); + } + var parts = url.split('/'); + return parts[0]; +} + +// This function returns the last 2 words of the domain of a URL +// This is probably not the correct check but it will do for now. +var getBaseDomain = function(host) { + var parts = host.split(":"); + var hostname = parts[0]; + var port = parts[1] || "80"; + parts = hostname.split("."); + if(parts.length < 2) + return hostname + ":" + port; + var tld = parts[parts.length-1]; + var domain = parts[parts.length-2]; + return domain + "." + tld + ":" + port; +} + +var runningOnLocalhost = function() { + let hostname = window.location.hostname; + return hostname == "localhost" || + hostname == "127.0.0.1" || + hostname == "::1"; +} + +var getLocalCrossOrigin = function() { + var domain; + if (window.location.host.indexOf("localhost") != -1) { + // TODO(kbr): figure out whether to use an IPv6 loopback address. + domain = "127.0.0.1"; + } else { + domain = "localhost"; + } + + var port = window.location.port || "80"; + return window.location.protocol + "//" + domain + ":" + port +} + +var getRelativePath = function(path) { + var relparts = window.location.pathname.split("/"); + relparts.pop(); // Pop off filename + var pathparts = path.split("/"); + + var i; + for (i = 0; i < pathparts.length; ++i) { + switch (pathparts[i]) { + case "": break; + case ".": break; + case "..": + relparts.pop(); + break; + default: + relparts.push(pathparts[i]); + break; + } + } + + return relparts.join("/"); +} + +async function loadCrossOriginImage(img, webUrl, localUrl) { + if (runningOnLocalhost()) { + img.src = getLocalCrossOrigin() + getRelativePath(localUrl); + console.log('[loadCrossOriginImage]', ' trying', img.src); + await img.decode(); + return; + } + + try { + img.src = getUrlOptions().imgUrl || webUrl; + console.log('[loadCrossOriginImage]', 'trying', img.src); + await img.decode(); + return; + } catch {} + + throw 'createCrossOriginImage failed'; +} + +/** + * Convert sRGB color to linear color. + * @param {!Array.<number>} color The color to be converted. + * The array has 4 elements, for example [R, G, B, A]. + * where each element is in the range 0 to 255. + * @return {!Array.<number>} color The color to be converted. + * The array has 4 elements, for example [R, G, B, A]. + * where each element is in the range 0 to 255. + */ +var sRGBToLinear = function(color) { + return [sRGBChannelToLinear(color[0]), + sRGBChannelToLinear(color[1]), + sRGBChannelToLinear(color[2]), + color[3]] +} + +/** + * Convert linear color to sRGB color. + * @param {!Array.<number>} color The color to be converted. + * The array has 4 elements, for example [R, G, B, A]. + * where each element is in the range 0 to 255. + * @return {!Array.<number>} color The color to be converted. + * The array has 4 elements, for example [R, G, B, A]. + * where each element is in the range 0 to 255. + */ +var linearToSRGB = function(color) { + return [linearChannelToSRGB(color[0]), + linearChannelToSRGB(color[1]), + linearChannelToSRGB(color[2]), + color[3]] +} + +function sRGBChannelToLinear(value) { + value = value / 255; + if (value <= 0.04045) + value = value / 12.92; + else + value = Math.pow((value + 0.055) / 1.055, 2.4); + return Math.trunc(value * 255 + 0.5); +} + +function linearChannelToSRGB(value) { + value = value / 255; + if (value <= 0.0) { + value = 0.0; + } else if (value < 0.0031308) { + value = value * 12.92; + } else if (value < 1) { + value = Math.pow(value, 0.41666) * 1.055 - 0.055; + } else { + value = 1.0; + } + return Math.trunc(value * 255 + 0.5); +} + +/** + * Return the named color in the specified color space. + * @param {string} colorName The name of the color to convert. + * Supported color names are: + * 'Red', which is the CSS color color('srgb' 1 0 0 1) + * 'Green', which is the CSS color color('srgb' 0 1 0 1) + * @param {string} colorSpace The color space to convert to. Supported + color spaces are: + * null, which is treated as sRGB + * 'srgb' + * 'display-p3'. + * Documentation on the formulas for color conversion between + * spaces can be found at + https://www.w3.org/TR/css-color-4/#predefined-to-predefined + * @return {!Array.<number>} color The color in the specified color + * space as an 8-bit RGBA array with unpremultiplied alpha. + */ +var namedColorInColorSpace = function(colorName, colorSpace) { + var result; + switch (colorSpace) { + case undefined: + case 'srgb': + switch(colorName) { + case 'Red': + return [255, 0, 0, 255]; + case 'Green': + return [0, 255, 0, 255]; + break; + default: + throw 'unexpected color name: ' + colorName; + }; + break; + case 'display-p3': + switch(colorName) { + case 'Red': + return [234, 51, 35, 255]; + break; + case 'Green': + return [117, 251, 76, 255]; + break; + default: + throw 'unexpected color name: ' + colorName; + } + break; + default: + throw 'unexpected color space: ' + colorSpace; + } +} + +/** + * Return the named color as it would be sampled with the specified + * internal format + * @param {!Array.<number>} color The color as an 8-bit RGBA array. + * @param {string} internalformat The internal format. + * @return {!Array.<number>} color The color, as it would be sampled by + * the specified internal format, as an 8-bit RGBA array. + */ +var colorAsSampledWithInternalFormat = function(color, internalFormat) { + switch (internalFormat) { + case 'ALPHA': + return [0, 0, 0, color[3]]; + case 'LUMINANCE': + return [color[0], color[0], color[0], 255]; + case 'LUMINANCE_ALPHA': + return [color[0], color[0], color[0], color[3]]; + case 'SRGB8': + case 'SRGB8_ALPHA8': + return [sRGBChannelToLinear(color[0]), + sRGBChannelToLinear(color[1]), + sRGBChannelToLinear(color[2]), + color[3]]; + case 'R16F': + case 'R32F': + case 'R8': + case 'R8UI': + case 'RED': + case 'RED_INTEGER': + return [color[0], 0, 0, 0]; + case 'RG': + case 'RG16F': + case 'RG32F': + case 'RG8': + case 'RG8UI': + case 'RG_INTEGER': + return [color[0], color[1], 0, 0]; + break; + default: + break; + } + return color; +} + +function comparePixels(cmp, ref, tolerance, diff) { + if (cmp.length != ref.length) { + testFailed("invalid pixel size."); + } + + var count = 0; + for (var i = 0; i < cmp.length; i++) { + if (diff) { + diff[i * 4] = 0; + diff[i * 4 + 1] = 255; + diff[i * 4 + 2] = 0; + diff[i * 4 + 3] = 255; + } + if (Math.abs(cmp[i * 4] - ref[i * 4]) > tolerance || + Math.abs(cmp[i * 4 + 1] - ref[i * 4 + 1]) > tolerance || + Math.abs(cmp[i * 4 + 2] - ref[i * 4 + 2]) > tolerance || + Math.abs(cmp[i * 4 + 3] - ref[i * 4 + 3]) > tolerance) { + if (count < 10) { + testFailed("Pixel " + i + ": expected (" + + [ref[i * 4], ref[i * 4 + 1], ref[i * 4 + 2], ref[i * 4 + 3]] + "), got (" + + [cmp[i * 4], cmp[i * 4 + 1], cmp[i * 4 + 2], cmp[i * 4 + 3]] + ")"); + } + count++; + if (diff) { + diff[i * 4] = 255; + diff[i * 4 + 1] = 0; + } + } + } + + return count; +} + +function destroyContext(gl) { + const ext = gl.getExtension('WEBGL_lose_context'); + if (ext) { + ext.loseContext(); + } + gl.canvas.width = 1; + gl.canvas.height = 1; +} + +function destroyAllContexts() { + if (!window._wtu_contexts) + return; + for (const x of window._wtu_contexts) { + destroyContext(x); + } + window._wtu_contexts = []; +} + +function displayImageDiff(cmp, ref, diff, width, height) { + var div = document.createElement("div"); + + var cmpImg = createImageFromPixel(cmp, width, height); + var refImg = createImageFromPixel(ref, width, height); + var diffImg = createImageFromPixel(diff, width, height); + wtu.insertImage(div, "Reference", refImg); + wtu.insertImage(div, "Result", cmpImg); + wtu.insertImage(div, "Difference", diffImg); + + var console = document.getElementById("console"); + console.appendChild(div); +} + +function createImageFromPixel(buf, width, height) { + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + var ctx = canvas.getContext("2d"); + var imgData = ctx.getImageData(0, 0, width, height); + + for (var i = 0; i < buf.length; i++) + imgData.data[i] = buf[i]; + ctx.putImageData(imgData, 0, 0); + var img = wtu.makeImageFromCanvas(canvas); + return img; +} + +async function awaitTimeout(ms) { + await new Promise(res => { + setTimeout(() => { + res(); + }, ms); + }); +} + +async function awaitOrTimeout(promise, opt_timeout_ms) { + async function throwOnTimeout(ms) { + await awaitTimeout(ms); + throw 'timeout'; + } + + let timeout_ms = opt_timeout_ms; + if (timeout_ms === undefined) + timeout_ms = 5000; + + await Promise.race([promise, throwOnTimeout(timeout_ms)]); +} + +var API = { + addShaderSource: addShaderSource, + addShaderSources: addShaderSources, + cancelAnimFrame: cancelAnimFrame, + create3DContext: create3DContext, + GLErrorException: GLErrorException, + create3DContextWithWrapperThatThrowsOnGLError: create3DContextWithWrapperThatThrowsOnGLError, + checkAreaInAndOut: checkAreaInAndOut, + checkCanvas: checkCanvas, + checkCanvasRect: checkCanvasRect, + checkCanvasRectColor: checkCanvasRectColor, + checkCanvasRects: checkCanvasRects, + checkFloatBuffer: checkFloatBuffer, + checkTextureSize: checkTextureSize, + clipToRange: clipToRange, + createColoredTexture: createColoredTexture, + createProgram: createProgram, + clearAndDrawUnitQuad: clearAndDrawUnitQuad, + clearAndDrawIndexedQuad: clearAndDrawIndexedQuad, + comparePixels: comparePixels, + destroyAllContexts: destroyAllContexts, + destroyContext: destroyContext, + dispatchPromise: dispatchPromise, + displayImageDiff: displayImageDiff, + drawUnitQuad: drawUnitQuad, + drawIndexedQuad: drawIndexedQuad, + drawUByteColorQuad: drawUByteColorQuad, + drawFloatColorQuad: drawFloatColorQuad, + dummySetProgramAndDrawNothing: dummySetProgramAndDrawNothing, + dumpShadersInfo: dumpShadersInfo, + endsWith: endsWith, + failIfGLError: failIfGLError, + fillTexture: fillTexture, + framebufferStatusShouldBe: framebufferStatusShouldBe, + getBytesPerComponent: getBytesPerComponent, + getDefault3DContextVersion: getDefault3DContextVersion, + getExtensionPrefixedNames: getExtensionPrefixedNames, + getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes, + getFileListAsync: getFileListAsync, + getLastError: getLastError, + getPrefixedProperty: getPrefixedProperty, + getScript: getScript, + getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes, + getTypedArrayElementsPerPixel: getTypedArrayElementsPerPixel, + getUrlArguments: getUrlArguments, + getUrlOptions: getUrlOptions, + getAttribMap: getAttribMap, + getUniformMap: getUniformMap, + glEnumToString: glEnumToString, + glErrorAssert: glErrorAssert, + glErrorShouldBe: glErrorShouldBe, + glTypeToTypedArrayType: glTypeToTypedArrayType, + hasAttributeCaseInsensitive: hasAttributeCaseInsensitive, + insertImage: insertImage, + isWebGL2: isWebGL2, + linkProgram: linkProgram, + loadCrossOriginImage: loadCrossOriginImage, + loadImageAsync: loadImageAsync, + loadImagesAsync: loadImagesAsync, + loadProgram: loadProgram, + loadProgramFromFile: loadProgramFromFile, + loadProgramFromScript: loadProgramFromScript, + loadProgramFromScriptExpectError: loadProgramFromScriptExpectError, + loadShader: loadShader, + loadShaderFromFile: loadShaderFromFile, + loadShaderFromScript: loadShaderFromScript, + loadStandardProgram: loadStandardProgram, + loadStandardProgramAsync: loadStandardProgramAsync, + loadStandardVertexShader: loadStandardVertexShader, + loadStandardVertexShaderAsync: loadStandardVertexShaderAsync, + loadStandardFragmentShader: loadStandardFragmentShader, + loadStandardFragmentShaderAsync: loadStandardFragmentShaderAsync, + loadUniformBlockProgram: loadUniformBlockProgram, + loadUniformBlockVertexShader: loadUniformBlockVertexShader, + loadUniformBlockFragmentShader: loadUniformBlockFragmentShader, + loadTextFileAsync: loadTextFileAsync, + loadTexture: loadTexture, + log: log, + loggingOff: loggingOff, + makeCheckRect: makeCheckRect, + makeImage: makeImage, + makeImageFromCanvas: makeImageFromCanvas, + makeVideo: makeVideo, + error: error, + runExtensionSupportedTest: runExtensionSupportedTest, + shallowCopyObject: shallowCopyObject, + setDefault3DContextVersion: setDefault3DContextVersion, + setupColorQuad: setupColorQuad, + setupProgram: setupProgram, + setupTransformFeedbackProgram: setupTransformFeedbackProgram, + setupQuad: setupQuad, + setupQuadWithTexCoords: setupQuadWithTexCoords, + setupIndexedQuad: setupIndexedQuad, + setupIndexedQuadWithOptions: setupIndexedQuadWithOptions, + setupSimpleColorProgram: setupSimpleColorProgram, + setupSimpleTextureProgram: setupSimpleTextureProgram, + setupSimpleTextureProgramESSL300: setupSimpleTextureProgramESSL300, + setupSimpleCubeMapTextureProgram: setupSimpleCubeMapTextureProgram, + setupSimpleVertexColorProgram: setupSimpleVertexColorProgram, + setupNoTexCoordTextureProgram: setupNoTexCoordTextureProgram, + setupTexturedQuad: setupTexturedQuad, + setupTexturedQuadWithTexCoords: setupTexturedQuadWithTexCoords, + setupTexturedQuadWithCubeMap: setupTexturedQuadWithCubeMap, + setupUnitQuad: setupUnitQuad, + setFloatDrawColor: setFloatDrawColor, + setUByteDrawColor: setUByteDrawColor, + startPlayingAndWaitForVideo: startPlayingAndWaitForVideo, + startsWith: startsWith, + shouldGenerateGLError: shouldGenerateGLError, + shouldThrow: shouldThrow, + readFile: readFile, + readFileList: readFileList, + replaceParams: replaceParams, + requestAnimFrame: requestAnimFrame, + runSteps: runSteps, + waitForComposite: waitForComposite, + + // fullscreen api + setupFullscreen: setupFullscreen, + + // color converter API + namedColorInColorSpace: namedColorInColorSpace, + colorAsSampledWithInternalFormat: colorAsSampledWithInternalFormat, + + // sRGB converter api + sRGBToLinear: sRGBToLinear, + linearToSRGB: linearToSRGB, + + getHost: getHost, + getBaseDomain: getBaseDomain, + runningOnLocalhost: runningOnLocalhost, + getLocalCrossOrigin: getLocalCrossOrigin, + getRelativePath: getRelativePath, + awaitOrTimeout: awaitOrTimeout, + awaitTimeout: awaitTimeout, + + none: false +}; + +Object.defineProperties(API, { + noTexCoordTextureVertexShader: { value: noTexCoordTextureVertexShader, writable: false }, + simpleTextureVertexShader: { value: simpleTextureVertexShader, writable: false }, + simpleTextureVertexShaderESSL300: { value: simpleTextureVertexShaderESSL300, writable: false }, + simpleColorFragmentShader: { value: simpleColorFragmentShader, writable: false }, + simpleColorFragmentShaderESSL300: { value: simpleColorFragmentShaderESSL300, writable: false }, + simpleVertexShader: { value: simpleVertexShader, writable: false }, + simpleVertexShaderESSL300: { value: simpleVertexShaderESSL300, writable: false }, + simpleTextureFragmentShader: { value: simpleTextureFragmentShader, writable: false }, + simpleTextureFragmentShaderESSL300: { value: simpleTextureFragmentShaderESSL300, writable: false }, + simpleHighPrecisionTextureFragmentShader: { value: simpleHighPrecisionTextureFragmentShader, writable: false }, + simpleCubeMapTextureFragmentShader: { value: simpleCubeMapTextureFragmentShader, writable: false }, + simpleVertexColorFragmentShader: { value: simpleVertexColorFragmentShader, writable: false }, + simpleVertexColorVertexShader: { value: simpleVertexColorVertexShader, writable: false } +}); + +return API; + +}()); |