我正在尝试将 Windows 程序移植到 GNU/Linux,但计算着色器无法编译。
我对 GLSL 有一些了解。所以我想知道是否有解决方法可以让着色器正常工作。
我编写了一个显示编译错误的最小示例。
这是计算着色器:
#version 430 core
#extension GL_ARB_gpu_shader_int64 : enable
//#extension GL_ARB_bindless_texture : enable
layout(binding = 1) uniform TextureHandles {
uvec2 texture_handles[512];
};
vec3 SampleTexture(uint texture_index, vec2 uv) {
uv.y = 1.f - uv.y;
sampler2D tex_sampler = sampler2D(texture_handles[texture_index]);
return textureLod(tex_sampler, uv, 0.0f).xyz;
}
void main() {
}
以下是编译错误:
0:11(2): error: image/sampler variables may only be declared as function parameters or uniform-qualified global variables
0:11(2): error: opaque variables must be declared uniform
0:11(26): error: cannot initialize tex_sampler variable opaque
0:11(26): error: cannot construct opaque type `sampler2D'
0:12(20): warning: `tex_sampler' used uninitialized
关于不透明类型sampler2D的构造,我在网上看到需要启用GL_ARB_bindless_texture扩展,但是当我启用它时,出现错误,说它在计算着色器中不受支持。
这是使用 GLEW 和 GLFW 的最小程序,显示错误:
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#define GLEW_STATIC
#include <GL/glew.h>
#include <stdlib.h>
#include <stdio.h>
/////////////////////////////////////////////////////////
//
// Shader programs for core profile:
//
const char * csprog_core =
"#version 430 core\n\
#extension GL_ARB_gpu_shader_int64 : enable\n\
//#extension GL_ARB_bindless_texture : enable\n\
\n\
layout(binding = 1) uniform TextureHandles {\n\
uvec2 texture_handles[512];\n\
};\n\
\n\
vec3 SampleTexture(uint texture_index, vec2 uv) {\n\
uv.y = 1.f - uv.y;\n\
sampler2D tex_sampler = sampler2D(texture_handles[texture_index]);\n\
return textureLod(tex_sampler, uv, 0.0f).xyz;\n\
}\n\
\n\
void main() {\n\
}\n\
\n";
int width, height;
void error_callback(int error, const char* description) {
fprintf(stderr, "Error: %s\n", description);
}
int main( int argc, char *argv[ ], char *envp[ ] ) {
if(!glfwInit()) {
exit(EXIT_FAILURE);
}
glfwSetErrorCallback(error_callback);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(640, 480, "Program", NULL, NULL);
if(!window) {
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
glfwGetFramebufferSize(window, &width, &height);
// get version info
const GLubyte* renderer;
const GLubyte* version;
///////////////////////////////////////////////////////////////////////
//
// start GLEW extension handler
//
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if(GLEW_OK != err) {
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
return(-1);
}
fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
// get version info
renderer = glGetString(GL_RENDERER); // get renderer string
version = glGetString(GL_VERSION); // version as a string
printf("\nRenderer: %s", renderer);
printf("\nOpenGL version supported %s\n", version);
fflush(stdout);
// tell GL to only draw onto a pixel if the shape is closer to the viewer
glEnable(GL_DEPTH_TEST); // enable depth-testing
glDepthFunc(GL_LESS); // depth-testing interprets a smaller value as "closer"
//////////////////////////////////////////////////////////
//
// Shaders:
//
GLint params;
GLint len;
GLuint cscore = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(cscore, 1, &csprog_core, NULL);
glCompileShader(cscore);
glGetShaderiv(cscore,GL_COMPILE_STATUS,¶ms);
if(params == GL_FALSE) {
GLchar log[100000];
glGetShaderInfoLog(cscore,100000,&len,log);
printf("\n\n%s\n\n",log);
exit(EXIT_FAILURE);
}
//
//////////////////////////////////////////////////////////
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
根据 Erdal Küçük 评论,我实现了纹理数组。将其宽度和高度设置为最大纹理的值。
我必须设置一些制服,以便在着色器中计算每个纹理的宽度和高度函数的 u 和 v 值。但 u 和 v 值的比例因子可以在 CPU 上预先计算。
计算着色器如下所示:
#version 430 core
#extension GL_ARB_gpu_shader_int64 : enable
uniform sampler2DArray Textures;
uniform uint maxwidth;
uniform uint maxheight;
uniform uint texwidths[MAX_TEXTURES];
uniform uint texheights[MAX_TEXTURES];
vec3 SampleTexture(uint texture_index, vec2 uv) {
uv.y = 1.f - uv.y;
uv.x = uv.x * float(texwidths[texture_index]) / maxwidth;
uv.y = uv.y * float(texheights[texture_index]) / maxheight;
return textureLod(Textures, vec3(uv, texture_index), 0.0f).xyz;
}
void main() {
}
硬件支持的数组的最大层数可以通过以下调用得知:
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &value);
为了将纹理上传到 GPU,我使用此代码,设置纹理中最大宽度和高度值的纹理数组:
GLuint texture_array = 0;
glGenTextures(1,&texture_array);
glBindTexture(GL_TEXTURE_2D_ARRAY,texture_array);
int maxwidth = 0;
int maxheight = 0;
for(auto i = 0; i < textures.size(); ++i) {
if(maxwidth < textures[i].width) maxwidth = textures[i].width;
if(maxheight < textures[i].height) maxheight = textures[i].height;
}
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, maxwidth, maxheight, textures.size());
std::vector<u_int32_t> clear_data(maxwidth * maxheight, 0);
for (auto i = 0; i < textures.size(); ++i) {
auto & tex = textures[i];
// Set to zero the texture at layer i:
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, maxwidth, maxheight, 1, GL_RGBA, GL_UNSIGNED_BYTE, &clear_data[0]);
// copy the texture to GPU at layer i:
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, tex.width, tex.height, 1, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[textures[i].data_start]);
texwidths_[i] = tex.width;
texheights_[i] = tex.height;
}
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D_ARRAY, NULL);
在启动着色器计算之前,我只需绑定纹理数组,并设置每个纹理的宽度和高度的统一值。