I am trying to create some types/procs to be able to use OpenGL functionality in Nim. I started with the Opengl Program object. I created a proc that compiles a vertex and fragment shader, then links it into a program and stores the id in the Program object. This works.
But to perform some error checking (checking errors in shader code or in linker), you need to pass a pre-allocated cstring to a function. I don't know how to do that and I don't seem to be able to find it in the manual. I tried several things, and the last one was creating a sequence of chars and passing the addr but it doesn't work.
How do you do this?
Here's a part the code:
import opengl
type
openglProgram = object
id: GLuint
...
proc createOpenglProgram(vert: string, frag: string) : openglProgram =
result.id = glCreateProgram()
var status: GLint
let vs = glCreateShader(GL_VERTEX_SHADER)
let verts = allocCStringArray([ vert ])
glShaderSource(vs, 1.GLsizei, verts, nil)
glCompileShader(vs)
glGetShaderiv(vs, GL_COMPILE_STATUS, status.addr)
if status != GL_TRUE.GLint:
var length: GLint
glGetShaderiv(vs, GL_INFO_LOG_LENGTH, length.addr)
var msg = cstring # <= this is the one that should be preallocated to be of size 'length'
glGetShaderInfoLog(vs, length, nil, msg) # <= this gives error because msg is a null pointer
echo "Error in vertex shader: " & $msg
glAttachShader(result.id, vs);
...
How?
Also: coding this is really a pain, it's ugly, and gives me a headache... I don't mind doing it once because after that, this code is never to be touched again, but if there is a way to do it better than this, please let me know. I'm still very new to Nim.
Also: when passing the cStringArray (in earlier call glShaderSource), I need to pass the size of that array. I know it is 1 so I hard code it, but is there a way to get the size from the cStringArray?
The best way to allocate cstrings in Nim is to do
var a = newString(len)
someProcThatTakesCstr(a.cstring, a.len)
when passing the cStringArray (in earlier call glShaderSource), I need to pass the size of that array. I know it is 1 so I hard code it, but is there a way to get the size from the cStringArray?
I'd suggest not heap allocating a cstringarray and just using a stack array with a cast as such https://github.com/beef331/truss3d/blob/master/src/truss3D/shaders.nim#L39-L43
A C program would record the amount of memory it allocates, because there's no way to retrieve this information. A traditional method is to use zero or null as the last element (which is why Nim cstrings end with 0), but it hurts speed.
I am not sure what you are trying to tell me. I know strings in C end with a 0 character. I have already written code for this in C, it is easy:
GLint length;
glGetShaderiv(vs, GL_INFO_LOG_LENGTH, &length);
char *msg = malloc(length);
glGetShaderInfoLog(vs, length, NULL, msg);
...
free(msg);
Or was there something else you wanted to say that I am missing?Can you tell me where I should have found this information in the manual? Because I had been searching for something like this.
There is no explicit mention but https://nim-lang.org/docs/manual.html#types-string-type says
The terminating zero cannot be accessed unless the string is converted to the cstring type first. The terminating zero assures that this conversion can be done in O(1) and without any allocations.
Which indirectly states "Hey you can use Nim strings as Cstrings anywhere you can do char* or char*, SomeInt len in C"
I think you misunderstood that I was asking about the last parameter containing the lengths of the cstrings themselves, but passing nil is ok there.
CStringArrays are just pointers to some collection, they do not store their length anywhere. If you use Nim's openArray[string] proc they're nil terminated, but there is no certain way to retrieve length from a cstringarray. https://nim-lang.org/docs/ctypes.html#cstringArray for reference.
... Which indirectly states "Hey you can use Nim strings as Cstrings anywhere you can do char* or char*, SomeInt len in C"
That's great information, thanks.
... they're nil terminated, but there is no certain way to retrieve length from a cstringarray
Feels like a flaw to me... I can find out the length by iterating the cstrings in the array until I arrive at nil, I feel like this should already be there.
On the other hand, I understand that these things are not meant to be used too much in Nim and I hope I can get past it soon.
Thanks!
For completeness: I had to reduce the length by 1 because OpenGL returns the length including the 0 character, making the string contain two 0 characters at the end.
From https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetProgram.xhtml:
GL_INFO_LOG_LENGTH
params returns the number of characters in the information log for program including the null termination character (i.e., the size of the character buffer required to store the information log). If program has no information log, a value of 0 is returned.
This is not really a big deal when simply printing the string, but if I want to for example query all shader uniform variables and put them in a table and then address them by their name, this becomes necessary because I need to add the 0 to find them if I don't decrease the length:
var
numUniforms: GLint
maxNameLen: GLint
glGetProgramiv(result.id, GL_ACTIVE_UNIFORMS, numUniforms.addr)
glGetProgramiv(result.id, GL_ACTIVE_UNIFORM_MAX_LENGTH, maxNameLen.addr)
var nameBuf = newString(maxNameLen - 1)
for i in 0.GLuint .. numUniforms.GLuint - 1:
var
usize: GLint
utype: GLenum
uloc: GLint
glGetActiveUniform(result.id, i, maxNameLen, nil, usize.addr, utype.addr, nameBuf.cstring)
uloc = glGetUniformLocation(result.id, nameBuf.cstring)
result.uniformLocation[nameBuf] = uloc
To clarify, without the -1, I had to:
let uColor = progTest.uniformLocation["uColor\0"]
Feels like a flaw to me... I can find out the length by iterating the cstrings in the array until I arrive at nil, I feel like this should already be there.
Welcome to why sentinel terminated collections were replaced with pascal collections. To encode the length you need more data and then you also need a conversion proc so thank apis that work with char** :D