| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"/> |
| <link rel="stylesheet" href="../../resources/js-test-style.css"/> |
| <script src="../../js/js-test-pre.js"></script> |
| <script src="../../js/webgl-test-utils.js"></script> |
| </head> |
| <body> |
| <div id="description"></div> |
| <div id="console"></div> |
| <canvas id="canvas" width="16" height="16" style="width: 50px; height: 50px; border: 1px solid black;"></canvas> |
| |
| <!-- Shaders to test output --> |
| <script id="vertexShader" type="x-shader/x-vertex"> |
| attribute vec4 aPosition; |
| void main() { |
| gl_Position = aPosition; |
| } |
| </script> |
| |
| <script id="fragmentShader" type="x-shader/x-fragment"> |
| precision mediump float; |
| uniform float uColor; |
| void main() { |
| gl_FragColor = vec4(uColor, uColor, uColor, 1); |
| } |
| </script> |
| |
| <script id="vshader" type="x-shader/x-vertex"> |
| attribute vec4 vPosition; |
| attribute vec2 texCoord0; |
| varying vec2 texCoord; |
| void main() |
| { |
| gl_Position = vPosition; |
| texCoord = texCoord0; |
| } |
| </script> |
| |
| <script id="fshader" type="x-shader/x-fragment"> |
| precision mediump float; |
| uniform sampler2D tex; |
| varying vec2 texCoord; |
| void main() |
| { |
| gl_FragColor = texture2D(tex, texCoord); |
| } |
| </script> |
| |
| <script> |
| "use strict"; |
| |
| var wtu = WebGLTestUtils; |
| var canvas; |
| var gl; |
| var ext = null; |
| |
| var extConstants = { |
| "SRGB_EXT": 0x8C40, |
| "SRGB_ALPHA_EXT": 0x8C42, |
| "SRGB8_ALPHA8_EXT": 0x8C43, |
| "FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT": 0x8210 |
| }; |
| |
| function getExtension() { |
| ext = gl.getExtension("EXT_sRGB"); |
| } |
| |
| function listsExtension() { |
| var supported = gl.getSupportedExtensions(); |
| return (supported.indexOf("EXT_sRGB") >= 0); |
| } |
| |
| function toVec3String(val) { |
| if (typeof(val) == 'number') { |
| return toVec3String([val, val, val]); |
| } |
| return '[' + val[0] + ', ' + val[1] + ', ' + val[2] + ']'; |
| } |
| |
| var e = 2; // Amount of variance to allow in result pixels - may need to be tweaked higher |
| |
| function expectResult(target) { |
| wtu.checkCanvasRect(gl, |
| Math.floor(gl.drawingBufferWidth / 2), |
| Math.floor(gl.drawingBufferHeight / 2), |
| 1, |
| 1, |
| [target, target, target, 255], |
| undefined, |
| e); |
| } |
| |
| function createGreysRGBTexture(gl, color, format) { |
| var numPixels = gl.drawingBufferWidth * gl.drawingBufferHeight; |
| var elements; |
| switch (format) { |
| case ext.SRGB_EXT: elements = 3; break; |
| case ext.SRGB_ALPHA_EXT: elements = 4; break; |
| default: return null; |
| } |
| |
| var size = numPixels * elements; |
| var buf = new Uint8Array(size); |
| for (var ii = 0; ii < numPixels; ++ii) { |
| var off = ii * elements; |
| buf[off + 0] = color; |
| buf[off + 1] = color; |
| buf[off + 2] = color; |
| if (format == ext.SRGB_ALPHA_EXT) { |
| buf[off + 3] = 255; |
| } |
| } |
| |
| var tex = gl.createTexture(); |
| gl.bindTexture(gl.TEXTURE_2D, tex); |
| gl.texImage2D(gl.TEXTURE_2D, |
| 0, |
| format, |
| gl.drawingBufferWidth, |
| gl.drawingBufferHeight, |
| 0, |
| format, |
| gl.UNSIGNED_BYTE, |
| buf); |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); |
| 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); |
| return tex; |
| } |
| |
| function testValidFormat(fn, internalFormat, formatName, enabled) { |
| if (enabled) { |
| fn(internalFormat); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "was able to create type " + formatName); |
| } else { |
| testInvalidFormat(fn, internalFormat, formatName, enabled); |
| } |
| } |
| |
| function testInvalidFormat(fn, internalFormat, formatName, enabled) { |
| fn(internalFormat); |
| var err = gl.getError(); |
| if (err == gl.NO_ERROR) { |
| testFailed("should NOT be able to create type " + formatName); |
| } else if (err == gl.INVALID_ENUM || err == gl.INVALID_VALUE) { |
| testPassed("not able to create invalid format: " + formatName); |
| } |
| } |
| |
| var textureFormatFixture = { |
| desc: "Checking texture formats", |
| create: function(format) { |
| var tex = gl.createTexture(); |
| gl.bindTexture(gl.TEXTURE_2D, tex); |
| gl.texImage2D(gl.TEXTURE_2D, |
| 0, // level |
| format, // internalFormat |
| gl.drawingBufferWidth, // width |
| gl.drawingBufferHeight, // height |
| 0, // border |
| format, // format |
| gl.UNSIGNED_BYTE, // type |
| null); // data |
| }, |
| tests: [ |
| { |
| desc: "Checking valid formats", |
| fn: testValidFormat, |
| formats: [ 'SRGB_EXT', 'SRGB_ALPHA_EXT' ] |
| }, |
| { |
| desc: "Checking invalid formats", |
| fn: testInvalidFormat, |
| formats: [ 'SRGB8_ALPHA8_EXT' ] |
| } |
| ] |
| }; |
| |
| var renderbufferFormatFixture = { |
| desc: "Checking renderbuffer formats", |
| create: function(format) { |
| var rbo = gl.createRenderbuffer(); |
| gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); |
| gl.renderbufferStorage(gl.RENDERBUFFER, |
| format, |
| gl.drawingBufferWidth, |
| gl.drawingBufferHeight); |
| }, |
| tests: [ |
| { |
| desc: "Checking valid formats", |
| fn: testValidFormat, |
| formats: [ 'SRGB8_ALPHA8_EXT' ] |
| }, |
| { |
| desc: "Checking invalid formats", |
| fn: testInvalidFormat, |
| formats: [ 'SRGB_EXT', 'SRGB_ALPHA_EXT' ] |
| } |
| ] |
| }; |
| |
| |
| description("Test sRGB texture support"); |
| |
| debug(""); |
| debug("Canvas.getContext"); |
| |
| canvas = document.getElementById("canvas"); |
| gl = wtu.create3DContext(canvas); |
| if (!gl) { |
| testFailed("context does not exist"); |
| } else { |
| testPassed("context exists"); |
| |
| debug(""); |
| debug("Checking sRGB texture support with extension disabled"); |
| |
| runFormatTest(textureFormatFixture, false); |
| runFormatTest(renderbufferFormatFixture, false); |
| |
| debug(""); |
| debug("Checking sRGB texture support"); |
| |
| // Query the extension and store globally so shouldBe can access it |
| ext = gl.getExtension("EXT_sRGB"); |
| |
| if (!ext) { |
| testPassed("No EXT_sRGB support -- this is legal"); |
| |
| runSupportedTest(false); |
| finishTest(); |
| } else { |
| testPassed("Successfully enabled EXT_sRGB extension"); |
| |
| runSupportedTest(true); |
| |
| gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); |
| |
| runConstantsTest(); |
| runFormatTest(textureFormatFixture, true); |
| runFormatTest(renderbufferFormatFixture, true); |
| runTextureReadConversionTest(); |
| runFramebufferTextureConversionTest(ext.SRGB_EXT); |
| runFramebufferTextureConversionTest(ext.SRGB_ALPHA_EXT); |
| runFramebufferRenderbufferConversionTest(); |
| runGenerateMipmapTest(); |
| runLoadFromImageTest(function() { |
| finishTest(); |
| }); |
| } |
| } |
| |
| function runConstantsTest() { |
| debug(""); |
| debug("Checking extension constants values"); |
| |
| for (var constant in extConstants) { |
| if (constant in ext) { |
| if (extConstants[constant] != ext[constant]) { |
| testFailed("Value of " + constant + " should be: " + extConstants[constant] + ", was: " + ext[constant]); |
| } else { |
| testPassed("Value of " + constant + " was expected value: " + extConstants[constant]); |
| } |
| } else { |
| testFailed(constant + " not found in extension object"); |
| } |
| } |
| } |
| |
| function runSupportedTest(extensionEnabled) { |
| if (listsExtension()) { |
| if (extensionEnabled) { |
| testPassed("EXT_sRGB listed as supported and getExtension succeeded"); |
| } else { |
| testFailed("EXT_sRGB listed as supported but getExtension failed"); |
| } |
| } else { |
| if (extensionEnabled) { |
| testFailed("EXT_sRGB not listed as supported but getExtension succeeded"); |
| } else { |
| testPassed("EXT_sRGB not listed as supported and getExtension failed -- this is legal"); |
| } |
| } |
| } |
| |
| function runFormatTest(fixture, enabled) { |
| debug(""); |
| debug(fixture.desc); |
| |
| for (var tt = 0; tt < fixture.tests.length; ++tt) { |
| var test = fixture.tests[tt]; |
| debug(test.desc); |
| |
| for (var ii = 0; ii < test.formats.length; ++ii) { |
| var formatName = test.formats[ii]; |
| test.fn(fixture.create, extConstants[formatName], "ext." + formatName, enabled); |
| } |
| |
| if (tt != fixture.tests.length - 1) |
| debug(""); |
| } |
| } |
| |
| function runTextureReadConversionTest() { |
| debug(""); |
| debug("Test the conversion of colors from sRGB to linear on texture read"); |
| |
| // Draw |
| var conversions = [ |
| [ 0, 0 ], |
| [ 63, 13 ], |
| [ 127, 54 ], |
| [ 191, 133 ], |
| [ 255, 255 ] |
| ]; |
| |
| var program = wtu.setupTexturedQuad(gl); |
| gl.uniform1i(gl.getUniformLocation(program, "tex"), 0); |
| |
| for (var ii = 0; ii < conversions.length; ii++) { |
| var tex = createGreysRGBTexture(gl, conversions[ii][0], ext.SRGB_EXT); |
| wtu.drawUnitQuad(gl); |
| expectResult(conversions[ii][1]); |
| } |
| } |
| |
| function runFramebufferTextureConversionTest(format) { |
| var formatString; |
| var validFormat; |
| switch (format) { |
| case ext.SRGB_EXT: formatString = "sRGB"; validFormat = false; break; |
| case ext.SRGB_ALPHA_EXT: formatString = "sRGB_ALPHA"; validFormat = true; break; |
| default: return null; |
| } |
| debug(""); |
| debug("Test " + formatString + " framebuffer attachments." + (validFormat ? "" : " (Invalid)")); |
| |
| var program = wtu.setupProgram(gl, ['vertexShader', 'fragmentShader'], ['aPosition'], [0]); |
| var tex = createGreysRGBTexture(gl, 0, format); |
| var fbo = gl.createFramebuffer(); |
| gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); |
| gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT)', 'ext.SRGB_EXT'); |
| |
| if (validFormat) { |
| shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); |
| |
| debug(""); |
| debug("Test the conversion of colors from linear to " + formatString + " on framebuffer (texture) write"); |
| |
| // Draw |
| var conversions = [ |
| [ 0, 0 ], |
| [ 13, 63 ], |
| [ 54, 127 ], |
| [ 133, 191 ], |
| [ 255, 255 ] |
| ]; |
| |
| wtu.setupUnitQuad(gl, 0); |
| |
| for (var ii = 0; ii < conversions.length; ii++) { |
| gl.uniform1f(gl.getUniformLocation(program, "uColor"), conversions[ii][0]/255.0); |
| wtu.drawUnitQuad(gl, [0, 0, 0, 0]); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| expectResult(conversions[ii][1]); |
| } |
| } else { |
| shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); |
| |
| wtu.setupUnitQuad(gl, 0); |
| gl.uniform1f(gl.getUniformLocation(program, "uColor"), 0.5); |
| wtu.drawUnitQuad(gl, [0, 0, 0, 0]); |
| wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION); |
| } |
| |
| gl.bindFramebuffer(gl.FRAMEBUFFER, null); |
| } |
| |
| function runFramebufferRenderbufferConversionTest() { |
| debug(""); |
| debug("Test the conversion of colors from linear to sRGB on framebuffer (renderbuffer) write"); |
| |
| function createsRGBFramebuffer(gl, width, height) { |
| var rbo = gl.createRenderbuffer(); |
| gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); |
| gl.renderbufferStorage(gl.RENDERBUFFER, ext.SRGB8_ALPHA8_EXT, width, height); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| var fbo = gl.createFramebuffer(); |
| gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); |
| gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, |
| gl.RENDERBUFFER, rbo); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT)', 'ext.SRGB_EXT'); |
| shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); |
| |
| return fbo; |
| } |
| |
| // Draw |
| var conversions = [ |
| [ 0, 0 ], |
| [ 13, 63 ], |
| [ 54, 127 ], |
| [ 133, 191 ], |
| [ 255, 255 ] |
| ]; |
| |
| var program = wtu.setupProgram(gl, ['vertexShader', 'fragmentShader'], ['aPosition'], [0]); |
| wtu.setupUnitQuad(gl, 0); |
| var fbo = createsRGBFramebuffer(gl, gl.drawingBufferWidth, gl.drawingBufferHeight); |
| |
| for (var ii = 0; ii < conversions.length; ii++) { |
| gl.uniform1f(gl.getUniformLocation(program, "uColor"), conversions[ii][0]/255.0); |
| wtu.drawUnitQuad(gl, [0, 0, 0, 0]); |
| expectResult(conversions[ii][1]); |
| } |
| } |
| |
| function runLoadFromImageTest(callback) { |
| debug(""); |
| debug("Tests to ensure that SRGB textures can successfully use image elements as their source"); |
| |
| var img = wtu.makeImage("../../resources/gray-1024x1024.jpg", function() { |
| var tex = gl.createTexture(); |
| gl.bindTexture(gl.TEXTURE_2D, tex); |
| gl.texImage2D(gl.TEXTURE_2D, 0, ext.SRGB_EXT, ext.SRGB_EXT, gl.UNSIGNED_BYTE, img); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| gl.texImage2D(gl.TEXTURE_2D, 0, ext.SRGB_ALPHA_EXT, ext.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE, img); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| callback(); |
| }, function() { |
| testFailed("Image could not be loaded"); |
| callback(); |
| }); |
| } |
| |
| function runGenerateMipmapTest() |
| { |
| debug(""); |
| debug("GenerateMipmaps for sRGB textures is forbidden"); |
| |
| var tex = gl.createTexture(); |
| gl.bindTexture(gl.TEXTURE_2D, tex); |
| |
| gl.texImage2D(gl.TEXTURE_2D, 0, ext.SRGB_ALPHA_EXT, 2, 2, 0, ext.SRGB_ALPHA_EXT, |
| gl.UNSIGNED_BYTE, null); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| gl.generateMipmap(gl.TEXTURE_2D); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); |
| |
| gl.deleteTexture(tex); |
| } |
| |
| var successfullyParsed = true; |
| </script> |
| </body> |
| </html> |