Strange GLSL if condition behavior - macos

I am having a strange issue with a GLSL fragment shader on an 2020 Intel MacBook Pro with Intel Iris Plus graphics and MacOS Ventura 13. It seems that when I use an if block and don't have an else block, then the if block always executes.
I have stripped out everything else from the shader to validate it isn't something that is NaN/INFINITY, divide by zero, or something similar. I've also seen some people mention to not use == with float and even with only > or < it is still occurring. With everything stripped out the below code is what I am running:
#version 400
layout(location = 0)out vec4 gc_color;
void main(){
float stackptr = -1.0;
if(stackptr > 0.0) {
gc_color = vec4(1.0, 1.0, 0.0, 0.0);
} // this always runs the if block
}
When I run this, the output is always yellow no matter what I update stackptr to. The condition seems to always evaluate to true. But also when I update it with an else block like below it seems to work correctly.
if(stackptr > 0.0) {
gc_color = vec4(1.0, 1.0, 0.0, 0.0);
} else {
gc_color = vec4(1.0, 0.0, 1.0, 0.0);
} // this runs correctly
This will change the color based on what the value of stackptr is as you would expect.
I have also tried replacing "stackptr" in the condition with the value (-1.0) and that works as expected. So it seems like an issue with the variable being evaluated.
Has anyone seen this behavior before? Is it possibly a driver issue? I'll try it with an Nvidia GPU soon on Windows to check.
There is a similar issue here but no one has given an answer.

You observe undefined behaviour here. In your first shader you don't assign gc_color if condition is false, you can see everything. Let's consider how it can be processed by shader's compiler and GPU.
Shader's compiler eliminates condition, since stackptr is a constant (and it can be deduced in some cases). Result would be arbitrary, gc_color is never set.
Dynamic branching. Yellow color is assigned when condition is true, otherwise it's undefined (any color). Dynamic branching implies branch prediction and drop part of command's pipeline in case of miss. It can be less efficient than static branching for simple conditions.
Static branching. We calculate both branches (true and false) and choose one of them when condition is resolved. There are some preconditions for it (e.g. there should not be side-effects in branches), but your shader looks eligible for it. Some implementations execute true branch unconditionally. When condition is known and result is true, we can do a jump to a command after false block. If result is false, we allow to execute commands in false branch that rewrite what was set in true branch. In you case there is no false branch, so nothing is rewritten and you see yellow came from true branch.

Related

Will this anyhit shader ignore all hits?

I am learning DXR (DirectX Raytracing), and have a very simple question as follows:
I am using Unity's HDRP RTX, and I have a simple "anyhit" shader as below:
[shader("anyhit")]
void AnyHit(inout RayPayload payload : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes)
{
IgnoreHit();
}
I thought it should ignore all hits, and thus no "closesthit" shader will be executed. However, I still see "closesthit" shader executed. The behavior is same even if I delete this "anyhit" shader totally. Why?
IgnoreHit only ends the current anyhit shader.
https://learn.microsoft.com/en-us/windows/win32/direct3d12/any-hit-shader
To ignore closesthit shader, configure the flag to RAY_FLAG_SKIP_CLOSEST_HIT_SHADER in your TraceRay function.
https://learn.microsoft.com/en-us/windows/win32/direct3d12/ray_flag

Screen glitching in OS X Metal app? Error (IOAF code 1)?

I'm making an app on OS X Sierra using Metal. Something I am doing is causing the screen to start glitching badly, flashing black in various places, which quickly escalates to the entire screen going black.
In XCode, if I use the GPU frame capture, the paused frame appears correct however -- it suddenly returns from the black abyss. I don't see any errors or warnings in the GPU frame information. However, I am relatively new to Metal and am not experienced with the frame debugger, so I may not know what to look for.
Usually there is nothing printed to the console, but occasionally I do get one of these:
Execution of the command buffer was aborted due to an error during execution. Internal Error (IOAF code 1)
The same app runs on iOS devices without this problem -- so far it only happens on OS X. Does this sound familiar? Any suggestions on what I should check?
I can post some code if it will be helpful, but right now I'm not sure what part of the program is the problem.
EDIT: In response to Noah Witherspoon -- it seems that the problem is caused by some kind of interaction between my scene drawing and the UI drawing. If I display only my scene, which is composed of fat, fuzzy lines, then the problem does not occur. It also does not occur if I display only the UI, which is orthographic projection, a bunch of rounded rectangles and some type. The problem happens only when both are showing. This is a lot of code, many buffers and a lot of commandBuffer usage, too much to put into a post. But here is a little bit.
My lines are rendered with vertex buffers which are arrays of floats, four per vertex:
let dataSize = count * 4 * MemoryLayout<Float>.size
vertexBuffer = device.makeBuffer(bytes: points, length: dataSize, options: MTLResourceOptions())!
These are rendered like this:
renderEncoder.setVertexBuffer(self.vertexBuffer, offset: 0, index: 0)
renderEncoder.setRenderPipelineState(strokeNode.strokePipelineState)
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: Int(widthCountEdge.count)*2-4)
renderEncoder.setRenderPipelineState(strokeNode.capPipelineState)
renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 12)
Here's my main loop for drawing with the command Buffer.
if let commandBuffer = commandQueue.makeCommandBuffer() {
commandBuffer.addCompletedHandler { (commandBuffer) -> Void in
self.strokeNode.bufferProvider.availableResourcesSemaphore.signal()
}
self.updateDynamicBufferState()
self.updateGameState(timeInterval)
let renderPassDescriptor = view.currentRenderPassDescriptor
renderPassDescriptor?.colorAttachments[0].loadAction = .clear
renderPassDescriptor?.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
renderPassDescriptor?.colorAttachments[0].storeAction = .store
if let renderPassDescriptor = renderPassDescriptor, let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) {
strokeNode.subrender(renderEncoder, parentModelViewMatrix: viewMatrix, projectionMatrix: projectionMatrix, renderer:self)
mainscreen.draw(renderEncoder);
renderEncoder.endEncoding()
if let drawable = view.currentDrawable {
commandBuffer.present(drawable)
}
}
commandBuffer.commit()
}
The line drawing happens in strokeNode.subrender, and then my UI drawing happens in mainscreen.draw. The UI drawing has a lot of components - a lot to list here -- but I will try taking them out one by one and see if I can narrow it down. If none of this looks problematic I'll edit and post some of that ...
Thanks!

Is opencl known to generate corrupt code?

I have a small opencl kernel that writes to a shared GL texture. I have separated different stages of the computation into several functions. Every function gets a pointer to the final color and passes this along if needed. If you look at the code fragment you see a line that is called "UNREACHABLE". For some reason it does get executed. What ever color I put in there appears in the final image. How is that possible?
If I duplicate the same code block below that does not happen. Only for the first one. :(
To make things even funnier, if I change the code above (e.g. add another multiplication) the UNREACHABLE line gets executed at random.
Therefore my questions: Is this a compiler bug? Do I exhaust a certain memory or register budged that I should be aware of? Are OpenCL compilers buggy in general?
void sample(float4 *color) {
...
float4 r_color = get_color(...);
float factor = r_color.w + (*color).w - 1.0f;
r_color = r_color * ((r_color.w - factor) / r_color.w);
*color += r_color;
if(color->w >= 1.0f) {
if(color->w <= 0.0f) {
(*color) = (float4)(0.0f, 0.0f, 0.0f, 1.0f); //UNREACHABLE?
return;
}
}
...
}
...
__kernel void render(
__write_only image2d_t output_buffer,
int width,
int height
) {
uint screen_x = get_global_id(0);
uint screen_y = get_global_id(1);
float4 color = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
sample(&color);
write_imagef(output_buffer, (int2)(screen_x, screen_y), color);
}
My Platform:
Apple
Intel(R) Core(TM) i5-2415M CPU # 2.30GHz
selected device has extensions: cl_APPLE_SetMemObjectDestructor cl_APPLE_ContextLoggingFunctions cl_APPLE_clut cl_APPLE_query_kernel_names cl_APPLE_gl_sharing cl_khr_gl_event cl_khr_fp64 cl_khr_global_int32_base_atomics cl_khr_global_int32_extended_atomics cl_khr_local_int32_base_atomics cl_khr_local_int32_extended_atomics cl_khr_byte_addressable_store cl_khr_int64_base_atomics cl_khr_int64_extended_atomics cl_khr_3d_image_writes cl_khr_image2d_from_buffer cl_APPLE_fp64_basic_ops cl_APPLE_fixed_alpha_channel_orders cl_APPLE_biased_fixed_point_image_formats cl_APPLE_command_queue_priority
[Edit]
After observing the values I get during the calculation I am thinking r_color.w being exactly 0.0f after get_color may cause the problem. I am still looking for a definite statement that says comparing a NaN is not defined or is always true or something.
Also "Is opencl known to generate corrupt code?" has the invisible postfix "or what am I missing".
I used to work with embedded systems where the vendor would provide their own proprietary compilers, which in turn were known to break your code. So I want to get this off the table if possible. I suspect that clang would not do that. But you never know.

WebGL context can't render simplest screen

I'm stuck trying to render some extremely basic stuff on webgl, I've dumbed down the rendering to the most basic thing I can think of in order to find where the issue lies, but I can't even draw a simple square for some reason. The scene I really want to render is more complex, but as I said, I've dumbed it down to try to find the problem and still no luck. I'm hoping someone can take a look and find whatever I'm missing, wich I assume is a setup step at some point.
The gl commands I'm running (as reported by webgl inspector, without errors) are:
clearColor(0,0,0,1)
clearDepth(1)
clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT)
useProgram([Program 2])
bindBuffer(ARRAY_BUFFER, [Buffer 5])
vertexAttribPointer(0, 2, FLOAT, false, 0, 0)
drawArrays(TRIANGLES, 0, 6)
The buffer that is being used there (Buffer 5) is setup as follows:
bufferData(ARRAY_BUFFER, [-1,-1,1,-1,-1,1,-1,1,1,-1,1,1], STATIC_DRAW)
And the program (Program 2) data is:
LINK_STATUS true
VALIDATE_STATUS false
DELETE_STATUS false
ACTIVE_UNIFORMS 0
ACTIVE_ATTRIBUTES 1
Vertex shader:
#ifdef GL_ES
precision highp float;
#endif
attribute vec2 aPosition;
void main(void) {
gl_Position = vec4(aPosition, 0, 1);
}
Fragment shader:
#ifdef GL_ES
precision highp float;
#endif
void main(void) {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
Other state I think could be relevant:
CULL_FACE false
CULL_FACE_MODE BACK
FRONT_FACE CCW
BLEND false
DEPTH_TEST false
VIEWPORT 0, 0 640 x 480
SCISSOR_TEST false
SCISSOR_BOX 0, 0 640 x 480
COLOR_WRITEMASK true,true,true,true
DEPTH_WRITEMASK true
STENCIL_WRITEMASK 0xffffffff
FRAMEBUFFER_BINDING null
What I expected to see from that setup/commands is a red quad taking up the whole clip space, but what I see is simply the cleared screen, as the drawArrays doesn't seem to be doing anything. Can anybody spot what I'm missing? Any tips on how to debug this would be very welcome too!
Here:
bufferData(ARRAY_BUFFER, [-1,-1,1,-1,-1,1,-1,1,1,-1,1,1], STATIC_DRAW)
replace to:
bufferData(ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]), STATIC_DRAW)
Because webgl doesn't know which type you are passing here (integer or float or byte). Example:
http: jsfiddle.net/9QxAz/
After reading #user1724911's fiddle, I found out what I missed is enabling the vertex attribute array - stupidly simple mistake. I'm actually surprised I didn't get any warning from webgl inspector about this, but the solution was simply to add a call to enable that attribute:
gl.enableVertexAttribArray(program.attributes.aPosition);

NULL checks in GLSL

I'm trying to check inside the shader (GLSL) if my vec4 is NULL. I need this for several reasons, mostly to get specific graphics cards compatible, since some of them pass a previous color in gl_FragColor, and some don't (providing a null vec4 that needs to be overwritten).
Well, on a fairly new Mac, someone got this error:
java.lang.RuntimeException: Error creating shader: ERROR: 0:107: '==' does not operate on 'vec4' and 'int'
ERROR: 0:208: '!=' does not operate on 'mat3' and 'int'
This is my code in the fragment shader:
void main()
{
if(gl_FragColor == 0) gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); //Line 107
vec4 newColor = vec4(0.0, 0.0, 0.0, 0.0);
[...]
if(inverseViewMatrix != 0) //Line 208
{
[do stuff with it; though I can replace this NULL check with a boolean]
}
[...]
gl_FragColor.rgb = mix(gl_FragColor.rgb, newColor.rgb, newColor.a);
gl_FragColor.a += newColor.a;
}
As you can see, I do a 0/NULL check for gl_FragColor at the start, because some graphics cards pass valuable information there, but some don't. Now, on that special mac, it didn't work. I did some research, but couldn't find any information on how to do a proper NULL check in GLSL. Is there even one, or do I really need to make separate shaders here?
All variables meant for reading, i.e. input variables always deliver sensible values. Being an output variable, gl_FragColor is not one of these variables!
In this code
void main()
{
if(gl_FragColor == 0) gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); //Line 107
vec4 newColor = vec4(0.0, 0.0, 0.0, 0.0);
The very first thing you do is reading from gl_FragColor. The GLSL specification clearly states, that the value of an output varialbe as gl_FragColoris, is undefined when the fragment shader stage is entered (point 1):
The value of an output variable will be undefined in any of the three following cases:
At the beginning of execution.
At each synchronization point, unless
the value was well-defined after the previous synchronization
point and was not written by any invocation since, or
the value was written by exactly one shader invocation since the
previous synchronization point, or
the value was written by multiple shader invocations since the
previous synchronization point, and the last write performed
by all such invocations wrote the same value.
When read by a shader invocation, if
the value was undefined at the previous synchronization
point and has not been writen by the same shader invocation since, or
the output variable is written to by any other shader
invocation between the previous and next synchronization points,
even if that assignment occurs in code following the read.
Only after an element of an output variable has been written to for the first time its value is defined. So the whole thing you do there makes no sense. That it "didn't work" is completely permissible and an error on your end.
You're invoking undefined behaviour and technically it would be permissible for your computer to become sentinent, chase you down the street and erase all of your data as an alternative reaction to this.
In GLSL a vec4 is a regular datatype just like int. It's not some sort of pointer to an array which could be a null pointer. At best it has some default value that's not being overwritten by a call to glUniform.
Variables in GLSL shaders are always defined (otherwise, you'll get a linker error). If you don't supply those values with data (by not loading the appropriate uniform, or binding attributes to in or attribute variables), the values in those variables will be undefined (i.e., garbage), but present.
Even if you can't have null values, you can test undefined variables. This is a trick that I use to debug my shaders:
...
/* First we test for lower range */
if(suspect_variable.x < 0.5) {
outColour = vec4(0,1,0,0); /* Green if in lower range*/
} else if(suspect_variable.x >= 0.5) { /*Then for the higher range */
outColour = vec4(1,0,0,0); /* Red if in higher range */
} else {
/* Now we have tested for all real values.
If we end up here we know that the value must be undefined */
outColour = vec4(0,0,1,0); /* Blue if it's undefined */
}
You might ask, what could make a variable undefined? Out of range access of an array would cause it to be undefined;
const int numberOfLights = 2;
uniform vec3 lightColour[numberOfLights];
...
for(int i = 0; i < 100; i++) {
/* When i bigger than 1 the suspect_variable would be undefined */
suspect_variable = suspect_variable * lightColour[i];
}
It is a simple and easy trick to use when you do not have access to real debugging tools.

Resources