/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <engine/gl/pipeline.hpp>
|
|
#include <engine/gl/clear-bits.hpp>
|
|
#include <engine/gl/stencil-face-bits.hpp>
|
|
#include <engine/gl/color-component-bits.hpp>
|
|
#include <engine/debug/log.hpp>
|
|
#include <glad/gl.h>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <bit>
|
|
|
|
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<std::uint32_t>(gl_scissor_box[2]);
|
|
m_default_framebuffer_dimensions[1] = static_cast<std::uint32_t>(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<const vertex_input_binding> vertex_bindings, std::span<const vertex_input_attribute> 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<const vertex_buffer* const> buffers, std::span<const std::size_t> offsets, std::span<const std::size_t> 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<GLuint>(first_binding + i),
|
|
buffers[i]->m_gl_named_buffer,
|
|
static_cast<GLintptr>(offsets[i]),
|
|
static_cast<GLsizei>(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<const gl::viewport> 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<GLint>(viewport.x),
|
|
static_cast<GLint>(viewport.y),
|
|
std::max(0, static_cast<GLsizei>(viewport.width)),
|
|
std::max(0, static_cast<GLsizei>(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<const gl::scissor_region> 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<GLint>(scissor.x),
|
|
static_cast<GLint>(scissor.y),
|
|
std::max(0, static_cast<GLsizei>(scissor.width)),
|
|
std::max(0, static_cast<GLsizei>(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<GLint>(m_depth_stencil_state.stencil_front.reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_front.compare_mask));
|
|
}
|
|
else
|
|
{
|
|
glStencilFuncSeparate(GL_FRONT, gl_compare_op, std::bit_cast<GLint>(m_depth_stencil_state.stencil_front.reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_front.compare_mask));
|
|
glStencilFuncSeparate(GL_BACK, gl_compare_op, std::bit_cast<GLint>(m_depth_stencil_state.stencil_back.reference), std::bit_cast<GLuint>(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<GLint>(m_depth_stencil_state.stencil_front.reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_front.compare_mask));
|
|
}
|
|
else
|
|
{
|
|
glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast<GLint>(m_depth_stencil_state.stencil_back.reference), std::bit_cast<GLuint>(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<GLint>(m_depth_stencil_state.stencil_front.reference), static_cast<GLuint>(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<GLint>(m_depth_stencil_state.stencil_front.reference), std::bit_cast<GLuint>(compare_mask));
|
|
glStencilFuncSeparate(GL_BACK, gl_compare_op_back, std::bit_cast<GLint>(m_depth_stencil_state.stencil_back.reference), std::bit_cast<GLuint>(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<GLint>(m_depth_stencil_state.stencil_front.reference), std::bit_cast<GLuint>(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<GLint>(m_depth_stencil_state.stencil_back.reference), std::bit_cast<GLuint>(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<GLint>(reference), std::bit_cast<GLuint>(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<GLint>(reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_front.compare_mask));
|
|
glStencilFuncSeparate(GL_BACK, gl_compare_op_back, std::bit_cast<GLint>(reference), std::bit_cast<GLuint>(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<GLint>(reference), std::bit_cast<GLuint>(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<GLint>(reference), std::bit_cast<GLuint>(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<GLuint>(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<float, 4>& 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<GLint>(first_vertex),
|
|
static_cast<GLsizei>(vertex_count),
|
|
static_cast<GLsizei>(instance_count),
|
|
static_cast<GLuint>(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<GLsizei>(instance_count),
|
|
GL_UNSIGNED_INT,// GL_UNSIGNED_SHORT, GL_UNSIGNED_BYTE
|
|
reinterpret_cast<const GLvoid*>(first_index * sizeof(unsigned int)),
|
|
static_cast<GLsizei>(instance_count),
|
|
static_cast<GLuint>(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<GLint>(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<float>(gl_viewport[0]),
|
|
static_cast<float>(gl_viewport[1]),
|
|
static_cast<float>(gl_viewport[2]),
|
|
static_cast<float>(gl_viewport[3]),
|
|
gl_depth_range[0],
|
|
gl_depth_range[1]
|
|
}};
|
|
m_viewport_state.scissors =
|
|
{{
|
|
static_cast<std::int32_t>(gl_scissor_box[0]),
|
|
static_cast<std::int32_t>(gl_scissor_box[1]),
|
|
static_cast<std::uint32_t>(std::max(0, gl_scissor_box[2])),
|
|
static_cast<std::uint32_t>(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<std::uint32_t>(gl_stencil_front_value_mask);
|
|
m_depth_stencil_state.stencil_front.write_mask = std::bit_cast<std::uint32_t>(gl_stencil_front_write_mask);
|
|
m_depth_stencil_state.stencil_front.reference = std::bit_cast<std::uint32_t>(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<std::uint32_t>(gl_stencil_back_value_mask);
|
|
m_depth_stencil_state.stencil_back.write_mask = std::bit_cast<std::uint32_t>(gl_stencil_back_write_mask);
|
|
m_depth_stencil_state.stencil_back.reference = std::bit_cast<std::uint32_t>(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<std::uint8_t>(gl_color_writemask[0]) |
|
|
(static_cast<std::uint8_t>(gl_color_writemask[1]) << 1) |
|
|
(static_cast<std::uint8_t>(gl_color_writemask[2]) << 2) |
|
|
(static_cast<std::uint8_t>(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<std::uint32_t>(gl_stencil_clear);
|
|
}
|
|
|
|
} // namespace gl
|