Skip to content
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

Texture state validation #6

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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 CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ option(GL_VALIDATION_LAYER_BUILD_TESTS "Build tests for the OpenGL Validation La
add_library(gl_validation_layer
src/context.cpp
src/shader.cpp
src/texture.cpp
include/gl_layer/context.h
include/gl_layer/private/context.h
include/gl_layer/private/types.h
Expand Down
9 changes: 9 additions & 0 deletions include/gl_layer/private/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class Context {
void glUseProgram(GLuint program);
void glDeleteProgram(GLuint program);

void glGenTextures(GLsizei count, GLuint* handles);
void glCreateTextures(GLenum target, GLsizei count, GLuint* handles);
void glBindTexture(GLenum target, GLuint texture);
void glDeleteTextures(GLsizei count, GLuint* handles);

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

Expand All @@ -35,8 +40,12 @@ class Context {
ContextGLFunctions gl;
GLuint current_program_handle = 0;

// One entry per texture target
std::unordered_map<GLTextureTarget, GLuint> bound_textures{};

std::unordered_map<GLuint, Shader> shaders{};
std::unordered_map<GLuint, Program> programs{};
std::unordered_map<GLuint, Texture> textures{};

template<typename... Args>
void output_fmt(const char* fmt, Args&& ... args) {
Expand Down
71 changes: 71 additions & 0 deletions include/gl_layer/private/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,35 @@ enum GLShaderInfoParam {
GL_ACTIVE_UNIFORMS = 0x8B86
};

enum GLTextureTarget {
NO_TEXTURE_TARGET = 0x0,
GL_TEXTURE_1D = 0x0DE0,
GL_TEXTURE_2D = 0x0DE1,
GL_TEXTURE_3D = 0x806F,
GL_TEXTURE_1D_ARRAY = 0x8C18,
GL_TEXTURE_2D_ARRAY = 0x8C1A,
GL_TEXTURE_RECTANGLE = 0x84F5,
GL_TEXTURE_CUBE_MAP = 0x8513,
GL_TEXTURE_CUBE_MAP_ARRAY = 0x9009,
GL_TEXTURE_BUFFER = 0x8C2A,
GL_TEXTURE_2D_MULTISAMPLE = 0x9100,
GL_TEXTURE_2D_MULTISAMPLE_ARRAY = 0x9102
};

enum GLFilter {
GL_NEAREST = 0x2600,
GL_LINEAR = 0x2601
};

enum GLEdgeSample {
GL_REPEAT = 0x2901,
GL_MIRRORED_REPEAT = 0x8370,
GL_CLAMP_TO_EDGE = 0x812F,
GL_CLAMP_TO_BORDER = 0x812D,
// Core in 4.4
GL_MIRROR_CLAMP_TO_EDGE = 0x8743
};

enum class CompileStatus {
UNCHECKED = -1,
FAILED = 0,
Expand Down Expand Up @@ -72,6 +101,48 @@ struct Program {
LinkStatus link_status = LinkStatus::UNCHECKED;
};

// Represents either the sampler state of a texture, or a sampler object created by glGenSamplers()
struct Sampler {
// GL_TEXTURE_MAG_FILTER
GLFilter mag_filter {};
// GL_TEXTURE_MIN_FILTER
GLFilter min_filter {};
// Core in 4.6
// GL_TEXTURE_MAX_ANISOTROPY
float anisotropy = 1.0;
// GL_TEXTURE_MAX_LOD
float max_lod = 0.0;
// GL_TEXTURE_MIN_LOD
float min_lod = 0.0;
// GL_TEXTURE_LOD_BIAS
float lod_bias = 0.0;
// GL_TEXTURE_WRAP_S
GLEdgeSample edge_s {};
// GL_TEXTURE_WRAP_T
GLEdgeSample edge_t {};
// GL_TEXTURE_WRAP_R
GLEdgeSample edge_r {};
// GL_TEXTURE_BORDER_COLOR
// From https://www.khronos.org/opengl/wiki/Sampler_Object#Sampling_parameters:
// The border color can be provided in floating-point values, Normalized Integers, or non-normalized integers, using the various forms of glSamplerParameter/glTexParameter.
// Note that the border color is a 4-component color, so you must use the v version of the function, and you must provide all four components in a single call.
// When using the fv function, the color will be stored as a float. When using iv, the color will be converted to a float via signed normalization.
// Since the components are GLint 32-bit integers, the range is from [-2^31, 2^31). When using the Ii or Iui forms, the color will be stored as signed or unsigned integers, as appropriate.
// This means we can store the color as 4 floating point values, and convert them by normalizing in the gl[Texture/Sampler]Parameter* call.
GLfloat border_color[4] {};
// GL_TEXTURE_CUBE_MAP_SEAMLESS
// May be overwritten by global parameter
GLboolean cubemap_seamless = false;
};

// Represents a texture created by glGenTextures()
struct Texture {
unsigned int handle {};
// Texture target, either as specified by first glBindTexture() call, or by
// glCreateTextures().
GLTextureTarget target {};
};

}

#endif
29 changes: 29 additions & 0 deletions src/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ const char* enum_str(GLenum v) {
case GL_LINK_STATUS: return "GL_LINK_STATUS";
case GL_INFO_LOG_LENGTH: return "GL_INFO_LOG_LENGTH";
case GL_SHADER_SOURCE_LENGTH: return "GL_SHADER_SOURCE_LENGTH";

case GL_TEXTURE_1D: return "GL_TEXTURE_1D";
case GL_TEXTURE_2D: return "GL_TEXTURE_2D";
case GL_TEXTURE_3D: return "GL_TEXTURE_3D";
case GL_TEXTURE_1D_ARRAY: return "GL_TEXTURE_1D_ARRAY";
case GL_TEXTURE_2D_ARRAY: return "GL_TEXTURE_2D_ARRAY";
case GL_TEXTURE_RECTANGLE: return "GL_TEXTURE_RECTANGLE";
case GL_TEXTURE_CUBE_MAP: return "GL_TEXTURE_CUBE_MAP";
case GL_TEXTURE_CUBE_MAP_ARRAY: return "GL_TEXTURE_CUBE_MAP_ARRAY";
case GL_TEXTURE_BUFFER: return "GL_TEXTURE_BUFFER";
case GL_TEXTURE_2D_MULTISAMPLE: return "GL_TEXTURE_2D_MULTISAMPLE";
case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: return "GL_TEXTURE_2D_MULTISAMPLE_ARRAY";
default:
return "";
}
Expand Down Expand Up @@ -99,6 +111,23 @@ void gl_layer_callback(const char* name_c, void* func_ptr, int num_args, ...) {
} else if (is_func(name, "glLinkProgram")) {
auto program = va_arg(args, GLuint);
gl_layer::g_context->glLinkProgram(program);
} else if (is_func(name, "glGenTextures")) {
auto count = va_arg(args, GLsizei);
auto textures = va_arg(args, GLuint*);
gl_layer::g_context->glGenTextures(count, textures);
} else if (is_func(name, "glCreateTextures")) {
auto target = va_arg(args, GLenum);
auto count = va_arg(args, GLsizei);
auto textures = va_arg(args, GLuint*);
gl_layer::g_context->glCreateTextures(target, count, textures);
} else if (is_func(name, "glDeleteTextures")) {
auto count = va_arg(args, GLsizei);
auto textures = va_arg(args, GLuint*);
gl_layer::g_context->glDeleteTextures(count, textures);
} else if (is_func(name, "glBindTexture")) {
auto target = va_arg(args, GLenum);
auto texture = va_arg(args, GLuint);
gl_layer::g_context->glBindTexture(target, texture);
}

va_end(args);
Expand Down
55 changes: 55 additions & 0 deletions src/texture.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <gl_layer/private/context.h>

namespace gl_layer {

void Context::glGenTextures(GLsizei count, GLuint* handles) {
for (GLsizei i = 0; i < count; ++i) {
textures.emplace(handles[i], Texture {
handles[i],
NO_TEXTURE_TARGET
});
}
}

void Context::glCreateTextures(GLenum target, GLsizei count, GLuint* handles) {
for (GLsizei i = 0; i < count; ++i) {
textures.emplace(handles[i], Texture {
handles[i],
static_cast<GLTextureTarget>(target)
});
}
}

void Context::glBindTexture(GLenum target, GLuint texture_handle) {
if (texture_handle == 0) {
// TODO: Discourage binding zero handle? (Suggest 1x1 texture to mark "invalid" instead).
return;
}

auto it = textures.find(texture_handle);
if (it == textures.end()) {
output_fmt("glBindTexture(target = %s, texture = %u): Invalid texture handle.", enum_str(target), texture_handle);
return;
}
Texture& texture = it->second;

// Target not set yet, this is the first time this texture is bound.
if (texture.target == NO_TEXTURE_TARGET) {
texture.target = static_cast<GLTextureTarget>(target);
}

if (target != texture.target) {
output_fmt("glBindTexture(target = %s, texture = %u): Invalid texture target (texture is already %s).", enum_str(target), texture_handle, enum_str(texture.target));
return;
}

bound_textures[texture.target] = texture_handle;
}

void Context::glDeleteTextures(GLsizei count, GLuint* handles) {
for (GLsizei i = 0; i < count; ++i) {
textures.erase(handles[i]);
}
}

}
Loading