What happens when I have a uint32 (R32) texture and I sample/fetch from it in a shader? - casting

Note: I distinguish sample from fetch in the title, since in my tests this behavior seems to differ between the two.
Possible answers:
The [0..2^32] range is scaled down to [0..1] (I think OpenGL works this way)
The uint32s are casted to float32s (which, incidentally, means that some precision is lost).
Other?

I made a test with renderdoc.
When using fetch, and the texture is defined with Texture2D<uint>, the fetch functions simply return a uint instead of a float4.

I assume that your texture/view format is R32_UINT.
If you do :
uint value = texture.Load(pixelLocation);
it will return you uint (just exact same value)
If you do :
float value = texture.Load(pixelLocation);
It will perform a cast (potential precision issue)
If you try to sample, it will do other, as sampling on uint format is normally not allowed (can go from wrong value to driver crash).
Case 1 (scaled down range): is not available with R32, only with R16 or R8
in that case you need to use DXGI_FORMAT_R16_UNORM instead of UINT for the shader view (or texture format). Sample of course works in that case.

Related

Write a RGBA8 image as a R32UI

I have an image with the format R8G8B8A8 (Unorm).
I want to write uint data on it (to be able to use atomic function).
So, when I want to "write" it, I am using in the glsl :
layout(set = 0, binding = 2, r32ui) restrict writeonly uniform uimage3D dst;
However, when I am performing something like
imageStore(dst, coords, uvec4(0xffffffff));
RenderDoc (and my app as well) tells me that all my values are 0 (instead of 1.0 (255 unorm)).
If I replace the r32ui by rgba8 everything works fine but I can not use atomic values. So I wonder if it is possible to do such thing. (However, if I use a r32f instead of rgba8, it works fine as well).
Do you have any solution?
Vulkan specification guarantees that atomic operations must be supported for storage images (VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT) with only R32_UINT and R32_SINT formats. Implementations may add such support for other formats as well but it's not obligatory. So it's nothing strange that atomic operations don't work with rgba8 format.
Next. You can create an image view with a different format than the format of the image. In such case the image view's format must be compatible with the image's format. In case of the R8G8B8A8 format, both SINT and UINT R32 formats are compatible (have the same number of bits). But to be able to create an image view with a different format, image itself must be created with a VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT flag.
One last thing - there is a note in the specification about format compatibility/mutable images:
Values intended to be used with one view format may not be exactly
preserved when written or read through a different format. For
example, an integer value that happens to have the bit pattern of a
floating point denorm or NaN may be flushed or canonicalized when
written or read through a view with a floating point format.
Similarly, a value written through a signed normalized format that has
a bit pattern exactly equal to -2^b may be changed to -2^b + 1 as
described in Conversion from Normalized Fixed-Point to Floating-Point.
Maybe this is the problem? Though it seems that there should be no conversion between rgba8 (unorm) and r32 (uint). Did validation layers report any warnings or errors? What layout is Your image in when You try to store data in it? Don't forget that:
Load and store operations on storage images can only be done on images
in the VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR or VK_IMAGE_LAYOUT_GENERAL
layout.

The Parameter's precision of OpenGL function glColor4d

I have problems with glcolor4d(). Since the data size is very large and I used
glBlendFuncSeparate(GL_SRC_ALPHA, GL_DST_ALPHA, GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
to meet my needs, I set the the parameter with
glColor4d(0.05, 0.05, 0.05, 0.04)
(the Parameters as small as possible). Once the alpha value and RGB parameters are lower than some values like 0.04, the data coundn't be display on the window.
So is there any limitations about the function precision? And how can I resovle this problem to set the parameter as small as possible? Hoping your reply.
Pretty much all of the d versions of the vertex attribute functions are utterly useless.
Your framebuffer does not have double-precision floats. The best you can do is use single-precision floats, but that requires using single-precision floats, which is not the default. So you need to use an FBO to render to such an image, which you probably aren't doing. The general default pixel format you'll get is 8-bits-per-pixel.
And that's ignoring whether the fixed-function pipeline will operate on floating-point values. It is just as likely to turn them into 8-bits-per-pixel values (since that's what colors are defined to be in the FFP).
If you want higher precision for your colors, then you need to use shaders.

Rendering a bit array with OpenGL

I have a bit array representing an image mask, stored in a uint8_t[] container array, in row first order. Hence, for each byte, I have 8 pixels.
Now, I need to render this with OpenGL ( >= 3.0 ). A positive bit is drawn as a white pixel and a negative bit is drawn as a black pixel.
How could I do this? Please
The first idea that comes to mind is to develop a specific shader for this. Can anyone give some hints on that?
You definitely must write a shader for this. First and foremost you want to prevent the OpenGL implementation to reinterpret the integer bits of your B/W bitmap as numbers in a certain range and map them to [0…1] floats. Which means you have to load your bits into an integer image format. Since your image format is octet groups of binary pixels (byte is a rather unspecific term and can refer to any number of bits, though 8 bits is the usual), a single channel format 8 bits format seems the right choice. The OpenGL-3 moniker for that is GL_R8UI. Keep in mind that the "width" of the texture will be 1/8th of the actual width of your B/W image. Also for unnormalized access you must use a usampler (for unsigned) or an isampler (for signed) (thanks #derhass for noticing that this was not properly written here).
To access individual bits you use the usual bit manipulation operators. Since you don't want your bits to become filtered, texel fetch access must be used. So to access the binary pixel at integer location x,y the following would be used.
uniform usampler2D tex;
uint shift = x % 8;
uint mask = 1 << shift;
uint octet = texelFetch(tex, ivec2(x/8,y)).r;
value = (octet & mask) >> shift;
The best solution would be to use a shader, you could also hack something like this:
std::bitset<8> bits = myuint;
Then get the values of the single bits with bits.at(position) and finally do a simple point drawing.

How do images work in opencl kernel?

I'm trying to find ways to copy multidimensional arrays from host to device in opencl and thought an approach was to use an image... which can be 1, 2, or 3 dimensional objects. However I'm confused because when reading a pixle from an array, they are using vector datatypes. Normally I would think double pointer, but it doesn't sound like that is what is meant by vector datatypes. Anyway here are my questions:
1) What is actually meant to vector datatype, why wouldn't we just specify 2 or 3 indices when denoting pixel coordinates? It looks like a single value such as float2 is being used to denote coordinates, but that makes no sense to me. I'm looking at the function read_imageui and read_image.
2) Can the input image just be a subset of the entire image and sampler be the subset of the input image? I don't understand how the coordinates are actually specified here either since read_image() only seams to take a single value for input and a single value for sampler.
3) If doing linear algebra, should I just bite the bullet and translate 1-D array data from the buffer into multi-dim arrays in opencl?
4) I'm still interested in images, so even if what I want to do is not best for images, could you still explain questions 1 and 2?
Thanks!
EDIT
I wanted to refine my question and ask, in the following khronos documentation they define...
int4 read_imagei (
image2d_t image,
sampler_t sampler,
int2 coord)
But nowhere can I find what image2d_t's definition or structure is supposed to be. The samething for sampler_t and int2 coord. They seem like structs to me or pointers to structs since opencl is supposed to be based on ansi c, but what are the fields of these structs or how do I note the coord with what looks like a scala?! I've seen the notation (int2)(x,y), but that's not ansi c, that looks like scala, haha. Things seem conflicting to me. Thanks again!
In general you can read from images in three different ways:
direct pixel access, no sampling
sampling, normalized coordinates
sampling, integer coordinates
The first one is what you want, that is, you pass integer pixel coordinates like (10, 43) and it will return the contents of the image at that point, with no filtering whatsoever, as if it were a memory buffer. You can use the read_image*() family of functions which take no sampler_t param.
The second one is what most people want from images, you specify normalized image coords between 0 and 1, and the return value is the interpolated image color at the specified point (so if your coordinates specify a point in between pixels, the color is interpolated based on surrounding pixel colors). The interpolation, and the way out-of-bounds coordinates are handled, are defined by the configuration of the sampler_t parameter you pass to the function.
The third one is the same as the second one, except the texture coordinates are not normalized, and the sampler needs to be configured accordingly. In some sense the third way is closer to the first, and the only additional feature it provides is the ability to handle out-of-bounds pixel coordinates (for instance, by wrapping or clamping them) instead of you doing it manually.
Finally, the different versions of each function, e.g. read_imagef, read_imagei, read_imageui are to be used depending on the pixel format of your image. If it contains floats (in each channel), use read_imagef, if it contains signed integers (in each channel), use read_imagei, etc...
Writing to an image on the other hand is straightforward, there are write_image{f,i,ui}() functions that take an image object, integer pixel coordinates and a pixel color, all very easy.
Note that you cannot read and write to the same image in the same kernel! (I don't know if recent OpenCL versions have changed that). In general I would recommend using a buffer if you are not going to be using images as actual images (i.e. input textures that you sample or output textures that you write to only once at the end of your kernel).
About the image2d_t, sampler_t types, they are OpenCL "pseudo-objects" that you can pass into a kernel from C (they are reserved types). You send your image or your sampler from the C side into clSetKernelArg, and the kernel gets back a sampler_t or an image2d_t in the kernel's parameter list (just like you pass in a buffer object and it gets a pointer). The objects themselves cannot be meaningfully manipulated inside the kernel, they are just handles that you can send into the read_image/write_image functions, along with a few others.
As for the "actual" low-level difference between images and buffers, GPU's often have specially reserved texture memory that is highly optimized for "read often, write once" access patterns, with special texture sampling hardware and texture caches to optimize scatter reads, mipmaps, etc..
On the CPU there is probably no underlying difference between an image and a buffer, and your runtime likely implements both as memory arrays while enforcing image semantics.

Using ImageMagick++ to modify image contrast/brightness

I'm trying to apply contrast and brightness to a bitmap in memory and I'm completely lost. Currently I'm trying to use Magick++ to do it, but if one of the other APIs would work better I'm all ears. I managed to find Magick::Image::sigmoidalContrast() for applying the contrast, but I can't figure out how to get it to work. I'm creating an image, passing it the buffer pointer, then calling that function, but it doesn't seem like it's changing anything so my first though was that it's making a copy and modifying that. Even so, I have no idea how to get the data out of the Magick::Image object.
Here's what I got so far.
Magick::Image image(fBitmapData->mGetTextureWidth(), fBitmapData->mGetTextureHeight(), "RGBA", MagickCore::CharPixel, pixels);
image.sigmoidalContrast(1, 20.0);
The documentation is useless and after searching I could only find hints that the first parameter is actually a boolean, even though it takes a size_t, that specifies whether to add or subtract the contrast, and the second value is something I have no idea what to pass so I'm just using 20.0 to test.
So does anyone know if this will work for contrast, and if not, then how do you apply contrast? And likewise I still have no idea how to apply brightness either and can't find any functions that look like they would work.
Figured it out; The function for contrast I was using was correct, and for brightness I ended up using image.modulate(brightness, 100.0, 100.0);. To get the data out of the image object you can grab the pixels of the entire image by doing
const MagickCore::PixelPacket * magickPixels = image.getConstPixels(0, 0, image.columns(), image.rows());
And then copy the magickPixels data back into the original pixels that were passed into the image constructor. An important thing to note is that the member MagickCore::PixelPacket::opacity is not what you would think it would be. If the pixel is completely transparent you'd think the value would be 0, right? Well for some reason ImageMagick is doing it opposite. So for full transparency the value would be 255. This means you need to do 255 - opacity to get the correct value.
Also be careful of the MAGICKCORE_QUANTUM_DEPTH that ImageMagick was compiled with, as this will change the values drastically. For my code MAGICKCORE_QUANTUM_DEPTH just happened to be defined as 16 so all of the values were a range of 0 to 65535, which I just fixed by doing realValue = magickValue >> 8 when copying the data back over since the texture data is unsigned char values.
Just for clarification on how to use these functions, since the documentation is horrible and completely wrong, the first parameter to signmoidalContrast() is actually a boolean, even though the type is a size_t, that specifies whether to increase the contrast (true) or reduce it (false), and the second is a range from 0.00001 to 20.0. I say 0.00001 because 0.0 is an invalid value so it just needs to be some decimal that is close to but not exactly 0.0.
For modulate() the documentation says that each value should be specified as 1.0 for no change, which is completely wrong. The values are actually a percentage so for no change you would specify 100.0.
I hope that helps someone because it took me all damn day to figure this stuff out.
According to the Imagemagick website - for the command line but may be the same?
-sigmoidal-contrast contrastxmid-point
increase the contrast without saturating highlights or shadows.
Increase the contrast of the image using a sigmoidal transfer function without saturating highlights or shadows. Contrast indicates how much to increase the contrast. For example, near 0 is none, 3 is typical and 20 is a lot. Note that exactly zero is invalid, but 0.0001 is negligibly different from no change in contrast. mid-point indicates where midtones fall in the resultant image (0 is white; 50% is middle-gray; 100% is black). By default the image contrast is increased, use +sigmoidal-contrast to decrease the contrast.
To achieve the equivalent of a sigmoidal brightness change, use -sigmoidal-contrast brightnessx0% to increase brightness and class="arg">+sigmoidal-contrast brightnessx0% to decrease brightness.
On the command line there is a new brightness contrast setting that may be in later versions of magic++?
-brightness-contrast brightness{xcontrast}{%}}
Adjust the brightness and/or contrast of the image.
Brightness and Contrast values apply changes to the input image. They are not absolute settings. A brightness or contrast value of zero means no change. The range of values is -100 to +100 on each. Positive values increase the brightness or contrast and negative values decrease the brightness or contrast. To control only contrast, set the brightness=0. To control only brightness, set contrast=0 or just leave it off.
You may also use -channel to control which channels to apply the brightness and/or contrast change. The default is to apply the same transformation to all channels.
Brightness and Contrast arguments are converted to offset and slope of a linear transform and applied using -function polynomial "slope,offset".
The slope varies from 0 at contrast=-100 to almost vertical at contrast=+100. For brightness=0 and contrast=-100, the result are totally midgray. For brightness=0 and contrast=+100, the result will approach but not quite reach a threshold at midgray; that is the linear transformation is a very steep vertical line at mid gray.
Negative slopes, i.e. negating the image, are not possible with this function. All achievable slopes are zero or positive.
The offset varies from -0.5 at brightness=-100 to 0 at brightness=0 to +0.5 at brightness=+100. Thus, when contrast=0 and brightness=100, the result is totally white. Similarly, when contrast=0 and brightness=-100, the result is totally black.
As the range of values for the arguments are -100 to +100, adding the '%' symbol is no different than leaving it off.
If magick++ is like Imagick it may be lagging a long way behind the Imagemagick options