I have a shader written in GLSL with an array of structs for holding light data. I use a constant to declare the array size, as is good practice. Let's say this variable is declared as
const int NUM_POINT_LIGHTS = 100;
How can I use C++ to pull this data out of the shader, so that my C++ program knows exactly how many lights it has available to it? I've tried declaring it as
const uniform int NUM_POINT_LIGHTS = 100;
As expected, this didn't work (though oddly enough, it appears as though the uniform specification simply overrode the const specification, as the OpenGL complained that I was initializing an array with a non-const value). I also tried
const int NUM_POINT_LIGHTS = 100;
uniform numPointLights = NUM_POINT_LIGHTS;
This would work, except for the fact that GLSL optimizes away unused uniforms so I have to track glsl into thinking the uniform is used somehow in order to be able to get a hold of the data. I've not been able to find any other method to query a program to get a constant value. Does anybody have any ideas how I might be able to pull a constant out of a shader so my program to get information that is functionally encoded in the shader for it's use?
I don't think you can directly get the value of the constant. However, I figure you must use the value of the constant, most likely as the size of a uniform array. If that's the case, you can get the size of the uniform array, which indirectly gets you the value of the constant.
Say your shader contains something like this:
const int NUM_POINT_LIGHTS = 100;
uniform vec3 LightPositions[NUM_POINT_LIGHTS];
Then you can first get the index of this uniform:
const GLchar* uniformName = "LightPositions";
GLuint uniformIdx = 0;
glGetUniformIndices(program, 1, &uniformName, &uniformIdx);
Using this index, you can then retrieve attributes of this uniform:
const int nameLen = strlen("LightPositions") + 1;
const GLchar name[nameLen];
GLint uniformSize = 0;
GLenum uniformType = GL_NONE;
glGetActiveUniform(program, uniformIdx, nameLen, NULL,
&uniformSize, &uniformType, name);
uniformSize should then be the value of the NUM_POINT_LIGHTS constant. Note that I haven't tried this, but I hope I got the arguments right based on the documentation.
A somewhat ugly but possibly very practical solution is of course to parse the value out of the shader source code. Since you need to read it in anyway before passing it to glShaderSource(), picking out the constant value should be easy enough.
Yet another option, if your main goal is to avoid having the constant in multiple places, is to define it in your C++ code, and add the constant definition to the shader code dynamically after reading in the shader code, and before passing it go glShaderSource().
You can't just query a constant from a GLSL program. No such thing is defined in the GLSL spec.
Uniform Buffer Objects might be a way to get around the issue of uniforms being optimized out.
https://www.opengl.org/wiki/Uniform_Buffer_Object
Related
Goal
I want to set 'shared' buffer's size in GLSL on runtime.
Question is "How to create shared memory in Vulkan Host C/C++ program?" \
Example
In OpenCL, below OpenCL Kernel function has '__local' argument.
// foo.cl
__kernel void foo(__global float *dst, __global float *src, __local *buf ){
/*do something great*/
}
and in host C++ program, I create __local memory and pass it kernel args.
void main(){
...
cl_uint args_index = 2;
foo.setArg(args_index,__local(128) ); // make 128bytes local memory and pass it to kernel.
}
I want to do same thing on Vulkan Compute Pipeline.
and I tried below.
GLSL
//foo.comp
#version 450
layout(binding=0) buffer dstBuffer{
uint dst[];
};
layout(binding=1) buffer srcBuffer{
uint src[];
};
// try 1
layout(binding=2) uniform SharedMemSize{
uint size[];
};
shared uint buf[size[0]]; // compile Error! msg : array size must be a constant integer expression
// try 2
layout(binding=2) shared SharedBuffer{
uint buf[];
}; // compile Error! msg :'shared block' not supported
//try 3
layout(binding=2) shared uint buf[]; // compile Error! msg : binding requires uniform or buffer.
I failed above things.
need your help. thanks.
GLSL has shared variables, which represent storage accessible to any member of a work group. However, it doesn't have "shared memory" in the same way as OpenCL. You cannot directly upload data to shared variables from the CPU, for example. You can't get a pointer to shared variables.
The closest you might get to this is to have some kind of shared variable array whose size is determined from outside of the shader. But even then, you're not influencing the size of memory in total; you're influencing the size of just that array.
And you can sort of do that. SPIR-V in OpenGL allows specialization constants. These are values specified by the outside world during the compilation process for the shader object. Such constants can be used as the size for a shared variable array. Of course, this means that changing it requires a full recompile/relink process.
In the following program the last print function prints -1 which is the location of the uniform named num. I gave right arguments to the function glGetUniformLocation(), yet got -1 as a result , can't figure out why?
P.S. The shader compiles successfully.
GLuint program = glCreateProgram();
GLuint computeShader = glCreateShader(GL_COMPUTE_SHADER);
const GLchar* const shaderSrc = {
"#version 310 es\n"
"\n"
"// Input layout qualifier declaring a 16 x 16 (x 1) local\n" "// workgroup size\n"
"layout (local_size_x = 16, local_size_y = 16) in;\n"
"uniform int num;\n"
"\n"
"void main(void)\n"
"{\n"
"//Do Nothing \n"
"}\n"
};
glShaderSource(computeShader, 1, &shaderSrc, NULL);
glCompileShader(computeShader);
int result;
glGetShaderiv(computeShader, GL_COMPILE_STATUS, &result);
if(result == GL_FALSE){
int length;
glGetShaderiv(computeShader, GL_INFO_LOG_LENGTH, &length);
char* message = static_cast<char*>(malloc(length));
glGetShaderInfoLog(computeShader, length, &length, message);
__android_log_print(ANDROID_LOG_INFO, "MyLog", "Shader Compile Error: %s", message);
free(message);
}
GL_CALL(glAttachShader(program, computeShader));
GL_CALL(glLinkProgram(program));
GL_CALL(glValidateProgram(program));
GL_CALL(glUseProgram(program));
auto location = glGetUniformLocation(program, "num");
__android_log_print(ANDROID_LOG_INFO, "MyLog", "Location %d", location);
Besides #Rabbid76's answer I want to specifically address the question in your title:
Why is the location returned by GL function call -1 if there's no error?
Because there is no error, and location -1 is not an error condition.
Only active uniforms do have a location. Querying the location for something which isn't an active uniform is defined to return -1. Also note that the following:
glUniform1f(-1, someValue)
is perfectly valid in the GL and will also not generate any errors as per the spec. It will just be silently ignored.
Since for a non-active uniform variable it is impossible to ever influence the outputs of your shader program, its value is irrelevant anyway, and the GL does not have to bother.
Note that this design choices in the API are quite reasonable. On the client side, you can use a set of uniforms you want to provide to your shaders. But you can have different alternatives for these shaders, each accessing a specific sub-set of those uniforms. The client side code can treat each of the variant the same, it does not have to know (or query) which uniforms each uses. It only must not treat location -1 as an error, as it isn't one. At least not if your client code can't be absolutely sure that this variable must be active.
Warning: querying the value of non-active uniforms actually is an error!
If you use functions like glGetUniformfv(program,location,params) with location set to -1, this actually is an error condition. The call will just return GL_INVALID_OPERATION and have no other effect (this also means that contents of the memory pointed to by params will not be modified - it won't return any default values for such things). This behavior is also quite reasonable, because as non-active uniform variables can't have a value, you cannot query them, and trying to do so is an error.
Warning: the situation is slightly different for vertex attributes
For vertex attributes, there is glGetAttribLocation, and this will also return GLint, and use -1 for attributes which don't exist or are not active. Querying these also is no error. But the GL functions which take those attribute locations (like glVertexAttribPointer, glEnableVertexAttribPointer) often use GLuint types, and
glVertexAttribPointer(-1, ...);
will generate a GL_INALID_VALUE GL error, as (GLuint)-1 is most likly greater than your implementation's GL_MAX_VERTEX_ATTRIBS value (and even if it weren't you're not getting the results you intented.
So for attribute locations, it is mandatory that your client code checks for -1...
The uniform variable num is not an active program resource, because it is not used in the shader program. The compiler and linker optimize the code and determine that the variable is not required. Therefore, you will not get a valid uniform location.
So I'm trying to gain access to vertex buffers on the GPU. Specifically I need to do some calculations with the vertices. So in order to do that I attempt to map the resource (vertex buffer) from the GPU, and copy it into system memory so the CPU can access the vertices. I used the following SO thread to put the code together: How to read vertices from vertex buffer in Direct3d11
Here is my code:
HRESULT hr = pSwapchain->GetDevice(__uuidof(ID3D11Device), (void**)&pDevice);
if (FAILED(hr))
return false;
pDevice->GetImmediateContext(&pContext);
pContext->OMGetRenderTargets(1, &pRenderTargetView, nullptr);
//Vertex Buffer
ID3D11Buffer* veBuffer;
UINT Stride;
UINT veBufferOffset;
pContext->IAGetVertexBuffers(0, 1, &veBuffer, &Stride, &veBufferOffset);
D3D11_MAPPED_SUBRESOURCE mapped_rsrc;
pContext->Map(veBuffer, NULL, D3D11_MAP_READ, NULL, &mapped_rsrc);
void* vert = new BYTE[mapped_rsrc.DepthPitch]; //DirectX crashes on this line...
memcpy(vert, mapped_rsrc.pData, mapped_rsrc.DepthPitch);
pContext->Unmap(veBuffer, 0);
I'm somewhat of a newbie when it comes to C++. So my assumptions may be incorrect. The initialization value that
mapped_rsrc.DepthPitch
returns is quite large. It returns 343597386. According to the documentation I listed below, it states that the return value of DepthPitch is returned in bytes. If I replace the initialization value with a much smaller number, like 10, the code runs just fine. From what I read about the Map() function here: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_mapped_subresource
It states :
Note The runtime might assign values to RowPitch and DepthPitch that
are larger than anticipated because there might be padding between
rows and depth.
Could this have something to do with the large value that is being returned? If so, does that mean I have to parse DepthPitch to remove any unneeded data? Or maybe it is an issue with the way vert is initialized?
There was no Vertex Buffer bound, so your IAGetVertexBuffers failed to return anything. You have to create a VB.
See Microsoft Docs: How to Create a Vertex Buffer
As someone new to DirectX 11, you should take a look at DirectX Tool Kit.
Suppose the following shader:
uniform float input[]
float do_stuff(int index) {
return input[index];
}
This code does something using some data without caring the size of input. But, the program will be linked with another shader, that will know the size and invoke it:
void do_stuff(int);
uniform float input[3];
void main() {
gl_FragColor = vec4(do_stuff(input[0]),
do_stuff(input[1]),
do_stuff(input[2]),
1);
}
Obviously, this code is stupid, I just created it demonstrate what I need. My real situation is: the data that the "do stuff" shader access is not predefined, each "do stuff" shader will have it owns data. Also, the "do stuff" shader can not know how many times it will be called, so its input are arrays with undefined size. On the other side, the "caller" shader will be generated in runtime, it knows exactly which data the "do_stuff" shaders will use and how many.
Actually, the "do stuff" shaders are user provided, while the "caller" shader mix them together.
So, with a bit of preprocessing on the "do stuff" shader I may solve my problem using parameters, but it will make a bit more complicated for me (because each "do stuff" shader would have a different function signature). If I could just create these undefined size uniform it would be easier.
Also, I am open to suggestions...
I'm using OpenGL to render camera perspectives, and a one point in my code I'm trying to take the direction of the light (shown here as type "Vector4") and multiply it by a matrix of type "Matrix4x4" that represents the Modelview transformation (sorry if this is not making any sense, this is for a school project, as such I'm still learning about this stuff) Anyway, my code goes as follows...
Vector4 lightDirection = data->dir * follow->getModelviewMatrix().getInverse().getTranspose();
data->dir = lightDirection;
setLight(*data);
this give me the following error:
passing 'const vec4<double>' as 'this' argument of 'vec4<T>& vec4<T>::operator=(const vec4<T>&)[with T = double]' discards qualifiers
Again, much of this code is prewritten for the class (namely the vector and matrix types) but if someone could just help me decipher what the error means it would be much appreciated! I can give more information as needed.
I figured 'data' or 'data->dir' were const, however I can find no mention of either of them to be. 'dir' is of type SceneLightData, and when its added on I'm doing this:
void Scene::addLight(const SceneLightData &sceneLight)
{
SceneLightData light = sceneLight;
m_lights.push_back(&light);
}
The error occurs on this line:
data->dir = lightDirection;
EDIT
problem solved. thanks everyone! solution:
void Scene::addLight(const SceneLightData &sceneLight)
{
SceneLightData* light = new SceneLightData;
*light = sceneLight;
m_lights.push_back(light);
}
and
SceneLightData* data = m_lights[i];
data->dir = data->dir * follow->getModelviewMatrix().getInverse().getTranspose();
setLight(*data);
data->dir is constant. It means you cannot change it, and you are trying to assign a different value to it, that's why compiler is getting mad at you. See const-correctness for more.
This error occurs because you are attempting to call a non-const member function on a const object. At some point in your code you are calling the assignment operator of a const vec4<double>.
The assignment operator (operator=) is non-const because assigning to an object will modify the object that is being assigned to (to give it its new value).
It is not clear from your example where exactly the problem is occurring. If you are still having trouble, try reducing the example down to a minimal complete program which demonstrates the issue, and we can explain further.