/* * Copyright (C) 2023 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #include #include #include #include #include #include #include #include #include namespace { static constexpr GLenum stencil_face_lut[] = { GL_NONE, // 0 GL_FRONT, // stencil_face_front_bit GL_BACK, // stencil_face_back_bit GL_FRONT_AND_BACK // stencil_face_front_and_back }; static constexpr GLenum stencil_op_lut[] = { GL_KEEP, // stencil_op::keep GL_ZERO, // stencil_op::zero GL_REPLACE, // stencil_op::replace GL_INCR, // stencil_op::increment_and_clamp GL_DECR, // stencil_op::decrement_and_clamp GL_INVERT, // stencil_op::invert GL_INCR_WRAP, // stencil_op::increment_and_wrap GL_DECR_WRAP // stencil_op::decrement_and_wrap }; static constexpr GLenum compare_op_lut[] = { GL_NEVER, // compare_op::never GL_LESS, // compare_op::less GL_EQUAL, // compare_op::equal GL_LEQUAL, // compare_op::less_or_equal GL_GREATER, // compare_op::greater GL_NOTEQUAL, // compare_op::not_equal GL_GEQUAL, // compare_op::greater_or_equal GL_ALWAYS // compare_op::always }; static constexpr GLenum provoking_vertex_mode_lut[] = { GL_FIRST_VERTEX_CONVENTION, // provoking_vertex_mode::first GL_LAST_VERTEX_CONVENTION // provoking_vertex_mode::last }; static constexpr GLenum primitive_topology_lut[] = { GL_POINTS, // primitive_topology::point_list GL_LINES, // primitive_topology::line_list GL_LINE_STRIP, // primitive_topology::line_strip, GL_TRIANGLES, // primitive_topology::triangle_list GL_TRIANGLE_STRIP, // primitive_topology::triangle_strip GL_TRIANGLE_FAN, // primitive_topology::triangle_fan GL_LINES_ADJACENCY, // primitive_topology::line_list_with_adjacency GL_LINE_STRIP_ADJACENCY, // primitive_topology::line_strip_with_adjacency GL_TRIANGLES_ADJACENCY, // primitive_topology::triangle_list_with_adjacency GL_TRIANGLE_STRIP_ADJACENCY, // primitive_topology::triangle_strip_with_adjacency GL_PATCHES // primitive_topology::patch_list }; static constexpr GLenum logic_op_lut[] = { GL_CLEAR , // logic_op::bitwise_clear GL_AND , // logic_op::bitwise_and GL_AND_REVERSE , // logic_op::bitwise_and_reverse GL_COPY , // logic_op::bitwise_copy GL_AND_INVERTED , // logic_op::bitwise_and_inverted GL_NOOP , // logic_op::bitwise_no_op GL_XOR , // logic_op::bitwise_xor GL_OR , // logic_op::bitwise_or GL_NOR , // logic_op::bitwise_nor GL_EQUIV , // logic_op::bitwise_equivalent GL_INVERT , // logic_op::bitwise_invert GL_OR_REVERSE , // logic_op::bitwise_or_reverse GL_COPY_INVERTED, // logic_op::bitwise_copy_inverted GL_OR_INVERTED , // logic_op::bitwise_or_inverted GL_NAND , // logic_op::bitwise_nand GL_SET // logic_op::bitwise_set }; static constexpr GLenum blend_factor_lut[] = { GL_ZERO , // blend_factor::zero GL_ONE , // blend_factor::one GL_SRC_COLOR , // blend_factor::src_color GL_ONE_MINUS_SRC_COLOR , // blend_factor::one_minus_src_color GL_DST_COLOR , // blend_factor::dst_color GL_ONE_MINUS_DST_COLOR , // blend_factor::one_minus_dst_color GL_SRC_ALPHA , // blend_factor::src_alpha GL_ONE_MINUS_SRC_ALPHA , // blend_factor::one_minus_src_alpha GL_DST_ALPHA , // blend_factor::dst_alpha GL_ONE_MINUS_DST_ALPHA , // blend_factor::one_minus_dst_alpha GL_CONSTANT_COLOR , // blend_factor::constant_color GL_ONE_MINUS_CONSTANT_COLOR, // blend_factor::one_minus_constant_color GL_CONSTANT_ALPHA , // blend_factor::constant_alpha GL_ONE_MINUS_CONSTANT_ALPHA, // blend_factor::one_minus_constant_alpha GL_SRC_ALPHA_SATURATE , // blend_factor::src_alpha_saturate GL_SRC1_COLOR , // blend_factor::src1_color GL_ONE_MINUS_SRC1_COLOR , // blend_factor::one_minus_src1_color GL_SRC1_ALPHA , // blend_factor::src1_alpha GL_ONE_MINUS_SRC1_ALPHA // blend_factor::one_minus_src1_alpha }; static constexpr GLenum blend_op_lut[] = { GL_FUNC_ADD , // blend_op::add GL_FUNC_SUBTRACT , // blend_op::subtract GL_FUNC_REVERSE_SUBTRACT, // blend_op::reverse_subtract GL_MIN , // blend_op::min GL_MAX // blend_op::max }; void gl_debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* user_param) { const auto src_str = [source]() -> const char* { switch (source) { case GL_DEBUG_SOURCE_API: return "API"; case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "window system"; case GL_DEBUG_SOURCE_SHADER_COMPILER: return "shader compiler"; case GL_DEBUG_SOURCE_THIRD_PARTY: return "third party"; case GL_DEBUG_SOURCE_APPLICATION: return "application"; case GL_DEBUG_SOURCE_OTHER: default: return "other"; } }(); const auto type_str = [type]() -> const char* { switch (type) { case GL_DEBUG_TYPE_ERROR: return "error"; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "deprecated behavior"; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "undefined behavior"; case GL_DEBUG_TYPE_PORTABILITY: return "portability"; case GL_DEBUG_TYPE_PERFORMANCE: return "performance"; case GL_DEBUG_TYPE_MARKER: return "marker"; case GL_DEBUG_TYPE_OTHER: default: return "message"; } }(); const auto severity_str = [severity]() -> const char* { switch (severity) { case GL_DEBUG_SEVERITY_LOW: return "low severity"; case GL_DEBUG_SEVERITY_MEDIUM: return "medium severity"; case GL_DEBUG_SEVERITY_HIGH: return "high severity"; case GL_DEBUG_SEVERITY_NOTIFICATION: default: return "notification"; } }(); switch (type) { case GL_DEBUG_TYPE_ERROR: { std::string formatted_message; std::format_to(std::back_inserter(formatted_message), "OpenGL {} {} ({}) {}: {}", src_str, type_str, severity_str, id, message); debug::log_fatal("{}", formatted_message); throw std::runtime_error(formatted_message); break; } case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: debug::log_error("OpenGL {} {} ({}) {}: {}", src_str, type_str, severity_str, id, message); break; case GL_DEBUG_TYPE_PORTABILITY: case GL_DEBUG_TYPE_PERFORMANCE: debug::log_warning("OpenGL {} {} ({}) {}: {}", src_str, type_str, severity_str, id, message); break; case GL_DEBUG_TYPE_MARKER: case GL_DEBUG_TYPE_OTHER: default: debug::log_debug("OpenGL {} {} ({}) {}: {}", src_str, type_str, severity_str, id, message); break; } } } namespace gl { pipeline::pipeline() { #if defined(DEBUG) glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback(gl_debug_message_callback, nullptr); #endif // Fetch limitations glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_sampler_anisotropy); // Fetch dimensions of default framebuffer GLint gl_scissor_box[4] = {0, 0, 0, 0}; glGetIntegerv(GL_SCISSOR_BOX, gl_scissor_box); m_default_framebuffer_dimensions[0] = static_cast(gl_scissor_box[2]); m_default_framebuffer_dimensions[1] = static_cast(gl_scissor_box[3]); // Enable seamless cubemap filtering glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Set clip control to lower left, 0 to 1 glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); // Disable multisampling glDisable(GL_MULTISAMPLE); // Set byte-alignment for packing and unpacking pixel rows glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Fetch pipeline state fetch_vertex_input_state(); fetch_input_assembly_state(); fetch_viewport_state(); fetch_rasterization_state(); fetch_depth_stencil_state(); fetch_color_blend_state(); fetch_clear_value(); } // void pipeline::set_vertex_input(std::span vertex_bindings, std::span vertex_attributes) // { // m_vertex_input_state.vertex_bindings.assign(vertex_bindings.begin(), vertex_bindings.end()); // m_vertex_input_state.vertex_attributes.assign(vertex_attributes.begin(), vertex_attributes.end()); // } void pipeline::bind_framebuffer(const gl::framebuffer* framebuffer) { if (m_framebuffer != framebuffer) { m_framebuffer = framebuffer; if (m_framebuffer) { glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer->m_gl_named_framebuffer); } else { glBindFramebuffer(GL_FRAMEBUFFER, 0); } } } void pipeline::bind_shader_program(const gl::shader_program* shader_program) { if (m_shader_program != shader_program) { m_shader_program = shader_program; if (m_shader_program) { glUseProgram(m_shader_program->gl_program_id); } else { glUseProgram(0); } } } void pipeline::bind_vertex_array(const vertex_array* array) { if (m_vertex_array != array) { m_vertex_array = array; if (m_vertex_array) { glBindVertexArray(m_vertex_array->m_gl_named_array); } else { glBindVertexArray(0); } } } void pipeline::bind_vertex_buffers(std::uint32_t first_binding, std::span buffers, std::span offsets, std::span strides) { if (!m_vertex_array) { throw std::runtime_error("Failed to bind vertex buffer: no vertex array bound."); } if (offsets.size() < buffers.size()) { throw std::out_of_range("Vertex binding offset out of range."); } if (strides.size() < buffers.size()) { throw std::out_of_range("Vertex binding stride out of range."); } for (std::size_t i = 0; i < buffers.size(); ++i) { glVertexArrayVertexBuffer ( m_vertex_array->m_gl_named_array, static_cast(first_binding + i), buffers[i]->m_gl_named_buffer, static_cast(offsets[i]), static_cast(strides[i]) ); } } void pipeline::set_primitive_topology(primitive_topology topology) { if (m_input_assembly_state.topology != topology) { m_input_assembly_state.topology = topology; } } void pipeline::set_primitive_restart_enabled(bool enabled) { if (m_input_assembly_state.primitive_restart_enabled != enabled) { if (enabled) { glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); } else { glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX); } m_input_assembly_state.primitive_restart_enabled = enabled; } } void pipeline::set_viewport(std::uint32_t first_viewport, std::span viewports) { // Bounds check if (first_viewport + viewports.size() > m_max_viewports) { throw std::out_of_range("Viewport index out of range."); } // Ignore empty commands if (!viewports.size()) { return; } const auto& active_viewport = m_viewport_state.viewports.front(); const auto& viewport = viewports.front(); // Update viewport position and dimensions if (active_viewport.width != viewport.width || active_viewport.height != viewport.height || active_viewport.x != viewport.x || active_viewport.y != viewport.y) { glViewport ( static_cast(viewport.x), static_cast(viewport.y), std::max(0, static_cast(viewport.width)), std::max(0, static_cast(viewport.height)) ); } // Update viewport depth range if (active_viewport.min_depth != viewport.min_depth || active_viewport.max_depth != viewport.max_depth) { glDepthRange(viewport.min_depth, viewport.max_depth); } // Update viewport state std::copy(viewports.begin(), viewports.end(), m_viewport_state.viewports.begin() + first_viewport); } void pipeline::set_scissor(std::uint32_t first_scissor, std::span scissors) { // Bounds check if (first_scissor + scissors.size() > m_max_viewports) { throw std::out_of_range("Scissor region index out of range."); } // Ignore empty commands if (scissors.empty()) { return; } const auto& active_scissor = m_viewport_state.scissors.front(); const auto& scissor = scissors.front(); // Update scissor region if (active_scissor.width != scissor.width || active_scissor.height != scissor.height || active_scissor.x != scissor.x || active_scissor.y != scissor.y) { glScissor ( static_cast(scissor.x), static_cast(scissor.y), std::max(0, static_cast(scissor.width)), std::max(0, static_cast(scissor.height)) ); } // Update viewport state std::copy(scissors.begin(), scissors.end(), m_viewport_state.scissors.begin() + first_scissor); } void pipeline::set_rasterizer_discard_enabled(bool enabled) { if (m_rasterization_state.rasterizer_discard_enabled != enabled) { if (enabled) { glEnable(GL_RASTERIZER_DISCARD); } else { glDisable(GL_RASTERIZER_DISCARD); } m_rasterization_state.rasterizer_discard_enabled = enabled; } } void pipeline::set_fill_mode(fill_mode mode) { if (m_rasterization_state.fill_mode != mode) { switch (mode) { case fill_mode::fill: glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case fill_mode::line: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); break; case fill_mode::point: glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); break; default: break; } m_rasterization_state.fill_mode = mode; } } void pipeline::set_cull_mode(cull_mode mode) { if (m_rasterization_state.cull_mode != mode) { if (mode == cull_mode::none) { glDisable(GL_CULL_FACE); } else { if (m_rasterization_state.cull_mode == cull_mode::none) { glEnable(GL_CULL_FACE); } switch (mode) { case cull_mode::back: glCullFace(GL_BACK); break; case cull_mode::front: glCullFace(GL_FRONT); break; case cull_mode::front_and_back: glCullFace(GL_FRONT_AND_BACK); break; default: break; } } m_rasterization_state.cull_mode = mode; } } void pipeline::set_front_face(front_face face) { if (m_rasterization_state.front_face != face) { glFrontFace(face == front_face::counter_clockwise ? GL_CCW : GL_CW); m_rasterization_state.front_face = face; } } void pipeline::set_depth_bias_enabled(bool enabled) { if (m_rasterization_state.depth_bias_enabled != enabled) { if (enabled) { glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_POINT); } else { glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_POLYGON_OFFSET_LINE); glDisable(GL_POLYGON_OFFSET_POINT); } m_rasterization_state.depth_bias_enabled = enabled; } } void pipeline::set_depth_bias_factors(float constant_factor, float slope_factor) { // Update depth bias factors if (m_rasterization_state.depth_bias_constant_factor != constant_factor || m_rasterization_state.depth_bias_slope_factor != slope_factor) { glPolygonOffset(slope_factor, constant_factor); m_rasterization_state.depth_bias_constant_factor = constant_factor; m_rasterization_state.depth_bias_slope_factor = slope_factor; } } void pipeline::set_depth_clamp_enabled(bool enabled) { if (m_rasterization_state.depth_clamp_enabled != enabled) { if (enabled) { glEnable(GL_DEPTH_CLAMP); } else { glDisable(GL_DEPTH_CLAMP); } m_rasterization_state.depth_clamp_enabled = enabled; } } void pipeline::set_scissor_test_enabled(bool enabled) { if (m_rasterization_state.scissor_test_enabled != enabled) { if (enabled) { glEnable(GL_SCISSOR_TEST); } else { glDisable(GL_SCISSOR_TEST); } m_rasterization_state.scissor_test_enabled = enabled; } } void pipeline::set_provoking_vertex_mode(provoking_vertex_mode mode) { if (m_rasterization_state.provoking_vertex_mode != mode) { const auto gl_provoking_vertex_mode = provoking_vertex_mode_lut[std::to_underlying(mode)]; glProvokingVertex(gl_provoking_vertex_mode); m_rasterization_state.provoking_vertex_mode = mode; } } void pipeline::set_point_size(float size) { if (m_rasterization_state.point_size != size) { glPointSize(size); m_rasterization_state.point_size = size; } } void pipeline::set_line_width(float width) { if (m_rasterization_state.line_width != width) { glLineWidth(width); m_rasterization_state.line_width = width; } } void pipeline::set_depth_test_enabled(bool enabled) { if (m_depth_stencil_state.depth_test_enabled != enabled) { m_depth_stencil_state.depth_test_enabled = enabled; if (enabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } } void pipeline::set_depth_write_enabled(bool enabled) { if (m_depth_stencil_state.depth_write_enabled != enabled) { m_depth_stencil_state.depth_write_enabled = enabled; glDepthMask(enabled); } } void pipeline::set_depth_compare_op(gl::compare_op compare_op) { if (m_depth_stencil_state.depth_compare_op != compare_op) { m_depth_stencil_state.depth_compare_op = compare_op; const auto gl_compare_op = compare_op_lut[std::to_underlying(compare_op)]; glDepthFunc(gl_compare_op); } } void pipeline::set_stencil_test_enabled(bool enabled) { if (m_depth_stencil_state.stencil_test_enabled != enabled) { m_depth_stencil_state.stencil_test_enabled = enabled; if (enabled) { glEnable(GL_STENCIL_TEST); } else { glDisable(GL_STENCIL_TEST); } } } void pipeline::set_stencil_op(std::uint8_t face_mask, stencil_op fail_op, stencil_op pass_op, stencil_op depth_fail_op, gl::compare_op compare_op) { bool stencil_op_updated = false; bool compare_op_updated = false; if (face_mask & stencil_face_front_bit) { if (m_depth_stencil_state.stencil_front.fail_op != fail_op || m_depth_stencil_state.stencil_front.pass_op != pass_op || m_depth_stencil_state.stencil_front.depth_fail_op != depth_fail_op) { m_depth_stencil_state.stencil_front.fail_op = fail_op; m_depth_stencil_state.stencil_front.pass_op = pass_op; m_depth_stencil_state.stencil_front.depth_fail_op = depth_fail_op; stencil_op_updated = true; } if (m_depth_stencil_state.stencil_front.compare_op != compare_op) { m_depth_stencil_state.stencil_front.compare_op = compare_op; compare_op_updated = true; } } if (face_mask & stencil_face_back_bit) { if (m_depth_stencil_state.stencil_back.fail_op != fail_op || m_depth_stencil_state.stencil_back.pass_op != pass_op || m_depth_stencil_state.stencil_back.depth_fail_op != depth_fail_op) { m_depth_stencil_state.stencil_back.fail_op = fail_op; m_depth_stencil_state.stencil_back.pass_op = pass_op; m_depth_stencil_state.stencil_back.depth_fail_op = depth_fail_op; stencil_op_updated = true; } if (m_depth_stencil_state.stencil_back.compare_op != compare_op) { m_depth_stencil_state.stencil_back.compare_op = compare_op; compare_op_updated = true; } } if (stencil_op_updated || compare_op_updated) { const auto gl_face = stencil_face_lut[face_mask]; if (stencil_op_updated) { const auto gl_fail_op = stencil_op_lut[std::to_underlying(fail_op)]; const auto gl_pass_op = stencil_op_lut[std::to_underlying(pass_op)]; const auto gl_depth_fail_op = stencil_op_lut[std::to_underlying(depth_fail_op)]; glStencilOpSeparate(gl_face, gl_fail_op, gl_depth_fail_op, gl_pass_op); } if (compare_op_updated) { const auto gl_compare_op = compare_op_lut[std::to_underlying(compare_op)]; if (face_mask == stencil_face_front_and_back) { if (m_depth_stencil_state.stencil_front.reference == m_depth_stencil_state.stencil_back.reference && m_depth_stencil_state.stencil_front.compare_mask == m_depth_stencil_state.stencil_back.compare_mask) { glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(m_depth_stencil_state.stencil_front.reference), std::bit_cast(m_depth_stencil_state.stencil_front.compare_mask)); } else { glStencilFuncSeparate(GL_FRONT, gl_compare_op, std::bit_cast(m_depth_stencil_state.stencil_front.reference), std::bit_cast(m_depth_stencil_state.stencil_front.compare_mask)); glStencilFuncSeparate(GL_BACK, gl_compare_op, std::bit_cast(m_depth_stencil_state.stencil_back.reference), std::bit_cast(m_depth_stencil_state.stencil_back.compare_mask)); } } else if (face_mask == stencil_face_front_bit) { glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(m_depth_stencil_state.stencil_front.reference), std::bit_cast(m_depth_stencil_state.stencil_front.compare_mask)); } else { glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(m_depth_stencil_state.stencil_back.reference), std::bit_cast(m_depth_stencil_state.stencil_back.compare_mask)); } } } } void pipeline::set_stencil_compare_mask(std::uint8_t face_mask, std::uint32_t compare_mask) { bool compare_mask_updated = false; if (face_mask & stencil_face_front_bit) { if (m_depth_stencil_state.stencil_front.compare_mask != compare_mask) { m_depth_stencil_state.stencil_front.compare_mask = compare_mask; compare_mask_updated = true; } } if (face_mask & stencil_face_back_bit) { if (m_depth_stencil_state.stencil_back.compare_mask != compare_mask) { m_depth_stencil_state.stencil_back.compare_mask = compare_mask; compare_mask_updated = true; } } if (compare_mask_updated) { const auto gl_face = stencil_face_lut[face_mask]; if (face_mask == stencil_face_front_and_back) { if (m_depth_stencil_state.stencil_front.reference == m_depth_stencil_state.stencil_back.reference && m_depth_stencil_state.stencil_front.compare_op == m_depth_stencil_state.stencil_back.compare_op) { const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)]; glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(m_depth_stencil_state.stencil_front.reference), static_cast(compare_mask)); } else { const auto gl_compare_op_front = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)]; const auto gl_compare_op_back = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_back.compare_op)]; glStencilFuncSeparate(GL_FRONT, gl_compare_op_front, std::bit_cast(m_depth_stencil_state.stencil_front.reference), std::bit_cast(compare_mask)); glStencilFuncSeparate(GL_BACK, gl_compare_op_back, std::bit_cast(m_depth_stencil_state.stencil_back.reference), std::bit_cast(compare_mask)); } } else if (face_mask == stencil_face_front_bit) { const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)]; glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(m_depth_stencil_state.stencil_front.reference), std::bit_cast(compare_mask)); } else { const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_back.compare_op)]; glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(m_depth_stencil_state.stencil_back.reference), std::bit_cast(compare_mask)); } } } void pipeline::set_stencil_reference(std::uint8_t face_mask, std::uint32_t reference) { bool reference_updated = false; if (face_mask & stencil_face_front_bit) { if (m_depth_stencil_state.stencil_front.reference != reference) { m_depth_stencil_state.stencil_front.reference = reference; reference_updated = true; } } if (face_mask & stencil_face_back_bit) { if (m_depth_stencil_state.stencil_back.reference != reference) { m_depth_stencil_state.stencil_back.reference = reference; reference_updated = true; } } if (reference_updated) { const auto gl_face = stencil_face_lut[face_mask]; if (face_mask == stencil_face_front_and_back) { if (m_depth_stencil_state.stencil_front.compare_mask == m_depth_stencil_state.stencil_back.compare_mask && m_depth_stencil_state.stencil_front.compare_op == m_depth_stencil_state.stencil_back.compare_op) { const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)]; glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(reference), std::bit_cast(m_depth_stencil_state.stencil_front.compare_mask)); } else { const auto gl_compare_op_front = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)]; const auto gl_compare_op_back = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_back.compare_op)]; glStencilFuncSeparate(GL_FRONT, gl_compare_op_front, std::bit_cast(reference), std::bit_cast(m_depth_stencil_state.stencil_front.compare_mask)); glStencilFuncSeparate(GL_BACK, gl_compare_op_back, std::bit_cast(reference), std::bit_cast(m_depth_stencil_state.stencil_back.compare_mask)); } } else if (face_mask == stencil_face_front_bit) { const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)]; glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(reference), std::bit_cast(m_depth_stencil_state.stencil_front.compare_mask)); } else { const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_back.compare_op)]; glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast(reference), std::bit_cast(m_depth_stencil_state.stencil_back.compare_mask)); } } } void pipeline::set_stencil_write_mask(std::uint8_t face_mask, std::uint32_t write_mask) { bool write_mask_updated = false; if (face_mask & stencil_face_front_bit) { if (m_depth_stencil_state.stencil_front.write_mask != write_mask) { m_depth_stencil_state.stencil_front.write_mask = write_mask; write_mask_updated = true; } } if (face_mask & stencil_face_back_bit) { if (m_depth_stencil_state.stencil_back.write_mask != write_mask) { m_depth_stencil_state.stencil_back.write_mask = write_mask; write_mask_updated = true; } } if (write_mask_updated) { const auto gl_face = stencil_face_lut[face_mask]; glStencilMaskSeparate(gl_face, std::bit_cast(write_mask)); } } void pipeline::set_logic_op_enabled(bool enabled) { if (m_color_blend_state.logic_op_enabled != enabled) { m_color_blend_state.logic_op_enabled = enabled; if (enabled) { glEnable(GL_COLOR_LOGIC_OP); } else { glDisable(GL_COLOR_LOGIC_OP); } } } void pipeline::set_logic_op(gl::logic_op logic_op) { if (m_color_blend_state.logic_op != logic_op) { m_color_blend_state.logic_op = logic_op; const auto gl_logic_op = logic_op_lut[std::to_underlying(logic_op)]; glLogicOp(gl_logic_op); } } void pipeline::set_color_blend_enabled(bool enabled) { if (m_color_blend_state.blend_enabled != enabled) { m_color_blend_state.blend_enabled = enabled; if (enabled) { glEnable(GL_BLEND); } else { glDisable(GL_BLEND); } } } void pipeline::set_color_blend_equation(const color_blend_equation& equation) { if (m_color_blend_state.color_blend_equation.src_color_blend_factor != equation.src_color_blend_factor || m_color_blend_state.color_blend_equation.dst_color_blend_factor != equation.dst_color_blend_factor || m_color_blend_state.color_blend_equation.src_alpha_blend_factor != equation.src_alpha_blend_factor || m_color_blend_state.color_blend_equation.dst_alpha_blend_factor != equation.dst_alpha_blend_factor) { m_color_blend_state.color_blend_equation.src_color_blend_factor = equation.src_color_blend_factor; m_color_blend_state.color_blend_equation.dst_color_blend_factor = equation.dst_color_blend_factor; m_color_blend_state.color_blend_equation.src_alpha_blend_factor = equation.src_alpha_blend_factor; m_color_blend_state.color_blend_equation.dst_alpha_blend_factor = equation.dst_alpha_blend_factor; const auto gl_src_rgb = blend_factor_lut[std::to_underlying(equation.src_color_blend_factor)]; const auto gl_dst_rgb = blend_factor_lut[std::to_underlying(equation.dst_color_blend_factor)]; const auto gl_src_alpha = blend_factor_lut[std::to_underlying(equation.src_alpha_blend_factor)]; const auto gl_dst_alpha = blend_factor_lut[std::to_underlying(equation.dst_alpha_blend_factor)]; glBlendFuncSeparate(gl_src_rgb, gl_dst_rgb, gl_src_alpha, gl_dst_alpha); } if (m_color_blend_state.color_blend_equation.color_blend_op != equation.color_blend_op || m_color_blend_state.color_blend_equation.alpha_blend_op != equation.alpha_blend_op) { m_color_blend_state.color_blend_equation.color_blend_op = equation.color_blend_op; m_color_blend_state.color_blend_equation.alpha_blend_op = equation.alpha_blend_op; const auto gl_mode_rgb = blend_op_lut[std::to_underlying(equation.color_blend_op)]; const auto gl_mode_alpha = blend_op_lut[std::to_underlying(equation.alpha_blend_op)]; glBlendEquationSeparate(gl_mode_rgb, gl_mode_alpha); } } void pipeline::set_color_write_mask(std::uint8_t mask) { if (m_color_blend_state.color_write_mask != mask) { m_color_blend_state.color_write_mask = mask; glColorMask ( mask & color_component_r_bit, mask & color_component_g_bit, mask & color_component_b_bit, mask & color_component_a_bit ); } } void pipeline::set_blend_constants(const std::array& blend_constants) { if (m_color_blend_state.blend_constants != blend_constants) { m_color_blend_state.blend_constants = blend_constants; glBlendColor(blend_constants[0], blend_constants[1], blend_constants[2], blend_constants[3]); } } void pipeline::draw(std::uint32_t vertex_count, std::uint32_t instance_count, std::uint32_t first_vertex, std::uint32_t first_instance) { glDrawArraysInstancedBaseInstance ( primitive_topology_lut[std::to_underlying(m_input_assembly_state.topology)], static_cast(first_vertex), static_cast(vertex_count), static_cast(instance_count), static_cast(first_instance) ); } void pipeline::draw_indexed(std::uint32_t index_count, std::uint32_t instance_count, std::uint32_t first_index, std::int32_t vertex_offset, std::uint32_t first_instance) { glDrawElementsInstancedBaseInstance ( primitive_topology_lut[std::to_underlying(m_input_assembly_state.topology)], static_cast(instance_count), GL_UNSIGNED_INT,// GL_UNSIGNED_SHORT, GL_UNSIGNED_BYTE reinterpret_cast(first_index * sizeof(unsigned int)), static_cast(instance_count), static_cast(first_instance) ); } void pipeline::clear_attachments(std::uint8_t mask, const clear_value& value) { GLbitfield gl_clear_mask = 0; if (mask & color_clear_bit) { // Add color attachment to OpenGL clear mask gl_clear_mask |= GL_COLOR_BUFFER_BIT; if (m_clear_value.color != value.color) { // Update color clear value glClearColor(value.color[0], value.color[1], value.color[2], value.color[3]); m_clear_value.color = value.color; } } if (mask & depth_clear_bit) { // Add depth attachment to OpenGL clear mask gl_clear_mask |= GL_DEPTH_BUFFER_BIT; if (m_clear_value.depth != value.depth) { // Update depth clear value glClearDepth(value.depth); m_clear_value.depth = value.depth; } } if (mask & stencil_clear_bit) { // Add stencil attachment to OpenGL clear mask gl_clear_mask |= GL_STENCIL_BUFFER_BIT; if (m_clear_value.stencil != value.stencil) { // Update stencil clear value glClearStencil(static_cast(value.stencil)); m_clear_value.stencil = value.stencil; } } // Clear attachments glClear(gl_clear_mask); } void pipeline::defaut_framebuffer_resized(std::uint32_t width, std::uint32_t height) noexcept { m_default_framebuffer_dimensions = {width, height}; } void pipeline::fetch_vertex_input_state() { } void pipeline::fetch_input_assembly_state() { m_input_assembly_state.primitive_restart_enabled = glIsEnabled(GL_PRIMITIVE_RESTART); } void pipeline::fetch_viewport_state() { // Query viewport position and dimensions GLint gl_viewport[4]; glGetIntegerv(GL_VIEWPORT, gl_viewport); // Query viewport depth range GLfloat gl_depth_range[2]; glGetFloatv(GL_DEPTH_RANGE, gl_depth_range); // Query scissor box GLint gl_scissor_box[4] = {0, 0, 0, 0}; glGetIntegerv(GL_SCISSOR_BOX, gl_scissor_box); // Match viewport state m_viewport_state.viewports = {{ static_cast(gl_viewport[0]), static_cast(gl_viewport[1]), static_cast(gl_viewport[2]), static_cast(gl_viewport[3]), gl_depth_range[0], gl_depth_range[1] }}; m_viewport_state.scissors = {{ static_cast(gl_scissor_box[0]), static_cast(gl_scissor_box[1]), static_cast(std::max(0, gl_scissor_box[2])), static_cast(std::max(0, gl_scissor_box[3])) }}; } void pipeline::fetch_rasterization_state() { // Query rasterizer discard bool gl_rasterizer_discard_enabled = glIsEnabled(GL_RASTERIZER_DISCARD); // Query fill mode GLint gl_fill_mode; glGetIntegerv(GL_POLYGON_MODE, &gl_fill_mode); // Query cull mode bool gl_cull_enabled = glIsEnabled(GL_CULL_FACE); GLint gl_cull_mode; glGetIntegerv(GL_CULL_FACE_MODE, &gl_cull_mode); // Query front face GLint gl_front_face; glGetIntegerv(GL_FRONT_FACE, &gl_front_face); // Query depth bias bool gl_depth_bias_enabled = glIsEnabled(GL_POLYGON_OFFSET_FILL) && glIsEnabled(GL_POLYGON_OFFSET_LINE) && glIsEnabled(GL_POLYGON_OFFSET_POINT); float gl_depth_bias_constant_factor; float gl_depth_bias_slope_factor; glGetFloatv(GL_POLYGON_OFFSET_UNITS, &gl_depth_bias_constant_factor); glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &gl_depth_bias_slope_factor); // Query depth clamp bool gl_depth_clamp_enabled = glIsEnabled(GL_DEPTH_CLAMP); // Query scissor test bool gl_scissor_test_enabled = glIsEnabled(GL_SCISSOR_TEST); // Query provoking vertex GLint gl_provoking_vertex; glGetIntegerv(GL_PROVOKING_VERTEX, &gl_provoking_vertex); // Query point size float gl_point_size; glGetFloatv(GL_POINT_SIZE, &gl_point_size); // Query line width float gl_line_width; glGetFloatv(GL_LINE_WIDTH, &gl_line_width); // Match rasterizer state m_rasterization_state.rasterizer_discard_enabled = gl_rasterizer_discard_enabled; m_rasterization_state.fill_mode = (gl_fill_mode == GL_POINT ? fill_mode::point : gl_fill_mode == GL_LINE ? fill_mode::line : fill_mode::fill); if (gl_cull_enabled) { m_rasterization_state.cull_mode = (gl_cull_mode == GL_FRONT_AND_BACK ? cull_mode::front_and_back : gl_fill_mode == GL_FRONT ? cull_mode::front : cull_mode::back); } else { m_rasterization_state.cull_mode = cull_mode::none; } m_rasterization_state.front_face = (gl_front_face == GL_CW ? front_face::clockwise : front_face::counter_clockwise); m_rasterization_state.depth_bias_enabled = gl_depth_bias_enabled; m_rasterization_state.depth_bias_constant_factor = gl_depth_bias_constant_factor; m_rasterization_state.depth_bias_slope_factor = gl_depth_bias_slope_factor; m_rasterization_state.depth_clamp_enabled = gl_depth_clamp_enabled; m_rasterization_state.scissor_test_enabled = gl_scissor_test_enabled; m_rasterization_state.provoking_vertex_mode = (gl_provoking_vertex == GL_FIRST_VERTEX_CONVENTION ? provoking_vertex_mode::first : provoking_vertex_mode::last); m_rasterization_state.point_size = gl_point_size; m_rasterization_state.line_width = gl_line_width; } void pipeline::fetch_depth_stencil_state() { auto inv_compare_op_lut = [](GLint func) -> gl::compare_op { switch (func) { case GL_NEVER: return compare_op::never; case GL_LESS: return compare_op::less; case GL_EQUAL: return compare_op::equal; case GL_LEQUAL: return compare_op::less_or_equal; case GL_GREATER: return compare_op::greater; case GL_NOTEQUAL: return compare_op::not_equal; case GL_GEQUAL: return compare_op::greater_or_equal; case GL_ALWAYS: default: return compare_op::always; } }; auto inv_stencil_op_lut = [](GLint op) -> gl::stencil_op { switch (op) { case GL_KEEP: return stencil_op::keep; case GL_ZERO: return stencil_op::zero; case GL_REPLACE: return stencil_op::replace; case GL_INCR: return stencil_op::increment_and_clamp; case GL_DECR: return stencil_op::decrement_and_clamp; case GL_INVERT: return stencil_op::invert; case GL_INCR_WRAP: return stencil_op::increment_and_wrap; case GL_DECR_WRAP: default: return stencil_op::decrement_and_wrap; } }; m_depth_stencil_state.depth_test_enabled = glIsEnabled(GL_DEPTH_TEST); GLboolean gl_depth_write_enabled; glGetBooleanv(GL_DEPTH_WRITEMASK, &gl_depth_write_enabled); m_depth_stencil_state.depth_write_enabled = gl_depth_write_enabled; GLint gl_depth_compare_op; glGetIntegerv(GL_DEPTH_FUNC, &gl_depth_compare_op); m_depth_stencil_state.depth_compare_op = inv_compare_op_lut(gl_depth_compare_op); m_depth_stencil_state.stencil_test_enabled = glIsEnabled(GL_STENCIL_TEST); // Stencil front { GLint gl_stencil_front_fail; GLint gl_stencil_front_pass_depth_pass; GLint gl_stencil_front_pass_depth_fail; GLint gl_stencil_front_func; GLint gl_stencil_front_value_mask; GLint gl_stencil_front_write_mask; GLint gl_stencil_front_ref; glGetIntegerv(GL_STENCIL_FAIL, &gl_stencil_front_fail); glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &gl_stencil_front_pass_depth_pass); glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &gl_stencil_front_pass_depth_fail); glGetIntegerv(GL_STENCIL_FUNC, &gl_stencil_front_func); glGetIntegerv(GL_STENCIL_VALUE_MASK, &gl_stencil_front_value_mask); glGetIntegerv(GL_STENCIL_WRITEMASK, &gl_stencil_front_write_mask); glGetIntegerv(GL_STENCIL_REF, &gl_stencil_front_ref); m_depth_stencil_state.stencil_front.fail_op = inv_stencil_op_lut(gl_stencil_front_fail); m_depth_stencil_state.stencil_front.pass_op = inv_stencil_op_lut(gl_stencil_front_pass_depth_pass); m_depth_stencil_state.stencil_front.depth_fail_op = inv_stencil_op_lut(gl_stencil_front_pass_depth_fail); m_depth_stencil_state.stencil_front.compare_op = inv_compare_op_lut(gl_stencil_front_func); m_depth_stencil_state.stencil_front.compare_mask = std::bit_cast(gl_stencil_front_value_mask); m_depth_stencil_state.stencil_front.write_mask = std::bit_cast(gl_stencil_front_write_mask); m_depth_stencil_state.stencil_front.reference = std::bit_cast(gl_stencil_front_ref); } // Stencil back { GLint gl_stencil_back_fail; GLint gl_stencil_back_pass_depth_pass; GLint gl_stencil_back_pass_depth_fail; GLint gl_stencil_back_func; GLint gl_stencil_back_value_mask; GLint gl_stencil_back_write_mask; GLint gl_stencil_back_ref; glGetIntegerv(GL_STENCIL_BACK_FAIL, &gl_stencil_back_fail); glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &gl_stencil_back_pass_depth_pass); glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &gl_stencil_back_pass_depth_fail); glGetIntegerv(GL_STENCIL_BACK_FUNC, &gl_stencil_back_func); glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &gl_stencil_back_value_mask); glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &gl_stencil_back_write_mask); glGetIntegerv(GL_STENCIL_BACK_REF, &gl_stencil_back_ref); m_depth_stencil_state.stencil_back.fail_op = inv_stencil_op_lut(gl_stencil_back_fail); m_depth_stencil_state.stencil_back.pass_op = inv_stencil_op_lut(gl_stencil_back_pass_depth_pass); m_depth_stencil_state.stencil_back.depth_fail_op = inv_stencil_op_lut(gl_stencil_back_pass_depth_fail); m_depth_stencil_state.stencil_back.compare_op = inv_compare_op_lut(gl_stencil_back_func); m_depth_stencil_state.stencil_back.compare_mask = std::bit_cast(gl_stencil_back_value_mask); m_depth_stencil_state.stencil_back.write_mask = std::bit_cast(gl_stencil_back_write_mask); m_depth_stencil_state.stencil_back.reference = std::bit_cast(gl_stencil_back_ref); } } void pipeline::fetch_color_blend_state() { auto inv_logic_op_lut = [](GLint op) -> gl::logic_op { switch (op) { case GL_CLEAR: return gl::logic_op::bitwise_clear; case GL_AND: return gl::logic_op::bitwise_and; case GL_AND_REVERSE: return gl::logic_op::bitwise_and_reverse; case GL_COPY: return gl::logic_op::bitwise_copy; case GL_AND_INVERTED: return gl::logic_op::bitwise_and_inverted; case GL_NOOP: return gl::logic_op::bitwise_no_op; case GL_XOR: return gl::logic_op::bitwise_xor; case GL_OR: return gl::logic_op::bitwise_or; case GL_NOR: return gl::logic_op::bitwise_nor; case GL_EQUIV: return gl::logic_op::bitwise_equivalent; case GL_INVERT: return gl::logic_op::bitwise_invert; case GL_OR_REVERSE: return gl::logic_op::bitwise_or_reverse; case GL_COPY_INVERTED: return gl::logic_op::bitwise_copy_inverted; case GL_OR_INVERTED: return gl::logic_op::bitwise_or_inverted; case GL_NAND: return gl::logic_op::bitwise_nand; case GL_SET: default: return gl::logic_op::bitwise_set; } }; auto inv_blend_factor_lut = [](GLint func) -> gl::blend_factor { switch (func) { case GL_ZERO: return blend_factor::zero; case GL_ONE: return blend_factor::one; case GL_SRC_COLOR: return blend_factor::src_color; case GL_ONE_MINUS_SRC_COLOR: return blend_factor::one_minus_src_color; case GL_DST_COLOR: return blend_factor::dst_color; case GL_ONE_MINUS_DST_COLOR: return blend_factor::one_minus_dst_color; case GL_SRC_ALPHA: return blend_factor::src_alpha; case GL_ONE_MINUS_SRC_ALPHA: return blend_factor::one_minus_src_alpha; case GL_DST_ALPHA: return blend_factor::dst_alpha; case GL_ONE_MINUS_DST_ALPHA: return blend_factor::one_minus_dst_alpha; case GL_CONSTANT_COLOR: return blend_factor::constant_color; case GL_ONE_MINUS_CONSTANT_COLOR: return blend_factor::one_minus_constant_color; case GL_CONSTANT_ALPHA: return blend_factor::constant_alpha; case GL_ONE_MINUS_CONSTANT_ALPHA: return blend_factor::one_minus_constant_alpha; case GL_SRC_ALPHA_SATURATE: return blend_factor::src_alpha_saturate; case GL_SRC1_COLOR: return blend_factor::src1_color; case GL_ONE_MINUS_SRC1_COLOR: return blend_factor::one_minus_src1_color; case GL_SRC1_ALPHA: return blend_factor::src1_alpha; case GL_ONE_MINUS_SRC1_ALPHA: default: return blend_factor::one_minus_src1_alpha; } }; auto inv_blend_op_lut = [](GLint mode) -> gl::blend_op { switch (mode) { case GL_FUNC_ADD: return blend_op::add; case GL_FUNC_SUBTRACT: return blend_op::subtract; case GL_FUNC_REVERSE_SUBTRACT: return blend_op::reverse_subtract; case GL_MIN: return blend_op::min; case GL_MAX: default: return blend_op::max; } }; m_color_blend_state.logic_op_enabled = glIsEnabled(GL_COLOR_LOGIC_OP); GLint gl_logic_op; glGetIntegerv(GL_LOGIC_OP_MODE, &gl_logic_op); m_color_blend_state.logic_op = inv_logic_op_lut(gl_logic_op); m_color_blend_state.blend_enabled = glIsEnabled(GL_BLEND); GLint gl_blend_src_rgb; GLint gl_blend_dst_rgb; GLint gl_blend_equation_rgb; GLint gl_blend_src_alpha; GLint gl_blend_dst_alpha; GLint gl_blend_equation_alpha; glGetIntegerv(GL_BLEND_SRC_RGB, &gl_blend_src_rgb); glGetIntegerv(GL_BLEND_DST_RGB, &gl_blend_dst_rgb); glGetIntegerv(GL_BLEND_EQUATION_RGB, &gl_blend_equation_rgb); glGetIntegerv(GL_BLEND_SRC_ALPHA, &gl_blend_src_alpha); glGetIntegerv(GL_BLEND_DST_ALPHA, &gl_blend_dst_alpha); glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &gl_blend_equation_alpha); m_color_blend_state.color_blend_equation.src_color_blend_factor = inv_blend_factor_lut(gl_blend_src_rgb); m_color_blend_state.color_blend_equation.dst_color_blend_factor = inv_blend_factor_lut(gl_blend_dst_rgb); m_color_blend_state.color_blend_equation.color_blend_op = inv_blend_op_lut(gl_blend_equation_rgb); m_color_blend_state.color_blend_equation.src_alpha_blend_factor = inv_blend_factor_lut(gl_blend_src_alpha); m_color_blend_state.color_blend_equation.dst_alpha_blend_factor = inv_blend_factor_lut(gl_blend_dst_alpha); m_color_blend_state.color_blend_equation.alpha_blend_op = inv_blend_op_lut(gl_blend_equation_alpha); GLboolean gl_color_writemask[4]; glGetBooleanv(GL_COLOR_WRITEMASK, gl_color_writemask); m_color_blend_state.color_write_mask = static_cast(gl_color_writemask[0]) | (static_cast(gl_color_writemask[1]) << 1) | (static_cast(gl_color_writemask[2]) << 2) | (static_cast(gl_color_writemask[3]) << 3); glGetFloatv(GL_BLEND_COLOR, m_color_blend_state.blend_constants.data()); } void pipeline::fetch_clear_value() { // Query clear values GLfloat gl_color_clear[4]; GLfloat gl_depth_clear; GLint gl_stencil_clear; glGetFloatv(GL_COLOR_CLEAR_VALUE, gl_color_clear); glGetFloatv(GL_DEPTH_CLEAR_VALUE, &gl_depth_clear); glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &gl_stencil_clear); // Match clear state m_clear_value.color = {gl_color_clear[0], gl_color_clear[1], gl_color_clear[2], gl_color_clear[3]}; m_clear_value.depth = gl_depth_clear; m_clear_value.stencil = static_cast(gl_stencil_clear); } } // namespace gl