GL_ARB_bindless_texture (uniform buffer backed)
Main.cpp
#define GLM_ENABLE_EXPERIMENTAL
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <iostream>
#include <vector>
#include "Shader.h"
#include <SOIL.h>
/* macro to detect where GL errors happens */
#define CHECK_GL_ERROR __CheckForGLError(__FILE__, __LINE__)
using namespace std;
using namespace glm;
/* main functions */
bool Initialize();
void Render();
void CleanUp();
void __CheckForGLError(const char* filename, int line);
void APIENTRY OpenGLDebugCallback(
GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userparam);
/* variables */
GLuint program = 0;
GLuint vertexshader = 0;
GLuint fragmentshader = 0;
GLuint vertexarray = 0;
GLuint vertexbuffer = 0;
GLuint uniformbuffer = 0;
GLuint texture[2] = { 0 };
/* structures */
struct Vertex {
vec3 Position;
vec2 TexCoord;
};
struct Texel { unsigned char r, g, b, a; };
struct Texture2DInfo {
int Width = 0;
int Height = 0;
void* Data = nullptr;
};
/* data for the GL debug callback */
struct DebugUserParams {
bool ShowNotifications = true;
} debuguserparams;
int main(int argc, char* argv[])
{
/* Initialize the library */
if (!glfwInit())
return 1;
bool fullscreen = false;
bool debugcontext = true;
/* Create a windowed mode window and its OpenGL context */
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, debugcontext ? GLFW_TRUE : GLFW_FALSE);
GLFWmonitor* monitor = fullscreen ? glfwGetPrimaryMonitor() : NULL;
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", monitor, NULL);
if (!window)
{
glfwTerminate();
return 1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
/* Initialize GLEW */
if (glewInit() != GLEW_OK)
{
glfwTerminate();
return 1;
}
/* Register debug callback if debug context is available */
GLint contextflags = 0;
glGetIntegerv(GL_CONTEXT_FLAGS, &contextflags);
if (contextflags & GL_CONTEXT_FLAG_DEBUG_BIT)
{
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(OpenGLDebugCallback, &debuguserparams);
}
if (Initialize())
{
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
Render();
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
}
else
cin.get(); /* ... wait and show errors in console */
CleanUp();
glfwTerminate();
return 0;
}
bool Initialize()
{
/* check if GL_ARB_bindless_texture is supported */
GLint extensioncount = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &extensioncount);
bool extension_available = false;
for (int i = 0; i < extensioncount; i++)
{
string extension((const char*)glGetStringi(GL_EXTENSIONS, i));
if (extension == "GL_ARB_bindless_texture")
{
extension_available = true;
break;
}
}
if (!extension_available)
{
cout << "GL_ARB_bindless_texture not supported" << endl;
return false;
}
// create all objects
program = glCreateProgram();
vertexshader = glCreateShader(GL_VERTEX_SHADER);
fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
glCreateVertexArrays(1, &vertexarray);
glCreateBuffers(1, &vertexbuffer);
glCreateBuffers(1, &uniformbuffer);
glCreateTextures(GL_TEXTURE_2D, 2, texture);
// compile shaders and link program
if (!CompileShader(vertexshader, LoadTextFile("Shaders/vs.glsl")))
return false;
if (!CompileShader(fragmentshader, LoadTextFile("Shaders/fs.glsl")))
return false;
if (!LinkProgram(program, { vertexshader, fragmentshader }))
return false;
/* setup VAO: */
/* set vertex format */
glVertexArrayAttribFormat(vertexarray, 0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(vertexarray, 1, 2, GL_FLOAT, GL_FALSE, 0);
/* set vertex buffers */
glVertexArrayVertexBuffer(vertexarray, 0, vertexbuffer, offsetof(Vertex, Position), sizeof(Vertex));
glVertexArrayVertexBuffer(vertexarray, 1, vertexbuffer, offsetof(Vertex, TexCoord), sizeof(Vertex));
/* enable attributes */
glEnableVertexArrayAttrib(vertexarray, 0);
glEnableVertexArrayAttrib(vertexarray, 1);
/* setup vertex buffer (2 triangles building a quad) */
float size = 0.9f;
vector<Vertex> vertices = {
{ { -size, -size, 0 },{ 0, 1 } },
{ { +size, -size, 0 },{ 1, 1 } },
{ { +size, +size, 0 },{ 1, 0 } },
{ { -size, -size, 0 },{ 0, 1 } },
{ { +size, +size, 0 },{ 1, 0 } },
{ { -size, +size, 0 },{ 0, 0 } },
};
glNamedBufferData(vertexbuffer, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
/* first texture: 2x2 */
Texel texturedata1[] = {
{ 0x00, 0x00, 0xFF, 0xFF },{ 0xFF, 0xFF, 0x00, 0xFF },
{ 0xFF, 0x00, 0x00, 0xFF },{ 0x00, 0xFF, 0x00, 0xFF },
};
Texture2DInfo textureinfo1;
textureinfo1.Width = 2;
textureinfo1.Height = 2;
textureinfo1.Data = texturedata1;
/* setup textures (as usual) */
glTextureStorage2D(texture[0], 1, GL_RGBA8, textureinfo1.Width, textureinfo1.Height);
glTextureSubImage2D(texture[0], 0, 0, 0, textureinfo1.Width, textureinfo1.Height, GL_RGBA, GL_UNSIGNED_BYTE, textureinfo1.Data);
glTextureParameteri(texture[0], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(texture[0], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureParameteri(texture[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(texture[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
int channels_unused = 0;
Texture2DInfo textureinfo2;
textureinfo2.Data = SOIL_load_image("image1.png", &textureinfo2.Width, &textureinfo2.Height, &channels_unused, SOIL_LOAD_RGBA);
if (!textureinfo2.Data)
{
cout << "cant load image" << endl;
return false;
}
glTextureStorage2D(texture[1], 1, GL_RGBA8, textureinfo2.Width, textureinfo2.Height);
glTextureSubImage2D(texture[1], 0, 0, 0, textureinfo2.Width, textureinfo2.Height, GL_RGBA, GL_UNSIGNED_BYTE, textureinfo2.Data);
glTextureParameteri(texture[1], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(texture[1], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureParameteri(texture[1], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(texture[1], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
SOIL_free_image_data((unsigned char*)textureinfo2.Data);
/* using GL_ARB_bindless_texture: */
/************************************************************************/
CHECK_GL_ERROR;
/* when memory allocated & parameters are set, get a "handle" for each texture */
GLuint64 handle_texture1 = glGetTextureHandleARB(texture[0]);
GLuint64 handle_texture2 = glGetTextureHandleARB(texture[1]);
/* next make sure that the textures are resident on the gpu */
glMakeTextureHandleResidentARB(handle_texture1);
glMakeTextureHandleResidentARB(handle_texture2);
/* prepare uniform buffer */
vector<GLuint64> all_texture_handles = {
handle_texture1,
handle_texture2,
};
GLsizeiptr buffersize = sizeof(GLuint64) * all_texture_handles.size();
const void* bufferdata = all_texture_handles.data();
glBindBufferBase(GL_UNIFORM_BUFFER, 1, uniformbuffer);
glNamedBufferData(uniformbuffer, buffersize, bufferdata, GL_DYNAMIC_DRAW);
/* now the textures can be used by shaders */
CHECK_GL_ERROR;
/************************************************************************/
return true;
}
void Render()
{
/* clear framebuffer */
glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
/* draw textured quad */
glUseProgram(program);
glBindVertexArray(vertexarray);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glUseProgram(0);
/* check for any error */
CHECK_GL_ERROR;
}
void CleanUp()
{
// destroy all objects
glDeleteProgram(program);
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
glDeleteVertexArrays(1, &vertexarray);
glDeleteBuffers(1, &vertexbuffer);
glDeleteBuffers(1, &uniformbuffer);
glDeleteTextures(2, texture);
CHECK_GL_ERROR;
}
void __CheckForGLError(const char* filename, int line)
{
for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)
{
cout << (char)7 << "OpenGL Error:\t" << filename << " line " << line << "\n\t";
if (error == GL_INVALID_ENUM)
cout << "GL_INVALID_ENUM";
if (error == GL_INVALID_VALUE)
cout << "GL_INVALID_VALUE";
if (error == GL_INVALID_OPERATION)
cout << "GL_INVALID_OPERATION";
if (error == GL_STACK_OVERFLOW)
cout << "GL_STACK_OVERFLOW";
if (error == GL_STACK_UNDERFLOW)
cout << "GL_STACK_UNDERFLOW";
if (error == GL_OUT_OF_MEMORY)
cout << "GL_OUT_OF_MEMORY";
if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
if (error == GL_CONTEXT_LOST)
cout << "GL_CONTEXT_LOST";
cout << endl << endl;
cin.get();
}
}
/* debug */
void APIENTRY OpenGLDebugCallback(
GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userparam)
{
/* debug user params */
DebugUserParams* params = (DebugUserParams*)userparam;
/* filter out unnecessary warnings */
if (!params->ShowNotifications)
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
return;
/* source */
string str_source;
if (source == GL_DEBUG_SOURCE_API) str_source = "API";
if (source == GL_DEBUG_SOURCE_WINDOW_SYSTEM) str_source = "Window System";
if (source == GL_DEBUG_SOURCE_SHADER_COMPILER) str_source = "Shader Compiler";
if (source == GL_DEBUG_SOURCE_THIRD_PARTY) str_source = "Third Party";
if (source == GL_DEBUG_SOURCE_APPLICATION) str_source = "Application";
if (source == GL_DEBUG_SOURCE_OTHER) str_source = "Other";
/* type */
string str_type;
if (type == GL_DEBUG_TYPE_ERROR) str_type = "Error";
if (type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) str_type = "Deprecated Behavior";
if (type == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR) str_type = "Undefined Behavior";
if (type == GL_DEBUG_TYPE_PORTABILITY) str_type = "Portability";
if (type == GL_DEBUG_TYPE_PERFORMANCE) str_type = "Performance";
if (type == GL_DEBUG_TYPE_MARKER) str_type = "Marker";
if (type == GL_DEBUG_TYPE_PUSH_GROUP) str_type = "Push Group";
if (type == GL_DEBUG_TYPE_POP_GROUP) str_type = "Pop Group";
if (type == GL_DEBUG_TYPE_OTHER) str_type = "Other";
/* severity */
string str_severity;
if (severity == GL_DEBUG_SEVERITY_HIGH) str_severity = "High";
if (severity == GL_DEBUG_SEVERITY_MEDIUM) str_severity = "Medium";
if (severity == GL_DEBUG_SEVERITY_LOW) str_severity = "Low";
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) str_severity = "Notification";
/* print message */
cout << "OpenGL Debug Message:" << endl;
cout << "----------------------------------" << endl;
cout << "ID: \t\t" << id << endl;
cout << "Source: \t" << str_source << endl;
cout << "Type: \t\t" << str_type << endl;
cout << "Severity: \t" << str_severity << endl;
cout << "Message: \t" << message << endl;
cout << "----------------------------------" << endl << endl;
}
Shader.h
#pragma once
#include <string>
#include <list>
// shader functions
std::string LoadTextFile(const std::string& filepath);
std::string ShaderTypeName(unsigned int shader);
bool CompileShader(unsigned int shader, const std::string& sourcecode);
std::string ShaderInfoLog(unsigned int shader);
bool LinkProgram(unsigned int program, const std::list<unsigned int>& shaderlist);
std::string ProgramInfoLog(unsigned int program);
Shader.cpp
#include "Shader.h"
#define GLEW_STATIC
#include <GL/glew.h>
#include <iostream>
#include <fstream>
std::string LoadTextFile(const std::string& filepath)
{
std::string result(""), line;
std::fstream f(filepath, std::ios::in);
while (f.good())
{
std::getline(f, line);
result += line + '\n';
}
return result;
}
std::string ShaderTypeName(unsigned int shader)
{
if (glIsShader(shader))
{
int type;
glGetShaderiv(shader, GL_SHADER_TYPE, &type);
if (type == GL_VERTEX_SHADER)
return "Vertex Shader";
if (type == GL_TESS_CONTROL_SHADER)
return "Tessellation Control Shader";
if (type == GL_TESS_EVALUATION_SHADER)
return "Tessellation Evaluation Shader";
if (type == GL_GEOMETRY_SHADER)
return "Geometry Shader";
if (type == GL_FRAGMENT_SHADER)
return "Fragment Shader";
if (type == GL_COMPUTE_SHADER)
return "Compute Shader";
}
return "invalid shader";
}
bool CompileShader(unsigned int shader, const std::string& sourcecode)
{
if (!glIsShader(shader))
{
std::cout << "ERROR: shader compilation failed, no valid shader specified" << std::endl;
return false;
}
if (sourcecode.empty())
{
std::cout << "ERROR: shader compilation failed, no source code specified (" << ShaderTypeName(shader) << ")" << std::endl;
return false;
}
// array of source code components
const char* sourcearray[] = { sourcecode.c_str() };
// set source code
glShaderSource(shader, 1, sourcearray, NULL);
// compile shaders
glCompileShader(shader);
// check compile status
int status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
// successfully compiled shader
if (status == GL_TRUE)
return true;
// show compile errors
std::cout << "ERROR: shader compilation failed (" << ShaderTypeName(shader) << ")" << std::endl << ShaderInfoLog(shader) << std::endl;
return false;
}
std::string ShaderInfoLog(unsigned int shader)
{
if (glIsShader(shader))
{
int logsize;
char infolog[1024] = { 0 };
glGetShaderInfoLog(shader, 1024, &logsize, infolog);
return std::string(infolog);
}
return "invalid shader";
}
bool LinkProgram(unsigned int program, const std::list<unsigned int>& shaderlist)
{
if (!glIsProgram(program))
{
std::cout << "ERROR: shader linking failed, no valid program specified" << std::endl;
return false;
}
// attach all shaders to the program
for (auto& shader : shaderlist)
{
if (glIsShader(shader))
glAttachShader(program, shader);
}
// link program
glLinkProgram(program);
// detach all shaders again
for (auto& shader : shaderlist)
{
if (glIsShader(shader))
glDetachShader(program, shader);
}
int status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
// successfully linked program
if (status == GL_TRUE)
return true;
// show link errors
std::cout << "ERROR: shader linking failed" << std::endl << ProgramInfoLog(program) << std::endl;
return false;
}
std::string ProgramInfoLog(unsigned int program)
{
if (glIsProgram(program))
{
int logsize;
char infolog[1024] = { 0 };
glGetProgramInfoLog(program, 1024, &logsize, infolog);
return std::string(infolog);
}
return "invalid program";
}
Vertexshader: "vs.glsl"
#version 450 core
#extension GL_ARB_bindless_texture : enable
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec2 in_texcoord;
out vec2 texcoord;
void main ()
{
gl_Position = vec4(in_position, 1);
texcoord = in_texcoord;
}
Fragmentshader: "fs.glsl"
#version 450 core
#extension GL_ARB_bindless_texture : enable
layout (bindless_sampler) uniform;
in vec2 texcoord;
layout (std140, binding = 1) uniform TextureBlock {
uvec4 maps[1024];
};
layout (location = 0) out vec4 out_color;
/* fix: because of layout std140 16byte alignment, get uvec2 from array of uvec4 */
uvec2 GetTexture(uint index)
{
uint index_corrected = index / 2;
if (index % 2 == 0)
return maps[index_corrected].xy;
return maps[index_corrected].zw;
}
void main ()
{
/* create samplers from uvec2 */
sampler2D tex1 = sampler2D(GetTexture(0));
sampler2D tex2 = sampler2D(GetTexture(1));
vec4 color1 = texture(tex1, texcoord);
vec4 color2 = texture(tex2, texcoord);
out_color = (color1 + color2) / 2;
}