Writing to a single colour channel with image_load_store - opengl

The image_load_store extension provides load/store functions with *vec4 access only. If I have layout(rgba32f) uniform image2D myimage; for example, it seems like I have to write to the entire pixel at once:
imageStore(myimage, coord, vec4(1,1,1,1));
and if I want to set only the red channel to 1, I need to do this:
vec4 pixel = imageLoad(myimage, coord);
pixel.r = 1;
imageStore(myimage, coord, pixel);
which introduces a fair amount of overhead with the extra read.
Is there a mechanism I can use to write only to a specific channel?
Something similar to glColorMask, or perhaps binding the data with a different format such as a larger GL_R32F image2D or imageBuffer? If so, what are some of the overhead implications (e.g. conversion/transfer/cache issues)?

Related

What is the relationship with index in glBindBufferBase() and stream number in geometry shader in OpenGL?

I want to transform data from geometry shaders to feedback buffer,so I set the names of variables like this:
char *Varying[] = {"lColor","lPos","gl_NextBuffer","rColor","rPos"};
then bind two buffers vbos[2] to transformFeedback object Tfb,vbos[0] to bind point 0, and vbos[1] to bind point 2:
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbos[0]);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, vbos[1]);
and set stream number like this:
layout(stream=0) out vec4 lColor;
layout(stream=0) out vec4 lPos;
layout(stream = 2) out rVert
{
vec4 rColor;
vec4 rPos;
};
So I thought lColor and lPos are transformed to vbos[0],rColor and rPos are transformed to vbos[1],but nothing came out,unless I change vbos[1] to bind point 1,and stream number of rvert to 1.
I think there is three variables about indexes for binding geometry shader output and feedback buffers,first is index in glBindBufferBase(target,index,id),second is sequences of names in glTransformFeedbackVaryings(program,count,names,buffermode),third is stream number of variables in geometry shader,what is the relationship of these three parameters?
And I found that no matter what parameters I set the glDrawTransformFeedbackStream ,picture didn't change,Why?
The geometry shader layout qualifier stream has no direct relationship to the TF buffer binding index assignment. There is one caveat here: variables from two different streams can't be assigned to the same buffer binding index. But aside from that, the two have no relationship.
In geometry shader transform feedback, streams are written independently of each other. When you output a vertex/primitive, you say which stream the GS invocation is writing. Only the output variables assigned to that stream are written, and the system keeps track of how much stuff has been written to each stream.
But how the output variables map to feedback buffers is entirely separate (aside from the aforementioned caveat).
In your example, glTransformFeedbackVaryings with {"lColor","lPos","gl_NextBuffer","rColor","rPos"} does the following. It starts with buffer index 0. It assigns lColor and lPos to buffer index 0. gl_NextBuffer causes the system to increment the value of the current buffer index. That value is 0, so incrementing it makes it 1. It then assigns rColor and rPos to buffer 1.
That's why your code doesn't work.
You can skip buffer indices in the TF buffer bindings. To do that, you have to use gl_NextBuffer twice, since each use increments the current buffer index.
Or if your GL version is high enough, you can just assign the buffer bindings and offsets directly in your shader:
layout(stream=0, xfb_offset = 0, xfb_buffer = 0) out vec4 lColor;
layout(stream=0, xfb_offset = 16, xfb_buffer = 0) out vec4 lPos;
layout(stream = 2, xfb_offset = 0, xfb_buffer = 2) out rVert
{
vec4 rColor;
vec4 rPos;
};

Dynamic indexing into uniform array of sampler2D doesn't work

I need to index into array of 2 uniform sampler2D. The index is dynamic per frame.That's,I have a dynamic uniform buffer which provides that index to a fragment shader. I use Vulkan API 1.2. In device feature listing I have:
shaderSampledImageArrayDynamicIndexing = 1
I am not sure 100% but It looks like this feature is core in 1.2.Nevertheless I did try to enable it during device creation like this:
VkPhysicalDeviceFeatures features = {};
features.shaderSampledImageArrayDynamicIndexing = VK_TRUE;
Then plugging into device creation:
VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pEnabledFeatures = &features ;
deviceCreateInfo.enabledExtensionCount = NUM_DEVICE_EXTENSIONS;
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
In the shader it looks like this:
layout(std140,set=0,binding=1)uniform Material
{
vec4 fparams0;
vec4 fparams1;
uvec4 iparams; //.z - array texture idx
uvec4 iparams1;
}material;
layout (set=1,binding = 0)uniform sampler2D u_ColorMaps[2];
layout (location = 0)in vec2 texCoord;
layout(location = 0) out vec4 outColor;
void main()
{
outColor = texture(u_ColorMaps[material.iparams.z],texCoord);
}
What I get is a combination of image pixels with some weird color. If I change to fixed indices - it works correctly. material.iparams.z param has been verified,it provides correct index number every frame (0 or 1). No idea what else is missing.Validation layers say nothing.
Mys setup: Windows, RTX3000 ,NVIDIA beta driver 443.41 (Vulkan 1.2)
Update:
I also found that dynamically indexed sampler return a value in Red channel (r)
which is close to one and zeros in GB. I don't set red color anyway,also the textures I fetch don't contain red. Here are two sreenshot, the upper is correct result which I get when indexing with constant value. Second is what happens when I index with dynamic uint which comes from dynamic UBO:
Correct
Wrong
The problem was due to usage of Y′CBCR samplers. It appears that Vulkan disallows indexing dynamically into array of such uniforms.
Here is what Vulkan specs says:
If the combined image sampler enables sampler Y′CBCR conversion or
samples a subsampled image, it must be indexed only by constant
integral expressions when aggregated into arrays in shader code,
irrespective of the shaderSampledImageArrayDynamicIndexing feature.
So,the solution for me was to provide two separately bound samplers and use dynamic indices with if()..else condition to decide which sampler to use. Push constants would also work,but in this case I have to re-record command buffers all the time. Hopefully this info will be helpful to other people working with video formats in Vulkan API.

Pass array of floats to fragment shader via texture

I am trying to pass an array of floats (in my case an audio wave) to a fragment shader via texture. It works but I get some imperfections as if the value read from the 1px height texture wasn't reliable.
This happens with many combinations of bar widths and amounts.
I get the value from the texture with:
precision mediump float;
...
uniform sampler2D uDisp;
...
void main(){
...
float columnWidth = availableWidth / barsCount;
float barIndex = floor((coord.x-paddingH)/columnWidth);
float textureX = min( 1.0, (barIndex+1.0)/barsCount );
float barValue = texture2D(uDisp, vec2(textureX, 0.0)).r;
...
If instead of the value from the texture I use something else the issue doesn't seem to be there.
barValue = barIndex*0.1;
Any idea what could be the issue? Is using a texture for this purpose a bad idea?
I am using Pixi.JS as WebGL framework, so I don't have access to low level APIs.
With a gradient texture for the data and many bars the problems becomes pretty evident.
Update: Looks like the issue relates to the consistency of the value of textureX.
Trying different formulas like barIndex/(barsCount-1.0) results in less noise. Wrapping it on a min definitely adds more noise.
Turned out the issue wasn't in reading the values from the texture, but was in the drawing. Instead of using IFs I switched to step and the problem went away.
vec2 topLeft = vec2(
paddingH + (barIndex*columnWidth) + ((columnWidth-barWidthInPixels)*0.5),
top
);
vec2 bottomRight = vec2(
topLeft.x + barWidthInPixels,
bottom
);
vec2 tl = step(topLeft, coord);
vec2 br = 1.0-step(bottomRight, coord);
float blend = tl.x * tl.y * br.x * br.y;
I guess comparisons of floats through IFs are not very reliable in shaders.
Generally mediump is insufficient for texture coordinates for any non-trivial texture, so where possible use highp. This isn't always available on some older GPUs, so depending on the platform this may not solve your problem.
If you know you are doing 1:1 mapping then also use GL_NEAREST rather than GL_LINEAR, as the quantization effect will more likely hide some of the precision side-effects.
Given you probably know the number of columns and bars you can probably pre-compute some of the values on the CPU (e.g. precompute 1/columns and pass that as a uniform) at fp32 precision. Passing in small values between 0 and 1 is always much better at preserving floating point accuracy, rather than passing in big values and then dividing out.

Access GLSL Uniforms after shaders have used them

I've noticed that my shaders are performing a calculation that I need in the CPU code. Is it possible for me to load that results of that calculation into a uniform array, and then access that uniform from the CPU once the GPU has finished working?
You can write arbitrary amounts of data through either Image Load/Store or SSBOs. While the number of image variables is restricted in image load/store, those variables can refer to buffer textures or array textures. Either of which give you access to a more-or-less arbitrarily large amount of data to write to:
layout(rgba32f, writeOnly) imageBuffer buffer;
imageStore(buffer, valueOffset1, value1);
imageStore(buffer, valueOffset2, value2);
imageStore(buffer, valueOffset3, value3);
imageStore(buffer, valueOffset4, value4);
SSBOs make this even easier:
layout(std430) buffer Data
{
float giantArray[];
};
giantArray[valueOffset1] = data1;
giantArray[valueOffset2] = data2;
giantArray[valueOffset3] = data3;
giantArray[valueOffset4] = data4;
However, note that any such writes will be unordered with regard to writes from other shader invocations. So overwriting such data will be... problematic. And you'll need an appropriate glMemoryBarrier call before you try to read from it.
But if all you're doing is a compute operation, you ought to be using dedicated compute shaders.
As far as i know, there is no way of retreiving uniform data from your GPU. But you could execute the calculation and set the output color to something you can identify on your screen depending on the expected result of your calculation. For exmaple:
#version 330 core
layout(location = 0) out vec4 color;
void main() {
if( Something you're trying to debug )
color = vec4(1, 1, 1, 1);
else
color = vec4(0, 0, 0, 1);
}
That's the only way I know of, and I use it all the time.

32bit (int) Buffer to Greyscale/Colour-mapped Image in OpenGL, Single Channel 32 bit Texture or TBO?

I have an int buffer of intensity values, I want to display this as a greyscale/colour-mapped image in OpenGL.
What is the best way to achieve this?
Standard Texture?
Can I do it via a standard glTexture, so something like:
gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, OpenGL.GL_R32f, width, height, 0, OpenGL.GL_RED_INTEGER, OpenGL.GL_UNSIGNED_INT, pixels);
In the shader I am under the impression I would use it the same as any other texture except I would use usampler2D instead of sampler2D, at which point I would get the true integer value (i.e. not 0-1 range).
TBO?
Or would it be better to achieve with a TBO and do something like:
gl.TexBuffer(OpenGL.GL_TEXTURE_BUFFER, OpenGL.GL_R32F, bufferID);
In terms of the shader I am actually quite confused. I have seen things like g = texelFetch(u_tbo_tex, offset + 1).r.. So I am guessing I would have to translate the texture coordinates into an offset, something like:
int offset = tex_coord.s + (tex_coord.t * imageWidth);
but then texelFetch actually returns a vec4, so presumably I would use:
int intensity = texelFetch( buffer, offset).r
But then as tex_coord.s & t are in 0-1, that would imply the need to:
int offset = tex_coord.s*imageHeight + ((tex_coord.t * imageWidth) * imageWidth);
Other Buffer
I have very little experience with buffer objects I feel like really all I am doing is using a buffer in GL....so I do feel like I am over complicating it and I am missing the "penny drop".
Important Notes
Why Int? : In some cases I do some manipulation on the data before turning into a colour and would prefer to do this at 32 bit precision to avoid potential precision errors. Arguably it might not make a difference as it eventually becomes a screen color...
Data update frequency: the intensity data is updated occasionally by user events but certainly not multiple times per frame (so I am presuming STATIC is more appropriate then DYNAMIC in this case?)
Use: The data is mainly for GL so _DRAW There is the possibility that the application could make use of GL to compute some values for it but I would probably create a separate READ buffer in this case
The highest integer value I have seen so far is "90,000" so I know it goes out of the 16 bit integer range.
Note: I am doing this through SharpGL and I have been unable to test at the moment as it has no definition for GL_R32f, so I shall have to find the gl.h on my windows platform (always fun) and add the correct const number*
You can use a normal texture with integer/unsigned integer format:
gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, OpenGL.GL_R32UI, width, height, 0, OpenGL.GL_RED_INTEGER, OpenGL.GL_UNSIGNED_INT, pixels);
In the shader you can use a usampler2D, since the texture function has an overload for this you directly get the integer values:
uniform usampler myUTexture;
uint value = texture(myUTexture, texCoord).r;
Edit:
Just for completness: texelFetch has also an overload for all types of 2d-sampler. The difference between texture and texelFetch is the coordinate system used ([0,1] for texture and pixel coordinates for texelFetch) and that texelFetch does not take any interpolation/mipmap into account.