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;

}