msfeldstein / interactive-shader-format-js

Renders ISF Effects and Compositions into a canvas
95 stars 19 forks source link

OffscreenCanvas support #16

Closed 2xAA closed 5 years ago

2xAA commented 6 years ago

No idea if this is the proper way of doing things, but it gets the job done.

I'll make a PR tonight, but these are my edits - just in case.

https://github.com/msfeldstein/interactive-shader-format-js/blob/7f84e3a3acf52c0f7777ebac864b86c82c84bff8/src/ISFRenderer.js#L141

ISFRenderer.prototype.pushTexture = function pushTexture(uniform) {
  if (!uniform.value) {
    return;
  }

  let flip = false;

  if (
    uniform.value.constructor.name !== "OffscreenCanvas" &&
    (
      uniform.value.tagName !== 'CANVAS' &&
      !uniform.value.complete &&
      uniform.value.readyState !== 4)
    ) {
    return;
  }

  if (uniform.value.constructor.name === "OffscreenCanvas") {
    uniform.value = uniform.value.transferToImageBitmap();
    flip = true;
  }

  const loc = this.program.getUniformLocation(uniform.name);
  uniform.texture.bind(loc);
  this.gl.texImage2D(
    this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, uniform.value);
  if (!uniform.textureLoaded) {
    const img = uniform.value;
    uniform.textureLoaded = true;
    const w = img.naturalWidth || img.width || img.videoWidth;
    const h = img.naturalHeight || img.height || img.videoHeight;
    this.setValue(`_${uniform.name}_imgSize`, [w, h]);
    this.setValue(`_${uniform.name}_imgRect`, [0, 0, 1, 1]);
    this.setValue(`_${uniform.name}_flip`, flip);
  }
};
2xAA commented 6 years ago

Actually, I seem to be having a little issue with vFlip. This could be environment specific.

I'll have to write a demo for ISFjs and test soon, ran out of time today.

msfeldstein commented 6 years ago

how exactly is this supposed to work?

2xAA commented 6 years ago

~The whole of this library seems to work fine in a Worker - we should just need to a few lines to allow OffscreenCanvas to be used as a texture.~

My Webpack version of this library worked fine in a Worker. I think we just need to get this building to UMD spec and it should work. Also babel isn't in dev dependancies so the library isn't building right now.

OffscreenCanvas doesn't have a tag property as it's not a HTML Element, so I'm checking the constructor name instead.

And to get the image data for the texture you need to call canvas.transferToImageBitmap().

I added some flip variable as well because I was having issues with vertical texture flipping, but this didn't solve the problem. Like I said, it's probably environment specific so I'll write a OffscreenCanvas demo using the included demo soon and figure this out.

2xAA commented 6 years ago

Actually, we can't use this yet 😢 The texture flipping and the call to canvas.transferToImageBitmap() make this unusable - luckily, using OffscreenCanvas as a webGL texture is already in Chrome Canary (bugtracker) and the following code works.

I'll make a preemptive PR with OffscreenCanvas demo now for when this feature ships.

ISFRenderer.prototype.pushTexture = function pushTexture(uniform) {
  if (!uniform.value) {
    return;
  }

  if (
    uniform.value.constructor.name !== 'OffscreenCanvas' &&
    (
      uniform.value.tagName !== 'CANVAS' &&
      !uniform.value.complete &&
      uniform.value.readyState !== 4)
    ) {
    return;
  }

  const loc = this.program.getUniformLocation(uniform.name);
  uniform.texture.bind(loc);
  this.gl.texImage2D(
    this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, uniform.value);
  if (!uniform.textureLoaded) {
    const img = uniform.value;
    uniform.textureLoaded = true;
    const w = img.naturalWidth || img.width || img.videoWidth;
    const h = img.naturalHeight || img.height || img.videoHeight;
    this.setValue(`_${uniform.name}_imgSize`, [w, h]);
    this.setValue(`_${uniform.name}_imgRect`, [0, 0, 1, 1]);
    this.setValue(`_${uniform.name}_flip`, false);
  }
};