Error: Mismatch between texture format and sampler type

My portal framework, which I wrote several years ago, has been working great until just recently. A few weeks ago I started seeing this error on the console:

GL_INVALID_OPERATION: Mismatch between texture format and sampler type (signed/unsigned/float/shadow).

The texture in question is a WebGLRenderTarget, which is created like this:

      this.renderTarget = new WebGLRenderTarget(this.portalBufferSize.x, this.portalBufferSize.y, {
        minFilter: LinearFilter,
        magFilter: LinearFilter,
        wrapS: ClampToEdgeWrapping,
        wrapT: ClampToEdgeWrapping,
        format: RGBAFormat,
      });
      this.material.uniforms.portalTexture = { value: this.renderTarget.texture };

And the shader in question looks like this (some details omitted for clarity):

uniform sampler2D portalTexture;
uniform vec4 viewportRect;

void main() {
  vec2 txCoord = (gl_FragCoord.xy - viewportRect.xy) / viewportRect.zw;
  gl_FragColor = texture2D(portalTexture, txCoord);
}`;

I’m really not sure what changed, other than I recently upgraded to the latest three.js, although I am not assuming that’s the cause.

Any ideas?

A bit more information. The problem is not just an error message, it’s a bug which causes portals not to be displayed:

Here’s how it looks when the bug doesn’t happen:

Generally the bug only occurs after a page refresh - that is, if I load the game at some other location and then travel to the portal (so that it scrolls on-screen as you move) then everything works. It’s only when the portal is displayed on the first frame that I get this problem.

The brown region you see in the first shot is the offscreen render target / texture (which has already been rendered with the portal content) failing to render to the main scene view. My debugging experiments have shown that the offscreen render target is being generated with the right content - but when I try to transfer that content to the main view, I get the WebGL error about mismatched format.

I’m not even sure how to debug something like this - it’s not like I can set a breakpoint in a shader. All I have to work with is the WebGL warning message printed on the JavaScript console, which doesn’t tell me much. I did try installing spector.js to inspect the WebGL commands being issued (which worked great, BTW), but the sequence of commands was exactly what you would expect - and was no different for the non-working case vs the working case.

I would assume you already tried this, but here goes:

What happens if you specify a data type on the render target? Perhaps the data is coming in as uint8 but the sampler expects floats?

Also, is rendering of your portals happening on the same thread, or is there a separate worker thread responsible for this? Since you’re using render targets, I would assume everything is single threaded. Are you using multiple renderers, and is this render target filled by another renderer instance? (threejs is notoriously bad in this case where multiple renderers exist in the same global (module) scope - but looking at your other posts, I think you already experienced this :sweat_smile:)

Thank you for your thoughtful reply.

I have played around a bit with the type field. Unfortunately, there are 11 possible values, and I don’t have any criteria for choosing one over the others. I tried both UnsignedByteType and FloatType, and neither affected the observed behavior - this is somewhat surprising to me, as I would have assumed that picking the “wrong” value would cause the bug to appear 100% of the time, rather than appearing conditionally as it does now.

To answer your other questions:

  • I’m only using one thread. (I do have a web worker, but it’s only used for constructing navigation meshes).
  • For portals I am using the same renderer for both the portal scene and the main scene. I have a second renderer which is used for character portraits, however disabling that second renderer has no effect on the bug.

Well, I solved the problem, although I never did figure out the cause. My solution was to re-write the portal material from scratch by extending MeshBasicMaterial instead of using ShaderMaterial.

In general, I have had trouble with ShaderMaterial in the past. I have a lot of shaders where I want to re-use a lot of the existing three.js shader machinery - lighting, clipping, and so on - but with one minor tweak, such as using an algorithmic noise instead of a texture map, or calculating the incoming light vector slightly differently. Unfortunately ShaderMaterial only gives you a blank slate, which means you either have to implement all that stuff yourself, or you have to use #import of the existing shader chunks, which can break when three.js changes.

1 Like