Skip to content

Loop protection breaks shaders #4080

@davepagurek

Description

@davepagurek

p5.js version

All

What is your operating system?

Mac OS

Web browser and version

All

Actual Behavior

Shaders that have a for loop in its source code in a string, or p5.strands shader functions, will break when you have not added // noprotect as a comment.

Expected Behavior

Ideally, neither of these should cause the sketch to fail, so loop protection would skip making changes in strings or in p5.strands functions.

Steps to reproduce

If you have a shader string that includes a for loop, such as in the following sketch, loop protection still gets added to the shader code, which produces invalid GLSL:

let blur
function setup() {
  createCanvas(400, 400, WEBGL)
  blur = buildFilterShader({
    'vec4 getColor': `(FilterInputs inputs, in sampler2D canvasContent) {
      vec4 total = vec4(0., 0., 0., 0.);
      for (int i = 0; i < 20; i++) {
        total += getTexture(
          canvasContent,
          inputs.texCoord + 15. * vec2(
            cos(float(i)/20. * ${TWO_PI}),
            sin(float(i)/20. * ${TWO_PI})
          ) / inputs.canvasSize
        );
      }
      vec4 avg = total / 20.;
      return avg;
    }`
  })
}

function draw() {
  background(220)
  noStroke()
  circle(0, 0, 100)
  filter(blur)
}

Live (remove the nopretect to test): https://editor.p5js.org/davepagurek/sketches/ozs78nOXV

More complicated still is a p5.strands shader, which is still javascript, but gets compiled into GLSL, so it needs to not have loop protection added in it:

let blur
function setup() {
  createCanvas(400, 400, WEBGL)
  blur = buildFilterShader(doBlur)
}

function doBlur() {
  filterColor.begin()
  let total = [0, 0, 0, 0]
  for (let i = 0; i < 20; i++) {
    total += getTexture(
      filterColor.canvasContent,
      filterColor.texCoord + 15 * [
        cos(i/20 * TWO_PI),
        sin(i/20 * TWO_PI)
      ] / filterColor.canvasSize
    )
  }
  let avg = total / 20
  filterColor.set(avg)
  filterColor.end()
}

function draw() {
  background(220)
  noStroke()
  circle(0, 0, 100)
  filter(blur)
}

Live (remove noprotect to test): https://editor.p5js.org/davepagurek/sketches/ByAq44OVU

Ideally, loop protection should not break code. To do this, we'd likely have to shift away from the current regex-based approach used by catarak/loop-protect.

The original jsbin/loop-protect has since been updated to use Babel's AST, so it would not transform strings (it's interesting to see the change in readme between the fork, which branched off of the jsbin one a while ago, and the more recently updated jsbin one, on its stance on ASTs.)

However, just updating to jsbin/loop-protect would not fix p5.strands functions: for that, we'd need to additionally (1) find p5.strands function calls (in the form of build*Shader(...) functions and base*Shader().modify(...) functions), and (2) skip any transpilation of functions passed into them, either inline functions or references to functions declared elsewhere.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugError or unexpected behaviors

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions