From 58841c23919bd59787effc0333a4897b43835412 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Mon, 29 Jul 2013 23:50:21 -0700 Subject: [PATCH] add support for OES_texture_float_linear with polyfill --- src/OES_texture_float_linear-polyfill.js | 131 +++++++++++++++++++++++ src/core/canvas.js | 2 +- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/OES_texture_float_linear-polyfill.js diff --git a/src/OES_texture_float_linear-polyfill.js b/src/OES_texture_float_linear-polyfill.js new file mode 100644 index 0000000..ac47593 --- /dev/null +++ b/src/OES_texture_float_linear-polyfill.js @@ -0,0 +1,131 @@ +// From: https://github.com/evanw/OES_texture_float_linear-polyfill +(function() { + // Uploads a 2x2 floating-point texture where one pixel is 2 and the other + // three pixels are 0. Linear filtering is only supported if a sample taken + // from the center of that texture is (2 + 0 + 0 + 0) / 4 = 0.5. + function supportsOESTextureFloatLinear(gl) { + // Need floating point textures in the first place + if (!gl.getExtension('OES_texture_float')) { + return false; + } + + // Create a render target + var framebuffer = gl.createFramebuffer(); + var byteTexture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, byteTexture); + 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); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, byteTexture, 0); + + // Create a simple floating-point texture with value of 0.5 in the center + var rgba = [ + 2, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + ]; + var floatTexture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, floatTexture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_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, 2, 2, 0, gl.RGBA, gl.FLOAT, new Float32Array(rgba)); + + // Create the test shader + var program = gl.createProgram(); + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(vertexShader, '\ + attribute vec2 vertex;\ + void main() {\ + gl_Position = vec4(vertex, 0.0, 1.0);\ + }\ + '); + gl.shaderSource(fragmentShader, '\ + uniform sampler2D texture;\ + void main() {\ + gl_FragColor = texture2D(texture, vec2(0.5));\ + }\ + '); + gl.compileShader(vertexShader); + gl.compileShader(fragmentShader); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + // Create a buffer containing a single point + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0]), gl.STREAM_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + + // Render the point and read back the rendered pixel + var pixel = new Uint8Array(4); + gl.useProgram(program); + gl.viewport(0, 0, 1, 1); + gl.bindTexture(gl.TEXTURE_2D, floatTexture); + gl.drawArrays(gl.POINTS, 0, 1); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); + + // The center sample will only have a value of 0.5 if linear filtering works + return pixel[0] === 127 || pixel[0] === 128; + } + + // The constructor for the returned extension object + function OESTextureFloatLinear() { + } + + // Cache the extension so it's specific to each context like extensions should be + function getOESTextureFloatLinear(gl) { + if (gl.$OES_texture_float_linear$ === void 0) { + Object.defineProperty(gl, '$OES_texture_float_linear$', { + enumerable: false, + configurable: false, + writable: false, + value: new OESTextureFloatLinear() + }); + } + return gl.$OES_texture_float_linear$; + } + + // This replaces the real getExtension() + function getExtension(name) { + return name === 'OES_texture_float_linear' + ? getOESTextureFloatLinear(this) + : oldGetExtension.call(this, name); + } + + // This replaces the real getSupportedExtensions() + function getSupportedExtensions() { + var extensions = oldGetSupportedExtensions.call(this); + if (extensions.indexOf('OES_texture_float_linear') === -1) { + extensions.push('OES_texture_float_linear'); + } + return extensions; + } + + // Get a WebGL context + try { + var gl = document.createElement('canvas').getContext('experimental-webgl'); + } catch (e) { + } + + // Don't install the polyfill if the browser already supports it or doesn't have WebGL + if (!gl || gl.getSupportedExtensions().indexOf('OES_texture_float_linear') !== -1) { + return; + } + + // Install the polyfill if linear filtering works with floating-point textures + if (supportsOESTextureFloatLinear(gl)) { + var oldGetExtension = WebGLRenderingContext.prototype.getExtension; + var oldGetSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions; + WebGLRenderingContext.prototype.getExtension = getExtension; + WebGLRenderingContext.prototype.getSupportedExtensions = getSupportedExtensions; + } +}()); diff --git a/src/core/canvas.js b/src/core/canvas.js index aeb87ed..b9bc4e1 100644 --- a/src/core/canvas.js +++ b/src/core/canvas.js @@ -31,7 +31,7 @@ function initialize(width, height) { // filter look a lot better. Note that on Windows, ANGLE does not let you // render to a floating-point texture when linear filtering is enabled. // See http://crbug.com/172278 for more information. - if (gl.getExtension('OES_texture_float')) { + if (gl.getExtension('OES_texture_float') && gl.getExtension('OES_texture_float_linear')) { var testTexture = new Texture(100, 100, gl.RGBA, gl.FLOAT); try { // Only use gl.FLOAT if we can render to it