WebGL Glitch In Some Intel Integrated Graphics Processors
Asked Answered
E

1

9

I have the situation that a shader produces a completely different output on my Apple (M1) iMac (and also iPhone) than on my Intel (i5-4300U / Haswell-ULT Integrated Graphics Controller) Thinkpad 440 (more info in the comments). Yet there seems to be no difference between browsers, Chrome and Safari resp. Chrome and GNOME Web produce the same image on the iMac resp. laptop. My Intel Thinkpad 13 Chromebook also displays the Intel version (couldn't test other browsers than Chrome there obviously). What could be the reason for this?

[EDIT] Most people (friends and folks who commented below) seem to get the Apple version, so only me and my two laptops apparently get the Intel version?

This is the shader:

  const gl = document.querySelector("canvas").getContext("webgl");
  gl.canvas.width = gl.canvas.height = 512
  gl.viewport(0, 0, 512, 512);
  const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    a_Position: {
      numComponents: 2,
      data: [-1, -1, -1, 3, 3, -1]
    },
  });
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.drawBufferInfo(gl, bufferInfo);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="x-shader/x-vertex">
  attribute vec4 a_Position;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  void main() {
      v_Position = a_Position.xy;
      v_TexCoord = a_Position.xy / 2.0 + 0.5;
      gl_Position = a_Position;
  }
</script>
<script id="fs" type="x-shader/x-vertex">
  precision highp float;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  vec2 compMult(vec2 u, vec2 v) {
    return vec2(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
  }

  vec2 G(vec3 x) {
    float lenx = length(x);
    float ang = 350.0 * lenx;
    return vec2(cos(ang), sin(ang)) / lenx;

  }

  void main() {
    gl_FragColor.a = 1.0;
    vec3 x0 = vec3(v_TexCoord, 1);
    vec2 amplitude;
    for(int i = 0; i < 10; i++) {
      for(int j = 0; j < 10; j++) {
        vec2 pos = 0.05 + vec2(i, j) / 10.0;
        vec3 x = vec3(0.4 + 0.2 * pos, 0);
        float disx0x = distance(x0, x);
        amplitude += compMult(
          G(x - vec3(-0.4765625, -0.671875, 300)),
          compMult(
            vec2(-1.0 / disx0x, 350.0),
            G(x - x0) / disx0x
          )
        );
      }
    }
    gl_FragColor.r = length(amplitude) / 10.0;
  }
</script>
<canvas></canvas>

And here are the different outputs:

(Apple)

(Intel)

[EDIT] I think I'm pretty close, look at the following snippet:

  const gl = document.querySelector("canvas").getContext("webgl");
  gl.canvas.width = gl.canvas.height = 512
  gl.viewport(0, 0, 512, 512);
  const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    a_Position: {
      numComponents: 2,
      data: [-1, -1, -1, 3, 3, -1]
    },
  });
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.drawBufferInfo(gl, bufferInfo);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="x-shader/x-vertex">
  attribute vec4 a_Position;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  void main() {
      v_Position = a_Position.xy;
      v_TexCoord = a_Position.xy / 2.0 + 0.5;
      gl_Position = a_Position;
  }
</script>
<script id="fs" type="x-shader/x-vertex">
  precision highp float;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  vec2 compMult(vec2 u, vec2 v) {
    return vec2(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
  }

  vec2 G(vec3 x) {
    float lenx = length(x);
    float ang = 350.0 * lenx;
    return vec2(cos(ang), sin(ang)) / lenx;

  }

  void main() {
    gl_FragColor.a = 1.0;
    vec3 x0 = vec3(v_TexCoord, 1);
    vec2 pos = floor(8.0 * v_TexCoord) / 8.0;
    vec3 x = vec3(0.375 + 0.25 * pos, 0);
    float disx0x = distance(x0, x);
    vec2 amplitude = compMult(
      G(x - vec3(-0.46875, -0.5, 300.0)),
      compMult(
        vec2(-1.0 / disx0x, 350.0),
        G(x - x0)
      )
    );
    gl_FragColor.rgb = vec3(0.5 + amplitude, 0.5);
  }
</script>
<canvas></canvas>

This is what it looks like on my Intel machines:

I think, from looking at the algorithm (there is not even a loop anymore), it's pretty clear that the gray areas should not be gray but similar to the other tiles. Also note that I never divide by small numbers, x resp. x0 have z-coordinates of 0 resp. 1, so distance(x, x0) and distance(x, vec3(-0.46875, -0.5, 300)) are never small, which are the only values I use as dividers. Also I changed some constants so that now all numbers have exact binary representations.

Enterovirus answered 22/7, 2022 at 12:22 Comment(7)
Cool finding. I get the Apple version on my laptop which has an AMD CPU and Nvidia graphics.Appurtenance
Thanks for letting us know! Yeah, I almost suspect that my Intel hardware is off somehow. Would be interesting if somebody with newer Intel hardware sees the same thing as me.Enterovirus
I get the Apple version on my laptop which has an Intel Core i5-7200U and integrated graphics (Intel HD Graphics 620)Tsunami
Weird, so I need a new laptop I guess :/Enterovirus
Can you please provide the specifications (Graphics API, OS name and version) of the two intel laptops.Chancellorsville
@HamidYusifli Here is the output from chrome://gpu on my Thinkpad 440 and this is the one on my Chromebook (Thinkpad 13), hope it helps!Enterovirus
As far as I can see, it is only a floating point accuracy issue. The accuracy depends on the hardware. Change float ang = 350.0 * lenx; to float ang = 300.0 * lenx; to see what I mean.Darwindarwinian
D
8

It is just a problem with the sin and cos functions on intel systems. The argument seems to exceed the defined value range. The allowed range of values depends on the vendor. This can be solved with the mod function. (4.0*acos(0.0) is 2*PI)

float ang = 350.0 * lenx;

float ang = mod(350.0*lenx, 4.0*acos(0.0));

  const gl = document.querySelector("canvas").getContext("webgl");
  gl.canvas.width = gl.canvas.height = 512
  gl.viewport(0, 0, 512, 512);
  const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    a_Position: {
      numComponents: 2,
      data: [-1, -1, -1, 3, 3, -1]
    },
  });
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.drawBufferInfo(gl, bufferInfo);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="x-shader/x-vertex">
  attribute vec4 a_Position;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  void main() {
      v_Position = a_Position.xy;
      v_TexCoord = a_Position.xy / 2.0 + 0.5;
      gl_Position = a_Position;
  }
</script>
<script id="fs" type="x-shader/x-vertex">
  precision highp float;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  vec2 compMult(vec2 u, vec2 v) {
    return vec2(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
  }

  vec2 G(vec3 x) {
    float lenx = length(x);
    float ang = mod(350.0*lenx, 4.0*acos(0.0));
    return vec2(cos(ang), sin(ang)) / lenx;

  }

  void main() {
    gl_FragColor.a = 1.0;
    vec3 x0 = vec3(v_TexCoord, 1);
    vec2 amplitude;
    for(int i = 0; i < 10; i++) {
      for(int j = 0; j < 10; j++) {
        vec2 pos = 0.05 + vec2(i, j) / 10.0;
        vec3 x = vec3(0.4 + 0.2 * pos, 0);
        float disx0x = distance(x0, x);
        amplitude += compMult(
          G(x - vec3(-0.4765625, -0.671875, 300)),
          compMult(
            vec2(-1.0 / disx0x, 350.0),
            G(x - x0) / disx0x
          )
        );
      }
    }
    gl_FragColor.r = length(amplitude) / 10.0;
  }
</script>
<canvas></canvas>
Darwindarwinian answered 23/7, 2022 at 7:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.