How to draw on MTLTexture in bgra8Unorm pixel format - ios

My code works when I draw on MTLTexture with rgba32Float pixel format, I can take then CVPixelBuffer out of it.
But FlutterTexture requires bgra8Unorm format. I do not want to convert CVPixelBuffer due to performance overhead.
So I'm trying to render on MTLTexture with bgra8Unorm pixel format, but the following fragment shader code won't compile:
fragment vector_uchar4 fragmentShader2(Vertex interpolated [[stage_in]]) {
return 0xFFFFFFFF;
}
With error: Invalid return type 'vector_uchar4' for fragment function
I've tried to replace it with uint type, but it crashes with error:
Fatal error: 'try!' expression unexpectedly raised an error:
Error Domain=AGXMetalA11 Code=3
"output of type uint is not compatible with a MTLPixelFormatBGRA8Unorm color attachement."
UserInfo={NSLocalizedDescription=output of type uint is not compatible with a MTLPixelFormatBGRA8Unorm color attachement.}
If I use vector_float4 or vector_half4 return type my texture and buffers are empty.
Which return type I have to use for bgra8Unorm pixel format and get non empty image? Is it possible with metal at all?

I've found answer on page 30 of Metal Shading Language specification
And finally this code draws image as expected:
fragment float4 fragmentShader2(Vertex interpolated [[stage_in]]) {
// ...
rgba8unorm<float4> rgba;
rgba = float4(color.r, color.g, color.b, 1.0);
return rgba;
}
If someone can explain what is happening under the hood, I would really like to not waste bounty.

It depends on many different factors. In most cases you should use float4 or half4.
All modern apple GPUs that support metal designed to perform calculation on ( 32-bit or 64-bit) floating point data. It's how GPUs works, this means that any read operation calculated by the shader on the Float, Snorm, Unorm formats will be performed on 32-bit or 64-bit floating point, regardless of the original input format.
On any writing operation shader performs conversion from 32-bit or 64-bit floating point to target format. For conversion rules please see Metal Shading Language specification page 217.
Any metal formats that use the Float, Snorm, Unorm suffix are floating-point formats, while Uint and Sint are unsigned and signed integer.
Float - A floating-point value in any of the representations defined by metal.
Unorm - A floating-point value in range [0.0, 1.0].
Snorm - A floating-point value in range [-1.0, 1.0].
Uint - A unsigned integer.
Sint - A signed integer.

Related

IOS how metal convert fragment shader return value to MTKView.colorPixelFormat?

I have a question is between the fragment shader return value and MTkView.colorPixelFormat.
My fragment shader return float4, it is a 4 * 32bit vector, and MTkView.colorPixelFormat is .bgr10_xr.
how to convert float4 to .bgr10_xr? or this conversion is automatically?
It should just work. Metal will do the conversion for you. Refer to section 7.7 of Metal Shading Language Specification theres an entry about 10-bit formats 7.7.4 Conversion for 10- and 11-bit Floating-Point Pixel Data Type.

Why pixel shader returns float4 when the back buffer format is DXGI_FORMAT_B8G8R8A8_UNORM?

Alright, so this has been bugging me for a while now, and could not find anything on MSDN that goes into the specifics that I need.
This is more of a 3 part question, so here it goes:
1-) When creating the swapchain applications specify backbuffer pixel formats, and most often is either B8G8R8A8 or R8G8B8A8. This gives 8 bit per color channel so a total of 4 bytes is used per pixel....so why does the pixel shader has to return a color as a float4 when float4 is actually 16 bytes?
2-) When binding textures to the Pixel Shader my textures are DXGI_FORMAT_B8G8R8A8_UNORM format, but why does the sampler need a float4 per pixel to work?
3-) Am I missing something here? am I overthinking this or what?
Please provide links to to support your claim. Preferably from MSDN!!!!
GPUs are designed to perform calculations on 32bit floating point data, at least if they want to support D3D11. As of D3D10 you can also perform 32bit signed and unsigned integer operations. There's no requirement or language support for types smaller than 4 bytes in HLSL, so there's no "byte/char" or "short" for 1 and 2 byte integers or lower precision floating point.
Any DXGI formats that use the "FLOAT", "UNORM" or "SNORM" suffix are non-integer formats, while "UINT" and "SINT" are unsigned and signed integer. Any reads performed by the shader on the first three types will be provided to the shader as 32 bit floating point irrespective of whether the original format was 8 bit UNORM/SNORM or 10/11/16/32 bit floating point. Data in vertices is usually stored at a lower precision than full-fat 32bit floating point to save memory, but by the time it reaches the shader it has already been converted to 32bit float.
On output (to UAVs or Render Targets) the GPU compresses the "float" or "uint" data to whatever format the target was created at. If you try outputting float4(4.4, 5.5, 6.6, 10.1) to a target that is 8-bit normalised then it'll simply be truncated to (1.0,1.0,1.0,1.0) and only consume 4 bytes per pixel.
So to answer your questions:
1) Because shaders only operate on 32 bit types, but the GPU will compress/truncate your output as necessary to be stored in the resource you currently have bound according to its type. It would be madness to have special keywords and types for every format that the GPU supported.
2) The "sampler" doesn't "need a float4 per pixel to work". I think you're mixing your terminology. The declaration that the texture is a Texture2D<float4> is really just stating that this texture has four components and is of a format that is not an integer format. "float" doesn't necessarily mean the source data is 32 bit float (or actually even floating point) but merely that the data has a fractional component to it (eg 0.54, 1.32). Equally, declaring a texture as Texture2D<uint4> doesn't mean that the source data is 32 bit unsigned necessarily, but more that it contains four components of unsigned integer data. However, the data will be returned to you and converted to 32 bit float or 32 bit integer for use inside the shader.
3) You're missing the fact that the GPU decompresses textures / vertex data on reads and compresses it again on writes. The amount of storage used for your vertices/texture data is only as much as the format that you create the resource in, and has nothing to do with the fact that the shader is operating on 32 bit floats / integers.

Sampling from single channel textures in DirectX

I have a 2D texture formatted as DXGI_FORMAT_R32_FLOAT. In my pixel shader I sample from it thusly:
float sample = texture.Sample(sampler, coordinates);
This results in the following compiler warning:
warning X3206: implicit truncation of vector type
I'm confused by this. Shouldn't Sample return a single channel, and therefore a scalar value, as opposed to a vector?
I'm using shader model 4 level 9_1.
Either declare your texture as having one channel, or specify which channel you want. Without the <float> bit, it'll assume it's a 4 channel texture and so therefore Sample will return a float4.
Texture2D<float> texture;
or
float sample = texture.Sample(sampler, coordinates).r;

Why does varying float equality test fail in glsl?

If I have a varying float in my shader program:
varying highp float someFloat;
and in the vertex shader, I set it to something.
someFloat = 1.0;
why in my fragment shader does this comparison seem to return false?
someFloat == 1.0 // false
but this returns true?
someFloat > .0 // true
testing on openGL ES in an iPad mini.
It happens on any IEEE 754 floating point number. It is because of the nature of floating point representation. Any language that use the IEEE 754 format will encounter the same problem.
Since 1.0 may not be represented exactly in floating point system as 1.000000000... , hence it is considered dangerous to compare them using ==.
Floating point numbers should always be compared with an epsilon value .
Since floating point calculations involve a bit of uncertainty we can try to allow for this by seeing if two numbers are ‘close’ to each other. If you decide – based on error analysis, testing, or a wild guess – that the result should always be within 0.00001 of the expected result then you can change your comparison to this:
if (fabs(someFloat - 1.0)) < 0.00001)
The maximum error value is typically called epsilon.
Probably you should read What Every Computer Scientist Should Know About Floating-Point Arithmetic

How do you use GL_UNSIGNED_BYTE for texture coordinates?

In the "OpenGL ES Programming Guide for iOS" documentation included with XCode, in the "Best Practices for Working with Vertex Data" section, under the heading "Use the Smallest Acceptable Types for Attributes", it says
Specify texture coordinates using 2 or 4 unsigned bytes
(GL_UNSIGNED_BYTE) or unsigned short (GL_UNSIGNED_SHORT).
I'm a bit puzzled. I thought that texture coordinates would be < 1 most of the time, and so would require a float to represent fractional values. How do you use unsigned bytes or unsigned shorts? Divide it by 255 in the shader?
If you use GL_UNSIGNED_BYTE you'll end up passing normalized of GL_TRUE, to (for example) glVertexAttribPointer, indicating that the values you're passing are not between 0 and 1, but should be normalized from their full range (eg. 0 to 255) to the normalized range of 0 to 1 before being passed to your shader. (See Section 2.1.2 of the OpenGL ES 2.0 spec for more details).
In other words, when you're passing integer types like unsigned byte, use a "normalized" value of GL_TRUE and use the full range of that type (such as 0 to 255), so a value of 127 would be approximately equivalent to floating point 0.5.

Resources