Metal Textures Are Not Bound But They Are Still Getting Applied

In my scene I have 2 with the following textures.
1) Water Well
  • base color texture

  • normal map texture

2) Basic Quad
  • no textures

when calling draw primitives with the Water Well I am binding to index 0 and 1 the 'base color texture', and the 'normal map texture'.

when calling draw primitives with the Basic Quad I am not binding any textures. In fact I specifically called these 2 functions before rendering the quad (setting nil as the texture).

renderCommandEncoder.setFragmentTexture(nil, 0) renderCommandEncoder.setFragmentTexture(nil, 1)

The Problem:
Even though there is no texture bound to index 0 or 1 when drawing the Basic Quad, It is still using both of the textures from the Water Well. I cannot seem to clear the textures from the texture buffers. This seems like a bug with apple, but I need to be sure.

Thank you



Replies

Whether texturing is applied depends on whether your fragment shader reads or samples from a texture. I does not depend on what you set in setFragmentTexture. If your fragment shader reads/samples from a texture and you set the indices sample from to nil, you'll get undefined behavior.

Are you rendering these scenes right after one another (i.e. are you rendering Water Well and then rendering Basic Quad in the same execution)? Are you using a different RenderPipelineState object with a fragment shader which does not read or sample from the texture when you render the Basic Quad scene?
Hey thank you for answering!

I have tried using individual renderPipelineStates and one singular renderPipelineState, the result is the same. The Well gets rendered first and the Quad is rendered right after using the same renderPassDescriptor and currently the same renderPipelineState.

If you need more info just search "Metal Shading Language Texture Will Not Clear" in stackoverflow. that is my post.

fragment half4 basic_fragment_shader(RasterizerData rd [[ stage_in ]],                    constant Material &material [[ buffer(1) ]],                    constant int &lightCount [[ buffer(2) ]],                    constant LightData *lightDatas [[ buffer(3) ]],                    sampler sampler2d [[ sampler(0) ]],                    texture2d<float> baseColorMap [[ texture(0) ]],                    texture2d<float> normalMap [[ texture(1) ]]){   float2 texCoord = rd.textureCoordinate;       float4 color = material.color;   if(!is_null_texture(baseColorMap)) {     color = baseColorMap.sample(sampler2d, texCoord);   }       return half4(color.r, color.g, color.b, color.a); }

The reason I am setting them to nil is because the documentation states that nil is the default value.

I will try to play around with the samplers to see if they could be the culprit.

Do you maybe suggest that I pass the values as "model.hasBaseTexture" instead of is_null_texture()?
I do not like this solution but it would work!

Ok i am sending hasBaseTexture / hasNormalMapTexture with the material and that works. Is this what you would suggest?
So the fragment shader looks fine: If you call setFragmentTexture at index 0 to nil, the fragment shader should return the value of material.color. I actually took a look at your project on GitHub but couldn't tell how you were setting nil for the fragment texture.(perhaps a different branch?).

I'm wondering also if you may be running into a driver bug. Can you tell me what device (GPU type) you're seeing this on?

It looks like you've found a workaround using hasBaseTexture and hasNormalMapTexture.

However, I'd like to note that you generally want to avoid branching for something where you could have just set a different render pipeline since the if check theoretically needs to be evaluated for every fragment. GPUs have gotten pretty good at handling branches that are not divergent between different instances of a shader's execution, but as you introduce more branches you should consider making values such as hasBaseTexture and hasNormalMapTexture function constants and create different render pipelines for each case.
I appreciate it!

I am working on a local branch. Whoops. But I can promise you that I am lol

I am not sure what you mean by type but... My GPU is Intel Iris Plus Graphics 650 1536 MB. My computer is 13-inch, 2017 Macbook pro.

I did not think that the branching in the fragment shader would be too much of an issue, and at some point in time I remember reading that you don't want to switch render pipeline states too often as it can be costly.

It does seem like a bug though. Unless my fundamental understanding of Metal is broken :)

I will mark the previous answer as the correct answer as it seems that we have an implementation issue altogether.

Answer: Different Pipeline States || .hasWhateverTexture.

How do I submit a bug ticket?
Thanks. the GPU is indeed what I meant. The shader backed compiler is GPU specific. I was unable to reproduce the problem a simple use of is_null_texture on my own on an AMD GPU. So I the suspect Intel Iris compiler does not correctly implement is_null_texture.

Info on creating a bug report is here. If you respond to the thread with the bug number, I can route it to the proper engineers more quickly.

It is true that frequent switching between render pipelines will have some impact on CPU performance (more impact than switching textures and buffers). In general, especially if your rendering is GPU bound, it is preferable to switch render pipelines than adding a branch to your shaders. However, there is no one size fits all solution.