<!--Copyright (c) 2019 The Khronos Group Inc.Use of this source code is governed by an MIT-style license that can befound in the LICENSE.txt file.--><!DOCTYPE html><html><head><metacharset="utf-8"><title>WebGL 2 ReadPixels Test.</title><linkrel="stylesheet"href="../../resources/js-test-style.css"/><scriptsrc="../../js/js-test-pre.js"></script><scriptsrc="../../js/webgl-test-utils.js"></script></head><body><divid="description"></div><divid="console"></div><script>"use strict";description("Checks that ReadPixels from a fbo works as expected.");varwtu=WebGLTestUtils;vargl=wtu.create3DContext(undefined,undefined,2);gl.pixelStorei(gl.PACK_ALIGNMENT,1);functiongetChannelCount(format){switch(format){casegl.RED:casegl.RED_INTEGER:casegl.ALPHA:casegl.LUMINANCE:return1;casegl.RB:casegl.RB_INTEGER:casegl.LUMINANCE_ALPHA:return2;casegl.RGB:casegl.RGB_INTEGER:return3;casegl.RGBA:casegl.RGBA_INTEGER:return4;default:return0;}}functiongetUnpackInfo(type){switch(type){casegl.UNSIGNED_SHORT_5_6_5:return{bitsCount:[5,6,5],isReverse:false};casegl.UNSIGNED_SHORT_4_4_4_4:return{bitsCount:[4,4,4,4],isReverse:false};casegl.UNSIGNED_SHORT_5_5_5_1:return{bitsCount:[5,5,5,1],isReverse:false};casegl.UNSIGNED_INT_2_10_10_10_REV:return{bitsCount:[2,10,10,10],isReverse:true};casegl.UNSIGNED_INT_10F_11F_11F_REV:return{bitsCount:[10,11,11],isReverse:true};casegl.UNSIGNED_INT_5_9_9_9_REV:return{bitsCount:[5,9,9,9],isReverse:true};default:returnnull;}}// bitsCount is an array contains bit count for each component.functionunpack(value,channelCount,bitsCount,isReverse){varresult=newArray(channelCount);varaccumBitsCount=0;for(vari=channelCount-1;i>=0;--i){varcurrentChannel=isReverse?(channelCount-i-1):i;varmask=0xFFFFFFFF>>>(32-bitsCount[i]);result[currentChannel]=((value>>accumBitsCount)&mask);accumBitsCount+=bitsCount[i];}returnresult;}functiongetColor(buf,index,readFormat,readType){varchannelCount=getChannelCount(readFormat);varresult=newArray(channelCount);varunpackInfo=getUnpackInfo(readType);if(unpackInfo){result=unpack(buf[index],channelCount,unpackInfo.bitsCount,unpackInfo.isReverse);}else{for(vari=0;i<channelCount;++i){result[i]=buf[index+i];}}returnresult;}functionconvertToSRGB(val){if(val<=0){return0;}elseif(val<0.0031308){return12.92*val;}elseif(val<1){return1.055*Math.pow(val,0.41666)-0.055;}else{return1;}}functiondenormalizeColor(srcInternalFormat,destType,color){varresult=color.slice();vartol=0;varsrcIsNormalized=false;switch(srcInternalFormat){casegl.R8:casegl.RG8:casegl.RGB8:casegl.RGBA8:casegl.RGB5_A1:casegl.SRGB8_ALPHA8:casegl.RGB10_A2:srcIsNormalized=true;tol=6;break;casegl.RGB565:// RGB565 needs slightly extra tolerance, at least on Google Pixel. crbug.com/682753srcIsNormalized=true;tol=7;break;casegl.RGBA4:srcIsNormalized=true;tol=10;break;}if(!srcIsNormalized){return{color:result,tol:tol};}if(srcInternalFormat==gl.SRGB8_ALPHA8){for(vari=0;i<3;++i){result[i]=convertToSRGB(result[i]);}}switch(destType){casegl.UNSIGNED_BYTE:result=result.map(val=>{returnval*0xFF});break;casegl.UNSIGNED_SHORT:// On Linux NVIDIA, tol of 33 is necessary to pass the test.tol=40;result=result.map(val=>{returnval*0xFFFF});break;casegl.UNSIGNED_INT:tol=40;result=result.map(val=>{returnval*0xFFFFFFFF});break;casegl.UNSIGNED_SHORT_4_4_4_4:result=result.map(val=>{returnval*0xF});break;casegl.UNSIGNED_SHORT_5_5_5_1:result[0]=result[0]*0x1F;result[1]=result[1]*0x1F;result[2]=result[2]*0x1F;result[3]=result[3]*0x1;break;casegl.UNSIGNED_SHORT_5_6_5:result[0]=result[0]*0x1F;result[1]=result[1]*0x3F;result[2]=result[2]*0x1F;break;casegl.UNSIGNED_INT_2_10_10_10_REV:tol=25;result[0]=result[0]*0x3FF;result[1]=result[1]*0x3FF;result[2]=result[2]*0x3FF;result[3]=result[3]*0x3;break;casegl.UNSIGNED_INT_5_9_9_9_REV:result[0]=result[0]*0x1FF;result[1]=result[1]*0x1FF;result[2]=result[2]*0x1FF;result[3]=result[3]*0x1F;break;}return{color:result,tol:tol};}functioncompareColor(buf,index,expectedColor,srcInternalFormat,srcFormat,srcType,readFormat,readType){varsrcChannelCount=getChannelCount(srcFormat);varreadChannelCount=getChannelCount(readFormat);varcolor=getColor(buf,index,readFormat,readType);expectedColor=denormalizeColor(srcInternalFormat,readType,expectedColor);varminChannel=Math.min(srcChannelCount,readChannelCount);for(vari=0;i<minChannel;++i){if(Math.abs(expectedColor.color[i]-color[i])>expectedColor.tol){testFailed("Expected color = "+expectedColor.color+", was = "+color);returnfalse;}}returntrue;}vartextureTestCases=[{texInternalFormat:'R8',texFormat:'RED',texType:'UNSIGNED_BYTE',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0.0,0.0,0],},{texInternalFormat:'R8UI',texFormat:'RED_INTEGER',texType:'UNSIGNED_BYTE',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[250,0,0,0],},{texInternalFormat:'R8I',texFormat:'RED_INTEGER',texType:'BYTE',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[-126,0,0,0],},{texInternalFormat:'R16UI',texFormat:'RED_INTEGER',texType:'UNSIGNED_SHORT',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[30001,0,0,0],},{texInternalFormat:'R16I',texFormat:'RED_INTEGER',texType:'SHORT',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[-14189,0,0,0],},{texInternalFormat:'R32UI',texFormat:'RED_INTEGER',texType:'UNSIGNED_INT',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[126726,0,0,0],},{texInternalFormat:'R32I',texFormat:'RED_INTEGER',texType:'INT',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[-126726,0,0,0],},{texInternalFormat:'RG8',texFormat:'RG',texType:'UNSIGNED_BYTE',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0.7,0.0,0],},{texInternalFormat:'RG8UI',texFormat:'RG_INTEGER',texType:'UNSIGNED_BYTE',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[250,124,0,0],},{texInternalFormat:'RG8I',texFormat:'RG_INTEGER',texType:'BYTE',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[-55,124,0,0],},{texInternalFormat:'RG16UI',texFormat:'RG_INTEGER',texType:'UNSIGNED_SHORT',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[30001,18,0,0],},{texInternalFormat:'RG16I',texFormat:'RG_INTEGER',texType:'SHORT',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[-14189,6735,0,0],},{texInternalFormat:'RG32UI',texFormat:'RG_INTEGER',texType:'UNSIGNED_INT',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[126726,1976,0,0],},{texInternalFormat:'RG32I',texFormat:'RG_INTEGER',texType:'INT',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[-126726,126726,0,0],},{texInternalFormat:'RGB8',texFormat:'RGB',texType:'UNSIGNED_BYTE',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,1,0,0],},{texInternalFormat:'RGB565',texFormat:'RGB',texType:'UNSIGNED_BYTE',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0.7,0.2,0],},{texInternalFormat:'RGB565',texFormat:'RGB',texType:'UNSIGNED_SHORT_5_6_5',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0.7,0.2,0],},{texInternalFormat:'RGBA8',texFormat:'RGBA',texType:'UNSIGNED_BYTE',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0,1,0.7],},{texInternalFormat:'SRGB8_ALPHA8',texFormat:'RGBA',texType:'UNSIGNED_BYTE',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0,1,0.7],},{texInternalFormat:'RGB5_A1',texFormat:'RGBA',texType:'UNSIGNED_BYTE',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0,0.7,1],},{texInternalFormat:'RGB5_A1',texFormat:'RGBA',texType:'UNSIGNED_SHORT_5_5_5_1',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0.7,1,0],},{texInternalFormat:'RGB5_A1',texFormat:'RGBA',texType:'UNSIGNED_INT_2_10_10_10_REV',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0.7,0,1],},{texInternalFormat:'RGBA4',texFormat:'RGBA',texType:'UNSIGNED_BYTE',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.5,0.7,1,0],},{texInternalFormat:'RGBA4',texFormat:'RGBA',texType:'UNSIGNED_SHORT_4_4_4_4',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[1,0,0.5,0.7],},{texInternalFormat:'RGBA8UI',texFormat:'RGBA_INTEGER',texType:'UNSIGNED_BYTE',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[127,0,255,178],},{texInternalFormat:'RGBA8I',texFormat:'RGBA_INTEGER',texType:'BYTE',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[-55,56,80,127],},{texInternalFormat:'RGB10_A2UI',texFormat:'RGBA_INTEGER',texType:'UNSIGNED_INT_2_10_10_10_REV',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[178,0,127,3],},{texInternalFormat:'RGBA16UI',texFormat:'RGBA_INTEGER',texType:'UNSIGNED_SHORT',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[14189,6735,0,19],},{texInternalFormat:'RGBA16I',texFormat:'RGBA_INTEGER',texType:'SHORT',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[14189,-6735,0,19],},{texInternalFormat:'RGBA32UI',texFormat:'RGBA_INTEGER',texType:'UNSIGNED_INT',readFormat:'RGBA_INTEGER',readType:'UNSIGNED_INT',clearColor:[126726,6726,98765,2015],},{texInternalFormat:'RGBA32I',texFormat:'RGBA_INTEGER',texType:'INT',readFormat:'RGBA_INTEGER',readType:'INT',clearColor:[126726,-6726,-98765,2015],},{texInternalFormat:'RGB10_A2',texFormat:'RGBA',texType:'UNSIGNED_INT_2_10_10_10_REV',readFormat:'RGBA',readType:'UNSIGNED_BYTE',clearColor:[0.7,0,0.5,1],},// TODO(zmo): add float/half_float test cases with extension supports.];functiongetArrayTypeFromReadPixelsType(gl,type){switch(type){casegl.UNSIGNED_BYTE:returnUint8Array;casegl.BYTE:returnInt8Array;casegl.UNSIGNED_SHORT:casegl.UNSIGNED_SHORT_5_6_5:casegl.UNSIGNED_SHORT_4_4_4_4:casegl.UNSIGNED_SHORT_5_5_5_1:returnUint16Array;casegl.SHORT:returnInt16Array;casegl.UNSIGNED_INT:casegl.UNSIGNED_INT_2_10_10_10_REV:casegl.UNSIGNED_INT_10F_11F_11F_REV:casegl.UNSIGNED_INT_5_9_9_9_REV:returnUint32Array;casegl.INT:returnInt32Array;casegl.HALF_FLOAT:returnUint16Array;casegl.FLOAT:returnFloat32Array;default:returnnull;}}functiongetFormatString(gl,format){switch(format){casegl.RED:return'RED';casegl.RED_INTEGER:return'RED_INTEGER';casegl.RG:return'RG';casegl.RG_INTEGER:return'RG_INTEGER';casegl.RGB:return'RGB';casegl.RGB_INTEGER:return'RGB_INTEGER';casegl.RGBA:return'RGBA';casegl.RGBA_INTEGER:return'RGBA_INTEGER';casegl.LUMINANCE:return'LUMINANCE';casegl.LUMINANCE_ALPHA:return'LUMINANCE_ALPHA';casegl.ALPHA:return'ALPHA';default:return'';};}functiongetTypeString(gl,type){switch(type){casegl.UNSIGNED_BYTE:return'UNSIGNED_BYTE';casegl.BYTE:return'BYTE';casegl.UNSIGNED_SHORT:return'UNSIGNED_SHORT';casegl.SHORT:return'SHORT';casegl.UNSIGNED_INT:return'UNSIGNED_INT';casegl.INT:return'INT';casegl.UNSIGNED_SHORT_5_6_5:return'UNSIGNED_SHORT_5_6_5';casegl.UNSIGNED_SHORT_5_5_5_1:return'UNSIGNED_SHORT_5_5_5_1';casegl.UNSIGNED_INT_2_10_10_10_REV:return'UNSIGNED_INT_2_10_10_10_REV';casegl.UNSIGNED_SHORT_4_4_4_4:return'UNSIGNED_SHORT_4_4_4_4';casegl.UNSIGNED_INT_10F_11F_11F_REV:return'UNSIGNED_INT_10F_11F_11F_REV';casegl.UNSIGNED_INT_5_9_9_9_REV:return'UNSIGNED_INT_5_9_9_9_REV';default:return'';};}functionelementCountPerPixel(gl,readFormat,readType){switch(readFormat){casegl.RED:casegl.RED_INTEGER:casegl.ALPHA:casegl.LUMINANCE:return1;casegl.RG:casegl.RG_INTEGER:casegl.LUMINANCE_ALPHA:return2;casegl.RGB:casegl.RGB_INTEGER:switch(readType){casegl.UNSIGNED_SHORT_5_6_5:return1;default:return3;}casegl.RGBA:casegl.RGBA_INTEGER:switch(readType){casegl.UNSIGNED_SHORT_4_4_4_4:casegl.UNSIGNED_SHORT_5_5_5_1:casegl.UNSIGNED_INT_2_10_10_10_REV:casegl.UNSIGNED_INT_10F_11F_11F_REV:casegl.UNSIGNED_INT_5_9_9_9_REV:return1;default:return4;}default:testFailed("Unexpected read format/type = "+readFormat+"/"+readType);return0;}}functiontestReadPixels(gl,srcInternalFormat,srcFormat,srcType,readFormat,readType,expectedColor){vararrayType=getArrayTypeFromReadPixelsType(gl,readType);varbuf=newarrayType(width*height*4);gl.readPixels(0,0,width,height,readFormat,readType,buf);wtu.glErrorShouldBe(gl,gl.NO_ERROR,"readPixels should generate no error");vardiffFound=false;for(varii=0;ii<width*height;++ii){varoffset=ii*elementCountPerPixel(gl,readFormat,readType);if(!compareColor(buf,offset,expectedColor,srcInternalFormat,srcFormat,srcType,readFormat,readType)){diffFound=true;break;}}if(!diffFound){testPassed("Color read back as expected");}}functionclearBuffer(gl,texInternalFormat,clearColor){varvalue;switch(texInternalFormat){casegl.R8UI:casegl.R16UI:casegl.R32UI:casegl.RG8UI:casegl.RG16UI:casegl.RG32UI:casegl.RGBA8UI:casegl.RGBA16UI:casegl.RGBA32UI:casegl.RGB10_A2UI:value=newUint32Array(4);for(varii=0;ii<4;++ii)value[ii]=clearColor[ii];gl.clearBufferuiv(gl.COLOR,0,value);break;casegl.R8I:casegl.R16I:casegl.R32I:casegl.RG8I:casegl.RG16I:casegl.RG32I:casegl.RGBA8I:casegl.RGBA16I:casegl.RGBA32I:value=newInt32Array(4);for(varii=0;ii<4;++ii)value[ii]=clearColor[ii];gl.clearBufferiv(gl.COLOR,0,value);break;default:gl.clearColor(clearColor[0],clearColor[1],clearColor[2],clearColor[3]);gl.clear(gl.COLOR_BUFFER_BIT);break;}}for(vartt=0;tt<textureTestCases.length;++tt){vartest=textureTestCases[tt];debug("");debug("ReadPixels from fbo with texture = ("+test.texInternalFormat+", "+test.texFormat+", "+test.texType+"), format = "+test.readFormat+", type = "+test.readType);varwidth=2;varheight=2;varfbo=gl.createFramebuffer();gl.bindFramebuffer(gl.FRAMEBUFFER,fbo);varcolorImage=gl.createTexture();gl.bindTexture(gl.TEXTURE_2D,colorImage);gl.texImage2D(gl.TEXTURE_2D,0,gl[test.texInternalFormat],width,height,0,gl[test.texFormat],gl[test.texType],null);gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,colorImage,0);wtu.glErrorShouldBe(gl,gl.NO_ERROR,"Setting up fbo should generate no error");if(gl.checkFramebufferStatus(gl.FRAMEBUFFER)!=gl.FRAMEBUFFER_COMPLETE){debug("fbo is not complete, skip");continue;}clearBuffer(gl,gl[test.texInternalFormat],test.clearColor);wtu.glErrorShouldBe(gl,gl.NO_ERROR,"Clear color should generate no error");varimplFormat=gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);varimplType=gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);varimplFormatString=getFormatString(gl,implFormat);varimplTypeString=getTypeString(gl,implType);if(gl[test.texInternalFormat]==gl.RGB10_A2){// This is a special case where three read format/type are supported.varreadTypes=[gl.UNSIGNED_BYTE,gl.UNSIGNED_INT_2_10_10_10_REV];varreadTypeStrings=['UNSIGNED_BYTE','UNSIGNED_INT_2_10_10_10_REV'];if(implFormat==gl.RGBA&&implTypeString!=''){readTypes.push(implType);readTypeStrings.push(implTypeString);}else{testFailed("Unimplemented Implementation dependent color read format/type = "+implFormat+"/"+implType);}for(varrr=0;rr<readTypes.length;++rr){debug("Special case RGB10_A2, format = RGBA, type = "+readTypeStrings[rr]);testReadPixels(gl,gl[test.texInternalFormat],gl[test.texFormat],gl[test.texType],gl.RGBA,readTypes[rr],test.clearColor);}}else{testReadPixels(gl,gl[test.texInternalFormat],gl[test.texFormat],gl[test.texType],gl[test.readFormat],gl[test.readType],test.clearColor);debug("Testing implementation dependent color read format = "+implFormatString+", type = "+implTypeString);if(implFormatString==''){testFailed("Invalid IMPLEMENTATION_COLOR_READ_FORMAT = "+implFormat);continue;}if(implTypeString==''){testFailed("Invalid IMPLEMENTATION_COLOR_READ_TYPE = "+implType);continue;}testReadPixels(gl,gl[test.texInternalFormat],gl[test.texFormat],gl[test.texType],implFormat,implType,test.clearColor);gl.deleteTexture(colorImage);gl.deleteFramebuffer(fbo);}}debug("")varsuccessfullyParsed=true;</script><scriptsrc="../../js/js-test-post.js"></script></body></html>