Skip to content

Shader validation #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea/
cmake-build-debug/
/build
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ option(GL_VALIDATION_LAYER_BUILD_TESTS "Build tests for the OpenGL Validation La
add_library(gl_validation_layer
src/context.cpp
src/shader.cpp
include/gl_layer/context.h
include/gl_layer/private/context.h
include/gl_layer/private/types.h
)

target_include_directories(gl_validation_layer PUBLIC include/)
Expand Down
11 changes: 10 additions & 1 deletion include/gl_layer/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@
extern "C" {
#endif

typedef struct ContextGLFunctions
{
// TODO: replace the types in the parameters with proper platform-detected ones
// TODO: evaluate whether * should be replaced with something like APIENTRYP in glad
void (*GetActiveUniform)(unsigned, unsigned, int, int*, int*, unsigned*, char*);
int (*GetUniformLocation)(unsigned, const char*);
void (*GetProgramiv)(unsigned, unsigned, int*);
}ContextGLFunctions;

/**
* @brief Initialize the OpenGL Validation Layer
* @param gl_version_major OpenGL context major version.
* @param gl_version_minor OpenGL context minor version.
* @return 0 on success, any other value on error.
*/
int gl_layer_init(unsigned int gl_version_major, unsigned int gl_version_minor);
int gl_layer_init(unsigned int gl_version_major, unsigned int gl_version_minor, const ContextGLFunctions* gl_functions);

/**
* @brief Terminate the OpenGL Validation Layer.
Expand Down
23 changes: 15 additions & 8 deletions include/gl_layer/private/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,32 @@ namespace gl_layer {

class Context {
public:
explicit Context(Version version);
explicit Context(Version version, const ContextGLFunctions* gl_functions);

void set_output_callback(GLLayerOutputFun callback, void* user_data);

void glCompileShader(unsigned int handle);
void glGetShaderiv(unsigned int handle, GLenum param, int* params);
void glAttachShader(unsigned int program, unsigned int shader);
void glCompileShader(GLuint program);
void glGetShaderiv(GLuint program, GLenum param, GLint* params);
void glAttachShader(GLuint program, GLuint shader);

void glGetProgramiv(unsigned int handle, GLenum param, int* params);
void glUseProgram(unsigned int handle);
void glGetProgramiv(GLuint program, GLenum param, GLint* params);
void glLinkProgram(GLuint program);
void glUseProgram(GLuint program);
void glDeleteProgram(GLuint program);

void validate_program_bound(std::string_view func_name);
bool validate_program_status(GLuint program);

private:
Version gl_version;

GLLayerOutputFun output_fun = nullptr;
void* output_user_data = nullptr;
ContextGLFunctions gl;
GLuint current_program_handle = 0;

std::unordered_map<unsigned int, Shader> shaders{};
std::unordered_map<unsigned int, Program> programs{};
std::unordered_map<GLuint, Shader> shaders{};
std::unordered_map<GLuint, Program> programs{};

template<typename... Args>
void output_fmt(const char* fmt, Args&& ... args) {
Expand Down
41 changes: 37 additions & 4 deletions include/gl_layer/private/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,40 @@
#define GL_VALIDATION_LAYER_TYPES_H_

#include <vector>
#include <unordered_map>
#include <cstdint>

namespace gl_layer {

using GLenum = unsigned int;
using GLenum = std::uint32_t;
using GLuint = std::uint32_t;
using GLint = std::int32_t;
using GLsizei = std::int32_t;
using GLchar = char;
using GLfloat = float;
using GLboolean = bool;

enum GLShaderInfoParam {
GL_SHADER_TYPE = 0x8B4F,
GL_DELETE_STATUS = 0x8B80,
GL_COMPILE_STATUS = 0x8B81,
GL_LINK_STATUS = 0x8B82,
GL_INFO_LOG_LENGTH = 0x8B84,
GL_SHADER_SOURCE_LENGTH = 0x8B88
GL_SHADER_SOURCE_LENGTH = 0x8B88,
GL_ACTIVE_UNIFORM_MAX_LENGTH = 0x8B87,
GL_ACTIVE_UNIFORMS = 0x8B86
};

enum class CompileStatus {
UNCHECKED = -1,
FAILED = 0,
OK = 1,
};

enum class LinkStatus {
UNCHECKED = -1,
FAILED = 0,
OK = 1,
};

const char* enum_str(GLenum v);
Expand All @@ -23,20 +45,31 @@ struct Version {
unsigned int minor = 0;
};

struct UniformInfo {
GLint array_size;
GLenum type;
// std::string name // the name is probably not useful for us

bool operator==(const UniformInfo& other) const {
return array_size == other.array_size && type == other.type;
}
};

// Represents a shader returned by glCreateShader
struct Shader {
unsigned int handle {};
// If this is -1, this means the compile status was never checked by the host application.
// This is an error that should be reported.
int compile_status = -1;
CompileStatus compile_status = CompileStatus::UNCHECKED;
};

// Represents a shader program returned by glCreateProgram
struct Program {
unsigned int handle {};
std::vector<unsigned int> shaders {};
std::unordered_map<GLint, UniformInfo> uniforms {};
// If this is -1, this means the status was never checked by the host application.
int link_status = -1;
LinkStatus link_status = LinkStatus::UNCHECKED;
};

}
Expand Down
43 changes: 28 additions & 15 deletions src/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ static void default_output_func(const char* text, void* = nullptr) {
printf("%s\n", text);
}

Context::Context(Version version) : gl_version(version) {
Context::Context(Version version, const ContextGLFunctions* gl_functions)
: gl_version(version), gl(*gl_functions) {
output_fun = &default_output_func;
}

Expand All @@ -42,12 +43,16 @@ Context* g_context = nullptr;

} // namespace gl_layer

static bool is_func(const char* name, std::string_view func) {
static bool is_func(std::string_view name, std::string_view func) {
return func == name;
}

int gl_layer_init(unsigned int gl_version_major, unsigned int gl_version_minor) {
gl_layer::g_context = new gl_layer::Context(gl_layer::Version{ gl_version_major, gl_version_minor });
static bool func_has(std::string_view name, std::string_view substr) {
return name.find(substr) != std::string_view::npos;
}

int gl_layer_init(unsigned int gl_version_major, unsigned int gl_version_minor, const ContextGLFunctions* gl_functions) {
gl_layer::g_context = new gl_layer::Context(gl_layer::Version{ gl_version_major, gl_version_minor }, gl_functions);
if (gl_layer::g_context) return 0;

return -1;
Expand All @@ -57,35 +62,43 @@ void gl_layer_terminate() {
delete gl_layer::g_context;
}

void gl_layer_callback(const char* name, void* func_ptr, int num_args, ...) {
void gl_layer_callback(const char* name_c, void* func_ptr, int num_args, ...) {
if (!gl_layer::g_context) {
// Report error: context not initialized.
return;
}

using namespace gl_layer; // GL types

std::string_view name = name_c;

va_list args;
va_start(args, num_args);

if (is_func(name, "glCompileShader")) {
gl_layer::g_context->glCompileShader(va_arg(args, unsigned int));
auto shader = va_arg(args, GLuint);
gl_layer::g_context->glCompileShader(shader);
} else if (is_func(name, "glGetShaderiv")) {
unsigned int shader = va_arg(args, unsigned int);
gl_layer::GLenum param = va_arg(args, gl_layer::GLenum);
int* params = va_arg(args, int*);
auto shader = va_arg(args, GLuint);
gl_layer::GLenum param = va_arg(args, GLenum);
auto* params = va_arg(args, GLint*);
gl_layer::g_context->glGetShaderiv(shader, param, params);
} else if (is_func(name, "glAttachShader")) {
// Note that the parameters must be pulled outside the function call, since there is no guarantee that they will evaluate in order!
unsigned int program = va_arg(args, unsigned int);
unsigned int shader = va_arg(args, unsigned int);
auto program = va_arg(args, GLuint);
auto shader = va_arg(args, GLuint);
gl_layer::g_context->glAttachShader(program, shader);
} else if (is_func(name, "glGetProgramiv")) {
unsigned int program = va_arg(args, unsigned int);
gl_layer::GLenum param = va_arg(args, gl_layer::GLenum);
int* params = va_arg(args, int*);
auto program = va_arg(args, GLuint);
gl_layer::GLenum param = va_arg(args, GLenum);
auto* params = va_arg(args, GLint*);
gl_layer::g_context->glGetProgramiv(program, param, params);
} else if (is_func(name, "glUseProgram")) {
unsigned int program = va_arg(args, unsigned int);
auto program = va_arg(args, GLuint);
gl_layer::g_context->glUseProgram(program);
} else if (is_func(name, "glLinkProgram")) {
auto program = va_arg(args, GLuint);
gl_layer::g_context->glLinkProgram(program);
}

va_end(args);
Expand Down
Loading