Dual Source Blending with Multiple Render Target link error - opengl

I am trying to implement text subpixel using dual-source blending.
https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_blend_func_extended.txt
layout(location = 0, index = 0) out vec4 fragColor;
layout(location = 0, index = 1) out vec4 srcColor1;
void main()
{
vec4 scolor0;
vec4 scolor1;
// some calulcations
fragColor = scolor0;
srcColor1 = scolor1;
}
It works fine.
But now I want to write to another render target
like
layout(location = 0, index = 0) out vec4 fragColor;
layout(location = 0, index = 1) out vec4 srcColor1;
layout(location = 1) out uvec4 myMRT;
and I tried changing location = 1 or location = 2 but in either case,I get this linking error
error: assembly compile error for fragment shader at offset
error: too many color outputs when using dual source output
Which is the best way to use MRT in case of Dual source blending?

The OpenGL 4.6 core profile specification states in section 17.3.6.3 "Dual Source Blending and Multiple Draw Buffers" (emphasis mine):
Blend functions that require the second color input
(Rs1;Gs1;Bs1;As1) (SRC1_- COLOR, SRC1_ALPHA, ONE_MINUS_SRC1_COLOR, or ONE_MINUS_SRC1_ALPHA)
may consume hardware resources that could otherwise be used for rendering to multiple draw buffers. Therefore, the number of draw
buffers that can be attached to a framebuffer may be lower when using
dual-source blending.
The maximum number of draw buffers that may be attached to a single framebuffer when using dual-source blending functions is
implementation-dependent and may be queried by calling GetIntegerv
with pname MAX_DUAL_SOURCE_DRAW_BUFFERS. When using dual-source
blending, MAX_DUAL_SOURCE_DRAW_BUFFERS should be used in place of
MAX_DRAW_BUFFERS to determine the maximum number of draw buffers
that may be attached to a single framebuffer. The value of
MAX_DUAL_SOURCE_DRAW_BUFFERS must be at least 1.
(The older ARB_blend_func_exteded extension you referred uses different wording, but also only guarantees a minumum of 1 for MAX_DUAL_SOURCE_DRAW_BUFFERS)
So even wit the most current GL spec, GL implementations are not required to support dual-source blending with multiple render targets at all. And looking at the current report for that capability on gpuinfo.org shows that there are no real word implementations supporting a value bigger than 1. So nope, you can't do this. At least that's the state of affairs in December 2020.

Related

Does dual-source blending require larger color buffer?

In OpenGL, we can turn on dual-source blending through following code in fragment shader:
layout(location = 0, index = 0) out vec4 color1;
layout(location = 0, index = 1) out vec4 color2;
And through token XX_SRC1_XX get color2 in blending functions. I have questions that:
If I want to do off-screen rendering, do I need to double the texture's storage's size as there are two colors output.
Is it that once I turn on dual-source blending then I can only output two colors to one buffer? And it means that I cannot bind more than one color buffers through attaching them to GL_COLOR_ATTACHMENTi tokens.
Is the qualifier 'index' here only used for dual-source blending purpose?
Dual-source blending means blending involving two color outputs from the FS. That has nothing to do with the nature of the images being written to (only the number). Location 0 still refers to the 0th index in the glDrawBuffers array, and the attachment works just as it did previously.
You are outputting two colors. But only one value (the result of the blend operation) gets written.
And it means that I cannot bind more than one color buffers through attaching them to GL_COLOR_ATTACHMENTi tokens.
Well, that depends on the number of attached images allowed when doing dual-source blending, as specified by your implementation. Granted, all known implementations set this to 1. Even Vulkan implementations all use 1 (or 0 if they don't support dual-source blending); the only ones that don't seem to be either early, broken implementations or software renderers,

Read write an image with the GL_ARB_fragment_shader_interlock extension

I am testing whether the GL_ARB_fragment_shader_interlock extension can execute the critical section code of the same pixel position in the order of instance rendering. I used instanced rendering to draw five translucent planes (instances in order from farthest to nearer) and the result is the same as the fixed pipeline blending result (the blending result is random without this extension). But one problem is that there will be an extra line in the middle of each plane (not when using fixed pipeline blending). I found that the adjacent sides of the triangle generating the fragments twice. But rasterization should ensure that adjacent triangles do not have overlapping pixels. How to solve this please? I don't know where I went wrong, here is the code and result, please enlighten me!
The GLSL code:
#version 450 core
#extension GL_ARB_fragment_shader_interlock : require
//out vec4 Color_;
in vec4 v2f_Color;
layout(binding = 0, rgba16f) uniform image2D uColorTex;
void main()
{
beginInvocationInterlockARB();
vec4 color = imageLoad(uColorTex, ivec2(gl_FragCoord.xy));
color = (1 - v2f_Color.a) * color + v2f_Color * v2f_Color.a;
imageStore(uColorTex, ivec2(gl_FragCoord.xy), color);
endInvocationInterlockARB();
//Color_ = v2f_Color;
}
Tthe result using extension to read write an image manually:
The result using fixed pipeline blending:
I am a bit suspicious of the ivec2(gl_FragCoord.xy). This is doing a conversion from floating-point to an integer, and it could be the case that it gets rounded in different directions between the different triangles. This might explain not only the overlap, but why there's a missing top-left pixel in one of the squares.
Judging by the spec, ivec2(gl_FragCoord.xy) should be equivalent to ivec2(trunc(gl_FragCoord.xy)), which really ought to be consistent, but maybe the implementation is bad...
You might want to try:
ivec2(round(gl_FragCoord.xy))
ivec2(round(gl_FragCoord.xy + 0.5))

Vulkan, why do validation layers (and by extension the spec) forbid pipelines from not writing to certain attachments?

In vulkan, if under the lifetime of a single render pass you naively render to a framebuffer that contains multiple attachemnts with a pipeline that renders to all of them and then render again with a pipeline that only renders to one of them you will get an error.
Let me give an example.
Consider the following image, which is an intermediary step in a multi pass effect.
Which is obtained from writing a wireframe on top of an albedo:
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) out vec4 color_out;
layout(location = 1) out vec4 color_out1;
layout(location = 2) out vec4 color_out2;
layout(location = 3) out vec4 color_out3;
layout(location = 4) out vec4 color_out4;
void main()
{
color_out = vec4(1,1,1,1);
color_out1 = vec4(0,0,0,0);
color_out2 = vec4(0,0,0,0);
color_out3 = vec4(0,0,0,0);
color_out4 = vec4(0,0,0,0);
}
The 4 "noop" outputs are not really necessary, they exist merely to prevent vulkan errors.
Let's assume we instead do this (and we modify our pieline as well):
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) out vec4 color_out;
void main()
{
color_out = vec4(1,1,1,1);
}
Then we obtain the same image.
However a critical difference exists:
The second image produces multiple errors, one for each attachemnt, which look like this:
Message ID name: UNASSIGNED-CoreValidation-Shader-InputNotProduced
Message: Attachment 1 not written by fragment shader; undefined values will be written to attachment
Severity: VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
Why is not explicitely writing to the attachments of a framebuffer not valid as per the spec? i.e why isn't the spec that if you do not write to an attachment, the contents are preserved?
why isn't the spec that if you do not write to an attachment, the contents are preserved?
Because Vulkan is a low-level rendering API.
What gets written to has always, in OpenGL just as in Vulkan, been governed by the write mask state, not anything the fragment shader does. If you have some number of attachments, again in OpenGL as well as Vulkan, any rendering operation will write to all of them (assuming the various tests are passed) unless write masks (or blending) are employed to prevent those writes.
Note that this distinction may well be a hardware issue. If a GPU uses specialized hardware to interpret fragment data and perform blending/write masking/etc, it is hardly unreasonable to consider that there may be no mechanism for the shader to directly communicate which values in the fragment are valid and which are not.
It appears that some hardware does handle this as you would prefer. Or at least in some subset of cases. However, as we're looking at unspecified behavior, there's no way to know what triggers it or in which cases it may not function.
Now, one might say that, given that the Vulkan pipeline object includes all of this state, the code which builds the internal pipeline state data could just detect which values get written by the FS and write-mask out the rest. But that's kind of against the point of being a low-level API.

Weird behaviour using mipmapping from glsl

I am trying to use mipmapping with vulkan. I do understand that I should use vkCmdBlit between each layer for each image, but before doing that, I just wanted to know how to change the layer in GLSL.
Here is what I did.
First I load and draw a texture (using layer 0) and there was no problem. The "rendered image" is the texture I load, so it is good.
Second, I use this shader (so I wanted to use the second layer (number 1)) but the "rendered image" does not change :
#version 450
layout(set = 0, binding = 0) uniform sampler2D tex;
in vec2 texCoords;
layout(location = 0) out vec4 outColor;
void main() {
outColor = textureLod(tex, texCoords, 1);
}
According to me, the rendered image should be changed, but not at all, it is always the same image, even if I increase the "1" (the number of the layer).
Third instead changing anything in the glsl code, I change the layer number into the ImageSubresourceRange to create the imageView, and the "rendered image" changed, so it seems normal to me and when I will use vkCmdBlit, I must see the original image in lower resolution.
The real problem is, when I try to use a mipmapping (through mipmapping) in GLSL, it does not affect at all the rendered image, but in C++ it does (and that seems fair).
here is (all) my source code
https://github.com/qnope/Vulkan-Example/tree/master/Mipmap
Judging by your default sampler creation info (https://github.com/qnope/Vulkan-Example/blob/master/Mipmap/VkTools/System/sampler.cpp#L28) you always set the maxLod member of your samplers to zero, so your lod is always clamped between 0.0 and 0.0 (minLod/maxLod). This would fit the behaviour you described.
So try setting the maxLod member of your sampler creation info to the actual number of mip maps in your texture and changing the lod level in the shader shoudl work fine.

In OpenGL, is there a way to blend based on a separate channel's value in the shader?

In OpenGL (not ES), is there a universal way to blend based a texture while drawing based on another texture or variable's value? On OpenGLES, I know that I can do custom blending on some platforms via extensions like GL_EXT_shader_framebuffer_fetch. The reason I ask, is that I have a special texture where the forth channel is not alpha, and I need to be able to blend it on a separate alpha which is available on a different map.
You want dual-source blending, which is available in core as of OpenGL 3.3. This allows you to provide a fragment shader with two outputs and use both of them in the blend function.
You would declare outputs in the fragment shader like this:
layout(location = 0, index = 0) out vec4 outColor;
layout(location = 0, index = 1) out vec4 outAlpha;
You could then set the blending function like this, for premultiplied alpha:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC1_COLOR);
Or non-premultiplied alpha:
glBlendFunc(GL_SRC1_COLOR, GL_ONE_MINUS_SRC1_COLOR);
Note that SRC1 here is the second output of the fragment shader. If I remember correctly, this blending will only work for one location.