maxwills's picture

Help With OpenTK "Half"

Edit: Solved, see posts bellow :D.

Hello.
I am having trouble when using OpenTK.Half (OpenTK v1.1) instead of floats when using them in vertex buffers for vertex attributes.
My program runs just fine when using VertexAttribPointerType.Float, but fails with VertexAttribPointerType.HalfFloat

I am using the buffers in this way (VB.net):

Dim BytesPerValue as Interger= OpenTK.Half.SizeInBytes 'this should be 2, right?
Buffers(i) = GL.GenBuffer()
GL.BindBuffer(BufferTarget.ArrayBuffer, Buffers(i))
GL.BufferData(BufferTarget.ArrayBuffer, CType(max_primitive_count * primitive_dimensions * BytesPerValue, IntPtr), IntPtr.Zero, BufferUsageHint.StreamDraw) 
 
'Then for the update and drawing part I do :
'n_primitives is the number of primitives to be updated and drawn, passed as an argument in the method. Data is a Byte array (read description in the bottom)
GL.BindBuffer(BufferTarget.ArrayBuffer, Buffers(CurrentBufferIndex))
Dim pointr = GL.MapBufferRange(BufferTarget.ArrayBuffer, IntPtr.Zero, n_primitives * PrimitiveDimensions * BytesPerValue, BufferAccessMask.MapWriteBit)
SyncLock Me 'protecting Data
       Runtime.InteropServices.Marshal.Copy(Data, 0, pointr, n_primitives * PrimitiveDimensions * BytesPerValue)
End SyncLock
GL.UnmapBuffer(BufferTarget.ArrayBuffer)
GL.VertexAttribPointer(AttributeIndex, PrimitiveDimensions,  VertexAttribPointerType.HalfFloat, False, 0, 0)
'... some other buffers code
GL.DrawArrays(PrimitiveType.Points, 0, Count) 'where count is the number of points to draw (same as n_primitives)

Data is a byte array. It contains the HalfFloat binary values (obtained via Half.GetBytes()), packed. This means, you can read or write a "half" every 2 bytes. Data.Length is (max_primitives*primitive_dimensions*bytespervalue)

The way I put data in the Data array is:

Dim b = Half.GetBytes(value)
Array.Copy(b, 0, Data, index * BytesPerValue, BytesPerValue)
'where index is the index of the "half" value in the array, and "value" is a "Half" number.

I repeat. This works well when using BytesPerValue = "SIzeOfSingle"(should be 4), VertexAttribPointerType.Float, and filling the Data with BitConverter.GetBytes(CSng(value))

I tried using another "Half" (half float) implementation for .net, but the result is the same.

Does anyone has an example of how to properly use half precision floats with OpenGL in Opentk? The answer to " do you really need half floats in your application?" in my case is:" Yes, I do, just because". So asking something alike wont solve any problem. I am aware of the precision issues of a Half, and by my calculations, that shouldn't be a problem in this case.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

Half float requires OpenGL 3.x or the GL_ARB_half_float_vertex extension.

Frassle's picture

"but fails" can you be a bit more descriptive? Using half instead of float should work, and the fact that you've tried other half implementations suggests this isn't an issue with OpenTK's half type. As with any OpenTK issue, do try running your program with the debug version of OpenTK, that will throw an exception if any of the OpenGL methods errors (in release builds you have to call GL.GetErrror).

Frassle's picture
the Fiddler wrote:

Half float requires OpenGL 3.x or the GL_ARB_half_float_vertex extension.

Again my only using modern GL leads me to forgetting things :P At any rate the debug build should throw an exception on VertexAttribPointer if Half isn't supported.

maxwills's picture

Finally solved it after almost a whole day of pain.

I am using a 3.3 OpenGL Context and the extension is supported. I also used a debug OpenTK dll(Finally I got it to work (because of some overload problems in VB.Net)).

My application is now working fine with "half". The thing is, it seems that there is "some padding somewhere at some low level". I mean, having a byte array of packed HalfFloats for 3-dimensional primitives is just wrong. For instance, if I want a position attribute with halffloats, the byte array I copy to the mapped buffer must be something like : [first_byte_of_pos_1.x, second_byte_of_pos_1.x, first_byte_of_pos_1.y, second_byte_of_pos_1.y, first_byte_of_pos_1.z, second_byte_of_pos_1.z, padding_byte, padding_byte, first_byte_of_pos_2.x, second_byte_of_pos_2.x, first_byte_of_pos_2.y, second_byte_of_pos_2.y, first_byte_of_pos_2.z, second_byte_of_pos_2.z, padding_byte, padding_byte, etc]

In my implementation I just fixed it by setting PrimitiveDimensions = 4 (if it was 3) and filling the data as I just explained. I guess if you work with 1dimensional vertex attribute with half floats, you should pad them with a couple of bytes.

If anyone has trouble with half floats, this may be the reason. (OpenTK.Half and Vector*h structures work OK)

Frassle's picture

I can't find anything to suggest (given the code you've shown) that OpenGL would be expecting padding bytes. You've set stride to 0 in VertexAttribPointer which should interpret the data as being tightly packed, assuming PrimitiveDimensions was 3 that should be 3 tightly packed half floats, followed immediately by the next 3.

the Fiddler's picture

According to https://www.opengl.org/wiki/Uniform_Buffer_Object:

Quote:

Uniform arrays in a buffer are stored sequentially. But they are not guaranteed to be tightly packed. Therefore, for any array uniforms in a block, you must query a stride value with GL_UNIFORM_ARRAY_STRIDE​. This stride is the byte distance from the beginning of one element to the beginning of the next.

I have not been able to find a reference for old-style uniforms (i.e. outside uniform blocks), but I would expect the same caveat to apply.

In other words, you need to query the array stride and pad according to that, if necessary.

Edit: sorry, I thought this was about uniforms, not vertex attributes. I would expect something similar to apply.

Edit 2: indeed, the behavior you are seeing is expected.

Quote:

There is something you should watch out for. The alignment of any attribute's data should be no less than 4 bytes. So if you have a vec3 of GLushort​s, you can't use that 4th component for a new attribute (such as a vec2 of GLbyte​s). If you want to pack something into that instead of having useless padding, you need to make it a vec4 of GLushort​s.

From https://www.opengl.org/wiki/Vertex_Specification_Best_Practices#Attribut...

maxwills's picture
Frassle wrote:

I can't find anything to suggest (given the code you've shown) that OpenGL would be expecting padding bytes. You've set stride to 0 in VertexAttribPointer which should interpret the data as being tightly packed, assuming PrimitiveDimensions was 3 that should be 3 tightly packed half floats, followed immediately by the next 3.

That was actually my confusion too. If zero stride is passed, you will suppose that 3 (tightly) packed half-floats per primitive should work. It doesn't u__u

the Fiddler's post explains the "problem". I think it is "the only place in the internet" where you get a hint on that.