Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility with webgl 2 #561

Closed
fuzhenn opened this issue May 23, 2020 · 10 comments
Closed

Compatibility with webgl 2 #561

fuzhenn opened this issue May 23, 2020 · 10 comments

Comments

@fuzhenn
Copy link
Contributor

fuzhenn commented May 23, 2020

First of all, this issue is NOT about upgrade to webgl 2, as #378.

This issue is about letting regl run on a webgl 2 context.

The reasons:

I have done some experiments in my projects and it seems feasible, I thinks it's a useful feature and can really save someone's life when only webgl 2 can do the things (e.g. myself 🙋 ) .

So if you think it's worth a try, I would like to submit a PR later. Thanks for your time.

@rreusser
Copy link
Member

rreusser commented May 27, 2020

If I override the test util to request a webgl2 context, I get a number of test failures (in the stencil + depth tests, specifically). I don't have the ability to work through these issues and seek more graceful failure, though I understand the desire to let it run on WebGL 2 and see what happens. Since this will always fundamentally be a WebGL 1 library, I think maybe the best approach is to monkey patch context creation with something like this:

function overrideContextType (forcedContextType, callback) {
  // Monkey-patch context creation to override the context type
  const origGetContext = HTMLCanvasElement.prototype.getContext
  HTMLCanvasElement.prototype.getContext = function (ignoredContextType, contextAttributes) {
    return origGetContext.bind(this)(forcedContextType, contextAttributes);
  };

  // Execute the callback with overridden context type
  callback();

  // Restore the original method
  HTMLCanvasElement.prototype.getContext = origGetContext;
}

regl = overrideContextType('webgl2', () => createREGL(...))

I have not tested this. After this, things may or may not work as expected. Buyer beware! I have no idea what will happen, though most of the tests seem to pass. Short of forking regl and meaningfully modifying it to work with webgl 2, I think this is the best suggestion I can offer.

@rreusser
Copy link
Member

To be clear though, short of a PR that performs some tricks to allow regl tests to pass on a webgl2 context, I do think something like the above code, if it works, is the ideal solution since it puts the burden on the user to obtain a webgl2 context and own responsibility for any undesirable side effects.

@fuzhenn
Copy link
Contributor Author

fuzhenn commented May 29, 2020

Thanks for your opinions. I am testing webgl2-compatible version regl in my project right now, and it seems to be promising.

I added some adapter codes to change the parameters on the fly before calling gl functions (e.g. gl.texImage2D). Until now it seems not many need to be adapted:

  • depth texture's internal format and format
  • float and half_float texture's internal format
  • vao extension functions
  • instancing extension functions
  • draw buffers extension functions

The code is not long, so I would like to paste here for a quick glance:

var GL_DEPTH_COMPONENT = 0x1902
var GL_DEPTH_STENCIL = 0x84F9
var HALF_FLOAT_OES = 0x8D61

// webgl1 extensions natively supported by webgl2
var gl2Extensions = {
  'WEBGL_depth_texture': {
    'UNSIGNED_INT_24_8_WEBGL': 0x84FA
  },
  'OES_element_index_uint': {},
  'OES_texture_float': {},
  'OES_texture_float_linear': {},
  'OES_texture_half_float': {
    'HALF_FLOAT_OES': 0x8D61
  },
  'OES_texture_half_float_linear': {},
  'EXT_color_buffer_float': {},
  'OES_standard_derivatives': {},
  'EXT_frag_depth': {},
  'EXT_blend_minmax': {
    'MIN_EXT': 0x8007,
    'MAX_EXT': 0x8008
  },
  'EXT_shader_texture_lod': {}
}

module.exports = {
  // webgl1 extensions natively supported by webgl2
  // this is called when initializing regl context
  gl2: function (gl, extensions) {
    gl[this.versionProperty] = 2
    for (var p in gl2Extensions) {
      extensions[p.toLowerCase()] = gl2Extensions[p]
    }

    // to support float and half-float textures
    gl.getExtension('EXT_color_buffer_float')

    // mocks of draw buffers's functions
    extensions['webgl_draw_buffers'] = {
      drawBuffersWEBGL: function () {
        return gl.drawBuffers.apply(gl, arguments)
      }
    }

    // mocks of vao extension
    extensions['oes_vertex_array_object'] = {
      'VERTEX_ARRAY_BINDING_OES':   0x85B5,
      'createVertexArrayOES': function () {
        return gl.createVertexArray()
      },
      'deleteVertexArrayOES': function () {
        return gl.deleteVertexArray.apply(gl, arguments)
      },
      'isVertexArrayOES': function () {
        return gl.isVertexArray.apply(gl, arguments)
      },
      'bindVertexArrayOES': function () {
        return gl.bindVertexArray.apply(gl, arguments)
      }
    }

    // mocks of instancing extension
    extensions['angle_instanced_arrays'] = {
      'VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE': 0x88FE,
      'drawArraysInstancedANGLE': function () {
        return gl.drawArraysInstanced.apply(gl, arguments)
      },
      'drawElementsInstancedANGLE': function () {
        return gl.drawElementsInstanced.apply(gl, arguments)
      },
      'vertexAttribDivisorANGLE': function () {
        return gl.vertexAttribDivisor.apply(gl, arguments)
      }
    }
  },

  versionProperty: '___regl_gl_version___',

  // texture internal format to update on the fly
  getInternalFormat: function (gl, format, type) {
    if (gl[this.versionProperty] !== 2) {
      return format
    }
    // webgl2 texture formats
    // reference:
    // https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html
    if (format === GL_DEPTH_COMPONENT) {
      // gl.DEPTH_COMPONENT24
      return 0x81A6
    } else if (format === GL_DEPTH_STENCIL) {
      // gl.DEPTH24_STENCIL8
      return 0x88F0
    } else if (type === HALF_FLOAT_OES && format === gl.RGBA) {
      // gl.RGBA16F
      return 0x881A
    } else if (type === HALF_FLOAT_OES && format === gl.RGB) {
      // gl.RGB16F
      return 0x881B
    } else if (type === gl.FLOAT && format === gl.RGBA) {
      // gl.RGBA32F
      return 0x8814
    } else if (type === gl.FLOAT && format === gl.RGB) {
      // gl.RGB32F
      return 0x8815
    }
    return format
  },

  // texture type to update on the fly
  getTextureType: function (gl, type) {
    if (gl[this.versionProperty] !== 2) {
      return type
    }
    if (type === HALF_FLOAT_OES) {
      return gl.HALF_FLOAT
    }
    return type
  }
}

@fuzhenn
Copy link
Contributor Author

fuzhenn commented Jun 4, 2020

Solved this issue by creating a wrapper of WebGL2 context to let it behave like a WebGL1 context.

Closing this now, thx for your time!

@fuzhenn fuzhenn closed this as completed Jun 4, 2020
@rreusser
Copy link
Member

rreusser commented Jun 4, 2020

Sounds good, @fuzhenn. If it's a wrapper with enough reusability, it could be added as a regl-webgl-2-compat repo or something, but glad it works!

@stevekane
Copy link

@fuzhenn This is great work and I am late to the party! Would you be willing to share your complete working wrapper or possibly even make it available somewhere on NPM?

@mpcomplete
Copy link

After some fiddling with @fuzhenn 's code a bit, I got it working for me as a relatively simple wrapper. https://github.com/mpcomplete/fireflies/blob/master/web/regl-webgl2-compat.js

You can use it like:
const webgl2 = require("./regl-webgl2-compat.js");
const regl = webgl2.overrideContextType(() => require("regl")({extensions: ...}));

I may make a standalone NPM module unless @fuzhenn would prefer to.

@fuzhenn
Copy link
Contributor Author

fuzhenn commented Jul 1, 2020

My wrapper is part of a big project, I haven't found any time window for a standalone version.
Thank @mpcomplete 's show up! Please go ahead for a npm module for anyone also needs this feature.

@baycxh
Copy link

baycxh commented Dec 23, 2020

@fuzhenn Hello,I got a problem with regl on android phone when I open a regl website.
I have changed serveral phones, come with the same problem. It appeared to be something wrong with the phone support for "oes_texture_float" extension. Is there any solution for the problem? Thanks!

Error: (regl) "oes_texture_float" extension is not supported by the current WebGL context

@rreusser
Copy link
Member

@baycxh In general, that's just a question of what the phone hardware supports. In many cases, you might be able to use OES_texture_half_float instead. I think there's not really a solution unless the developer takes into account different availability of extensions and implements fallbacks in case an extension isn't available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants