#pragma once #include "capabilities.hpp" #include "Utilities/geometry.h" #include namespace gl { struct driver_state { const u32 DEPTH_BOUNDS_MIN = 0xFFFF0001; const u32 DEPTH_BOUNDS_MAX = 0xFFFF0002; const u32 DEPTH_RANGE_MIN = 0xFFFF0003; const u32 DEPTH_RANGE_MAX = 0xFFFF0004; std::unordered_map properties = {}; std::unordered_map> indexed_properties = {}; GLuint current_program = GL_NONE; std::array, 48> bound_textures{ {} }; bool enable(u32 test, GLenum cap) { auto found = properties.find(cap); if (found != properties.end() && found->second == test) return !!test; properties[cap] = test; if (test) glEnable(cap); else glDisable(cap); return !!test; } bool enablei(u32 test, GLenum cap, u32 index) { auto found = indexed_properties.find(cap); const bool exists = found != indexed_properties.end(); if (!exists) { indexed_properties[cap] = {}; indexed_properties[cap][index] = test; } else { if (found->second[index] == test) return !!test; found->second[index] = test; } if (test) glEnablei(cap, index); else glDisablei(cap, index); return !!test; } bool enable(GLenum cap) { return enable(GL_TRUE, cap); } bool enablei(GLenum cap, u32 index) { return enablei(GL_TRUE, cap, index); } bool disable(GLenum cap) { return enable(GL_FALSE, cap); } bool disablei(GLenum cap, u32 index) { return enablei(GL_FALSE, cap, index); } inline bool test_property(GLenum property, u32 test) const { auto found = properties.find(property); if (found == properties.end()) return false; return (found->second == test); } inline bool test_propertyi(GLenum property, u32 test, GLint index) const { auto found = indexed_properties.find(property); if (found == indexed_properties.end()) return false; return found->second[index] == test; } void depth_func(GLenum func) { if (!test_property(GL_DEPTH_FUNC, func)) { glDepthFunc(func); properties[GL_DEPTH_FUNC] = func; } } void depth_mask(GLboolean mask) { if (!test_property(GL_DEPTH_WRITEMASK, mask)) { glDepthMask(mask); properties[GL_DEPTH_WRITEMASK] = mask; } } void clear_depth(GLfloat depth) { u32 value = std::bit_cast(depth); if (!test_property(GL_DEPTH_CLEAR_VALUE, value)) { glClearDepth(depth); properties[GL_DEPTH_CLEAR_VALUE] = value; } } void stencil_mask(GLuint mask) { if (!test_property(GL_STENCIL_WRITEMASK, mask)) { glStencilMask(mask); properties[GL_STENCIL_WRITEMASK] = mask; } } void clear_stencil(GLint stencil) { u32 value = std::bit_cast(stencil); if (!test_property(GL_STENCIL_CLEAR_VALUE, value)) { glClearStencil(stencil); properties[GL_STENCIL_CLEAR_VALUE] = value; } } void color_maski(GLint index, u32 mask) { if (!test_propertyi(GL_COLOR_WRITEMASK, mask, index)) { glColorMaski(index, ((mask & 0x10) ? 1 : 0), ((mask & 0x20) ? 1 : 0), ((mask & 0x40) ? 1 : 0), ((mask & 0x80) ? 1 : 0)); indexed_properties[GL_COLOR_WRITEMASK][index] = mask; } } void color_maski(GLint index, bool r, bool g, bool b, bool a) { u32 mask = 0; if (r) mask |= 0x10; if (g) mask |= 0x20; if (b) mask |= 0x40; if (a) mask |= 0x80; color_maski(index, mask); } void clear_color(u8 r, u8 g, u8 b, u8 a) { u32 value = u32{ r } | u32{ g } << 8 | u32{ b } << 16 | u32{ a } << 24; if (!test_property(GL_COLOR_CLEAR_VALUE, value)) { glClearColor(r / 255.f, g / 255.f, b / 255.f, a / 255.f); properties[GL_COLOR_CLEAR_VALUE] = value; } } void clear_color(const color4f& color) { clear_color(static_cast(color.r * 255), static_cast(color.g * 255), static_cast(color.b * 255), static_cast(color.a * 255)); } void depth_bounds(float min, float max) { u32 depth_min = std::bit_cast(min); u32 depth_max = std::bit_cast(max); if (!test_property(DEPTH_BOUNDS_MIN, depth_min) || !test_property(DEPTH_BOUNDS_MAX, depth_max)) { if (get_driver_caps().NV_depth_buffer_float_supported) { glDepthBoundsdNV(min, max); } else { glDepthBoundsEXT(min, max); } properties[DEPTH_BOUNDS_MIN] = depth_min; properties[DEPTH_BOUNDS_MAX] = depth_max; } } void depth_range(float min, float max) { u32 depth_min = std::bit_cast(min); u32 depth_max = std::bit_cast(max); if (!test_property(DEPTH_RANGE_MIN, depth_min) || !test_property(DEPTH_RANGE_MAX, depth_max)) { if (get_driver_caps().NV_depth_buffer_float_supported) { glDepthRangedNV(min, max); } else { glDepthRange(min, max); } properties[DEPTH_RANGE_MIN] = depth_min; properties[DEPTH_RANGE_MAX] = depth_max; } } void logic_op(GLenum op) { if (!test_property(GL_COLOR_LOGIC_OP, op)) { glLogicOp(op); properties[GL_COLOR_LOGIC_OP] = op; } } void line_width(GLfloat width) { u32 value = std::bit_cast(width); if (!test_property(GL_LINE_WIDTH, value)) { glLineWidth(width); properties[GL_LINE_WIDTH] = value; } } void front_face(GLenum face) { if (!test_property(GL_FRONT_FACE, face)) { glFrontFace(face); properties[GL_FRONT_FACE] = face; } } void cull_face(GLenum mode) { if (!test_property(GL_CULL_FACE_MODE, mode)) { glCullFace(mode); properties[GL_CULL_FACE_MODE] = mode; } } void polygon_offset(float factor, float units) { u32 _units = std::bit_cast(units); u32 _factor = std::bit_cast(factor); if (!test_property(GL_POLYGON_OFFSET_UNITS, _units) || !test_property(GL_POLYGON_OFFSET_FACTOR, _factor)) { glPolygonOffset(factor, units); properties[GL_POLYGON_OFFSET_UNITS] = _units; properties[GL_POLYGON_OFFSET_FACTOR] = _factor; } } void use_program(GLuint program) { if (current_program == program) { return; } current_program = program; glUseProgram(program); } void bind_texture(GLuint layer, GLenum target, GLuint name, GLboolean force = GL_FALSE) { ensure(layer < 48); auto& bound = bound_textures[layer][target]; if (bound != name || force) { glActiveTexture(GL_TEXTURE0 + layer); glBindTexture(target, name); bound = name; } } void unbind_texture(GLenum target, GLuint name) { // To be called with glDeleteTextures. // OpenGL internally unbinds the texture on delete, but then reuses the same ID when GenTextures is called again! // This can also be avoided using unique internal names, such as 64-bit handles, but that involves changing a lot of code for little benefit for (auto& layer : bound_textures) { if (layer.empty()) { continue; } if (auto found = layer.find(target); found != layer.end() && found->second == name) { // Actually still bound! found->second = GL_NONE; return; } } } }; class command_context { driver_state* drv; public: command_context() : drv(nullptr) {} command_context(driver_state& drv_) : drv(&drv_) {} driver_state* operator -> () { return drv; } }; void set_command_context(gl::command_context& ctx); void set_command_context(gl::driver_state& ctx); gl::command_context get_command_context(); void set_primary_context_thread(bool = true); bool is_primary_context_thread(); class fence; void flush_command_queue(fence& fence_obj); }