Antkeeper  0.0.1
pipeline.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <engine/gl/pipeline.hpp>
21 #include <engine/gl/clear-bits.hpp>
24 #include <engine/debug/log.hpp>
25 #include <glad/gl.h>
26 #include <algorithm>
27 #include <stdexcept>
28 #include <bit>
29 
30 namespace {
31 
32  static constexpr GLenum stencil_face_lut[] =
33  {
34  GL_NONE, // 0
35  GL_FRONT, // stencil_face_front_bit
36  GL_BACK, // stencil_face_back_bit
37  GL_FRONT_AND_BACK // stencil_face_front_and_back
38  };
39 
40  static constexpr GLenum stencil_op_lut[] =
41  {
42  GL_KEEP, // stencil_op::keep
43  GL_ZERO, // stencil_op::zero
44  GL_REPLACE, // stencil_op::replace
45  GL_INCR, // stencil_op::increment_and_clamp
46  GL_DECR, // stencil_op::decrement_and_clamp
47  GL_INVERT, // stencil_op::invert
48  GL_INCR_WRAP, // stencil_op::increment_and_wrap
49  GL_DECR_WRAP // stencil_op::decrement_and_wrap
50  };
51 
52  static constexpr GLenum compare_op_lut[] =
53  {
54  GL_NEVER, // compare_op::never
55  GL_LESS, // compare_op::less
56  GL_EQUAL, // compare_op::equal
57  GL_LEQUAL, // compare_op::less_or_equal
58  GL_GREATER, // compare_op::greater
59  GL_NOTEQUAL, // compare_op::not_equal
60  GL_GEQUAL, // compare_op::greater_or_equal
61  GL_ALWAYS // compare_op::always
62  };
63 
64  static constexpr GLenum provoking_vertex_mode_lut[] =
65  {
66  GL_FIRST_VERTEX_CONVENTION, // provoking_vertex_mode::first
67  GL_LAST_VERTEX_CONVENTION // provoking_vertex_mode::last
68  };
69 
70  static constexpr GLenum primitive_topology_lut[] =
71  {
72  GL_POINTS, // primitive_topology::point_list
73  GL_LINES, // primitive_topology::line_list
74  GL_LINE_STRIP, // primitive_topology::line_strip,
75  GL_TRIANGLES, // primitive_topology::triangle_list
76  GL_TRIANGLE_STRIP, // primitive_topology::triangle_strip
77  GL_TRIANGLE_FAN, // primitive_topology::triangle_fan
78  GL_LINES_ADJACENCY, // primitive_topology::line_list_with_adjacency
79  GL_LINE_STRIP_ADJACENCY, // primitive_topology::line_strip_with_adjacency
80  GL_TRIANGLES_ADJACENCY, // primitive_topology::triangle_list_with_adjacency
81  GL_TRIANGLE_STRIP_ADJACENCY, // primitive_topology::triangle_strip_with_adjacency
82  GL_PATCHES // primitive_topology::patch_list
83  };
84 
85  static constexpr GLenum logic_op_lut[] =
86  {
87  GL_CLEAR , // logic_op::bitwise_clear
88  GL_AND , // logic_op::bitwise_and
89  GL_AND_REVERSE , // logic_op::bitwise_and_reverse
90  GL_COPY , // logic_op::bitwise_copy
91  GL_AND_INVERTED , // logic_op::bitwise_and_inverted
92  GL_NOOP , // logic_op::bitwise_no_op
93  GL_XOR , // logic_op::bitwise_xor
94  GL_OR , // logic_op::bitwise_or
95  GL_NOR , // logic_op::bitwise_nor
96  GL_EQUIV , // logic_op::bitwise_equivalent
97  GL_INVERT , // logic_op::bitwise_invert
98  GL_OR_REVERSE , // logic_op::bitwise_or_reverse
99  GL_COPY_INVERTED, // logic_op::bitwise_copy_inverted
100  GL_OR_INVERTED , // logic_op::bitwise_or_inverted
101  GL_NAND , // logic_op::bitwise_nand
102  GL_SET // logic_op::bitwise_set
103  };
104 
105  static constexpr GLenum blend_factor_lut[] =
106  {
107  GL_ZERO , // blend_factor::zero
108  GL_ONE , // blend_factor::one
109  GL_SRC_COLOR , // blend_factor::src_color
110  GL_ONE_MINUS_SRC_COLOR , // blend_factor::one_minus_src_color
111  GL_DST_COLOR , // blend_factor::dst_color
112  GL_ONE_MINUS_DST_COLOR , // blend_factor::one_minus_dst_color
113  GL_SRC_ALPHA , // blend_factor::src_alpha
114  GL_ONE_MINUS_SRC_ALPHA , // blend_factor::one_minus_src_alpha
115  GL_DST_ALPHA , // blend_factor::dst_alpha
116  GL_ONE_MINUS_DST_ALPHA , // blend_factor::one_minus_dst_alpha
117  GL_CONSTANT_COLOR , // blend_factor::constant_color
118  GL_ONE_MINUS_CONSTANT_COLOR, // blend_factor::one_minus_constant_color
119  GL_CONSTANT_ALPHA , // blend_factor::constant_alpha
120  GL_ONE_MINUS_CONSTANT_ALPHA, // blend_factor::one_minus_constant_alpha
121  GL_SRC_ALPHA_SATURATE , // blend_factor::src_alpha_saturate
122  GL_SRC1_COLOR , // blend_factor::src1_color
123  GL_ONE_MINUS_SRC1_COLOR , // blend_factor::one_minus_src1_color
124  GL_SRC1_ALPHA , // blend_factor::src1_alpha
125  GL_ONE_MINUS_SRC1_ALPHA // blend_factor::one_minus_src1_alpha
126  };
127 
128  static constexpr GLenum blend_op_lut[] =
129  {
130  GL_FUNC_ADD , // blend_op::add
131  GL_FUNC_SUBTRACT , // blend_op::subtract
132  GL_FUNC_REVERSE_SUBTRACT, // blend_op::reverse_subtract
133  GL_MIN , // blend_op::min
134  GL_MAX // blend_op::max
135  };
136 
137  void gl_debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* user_param)
138  {
139  const auto src_str = [source]() -> const char*
140  {
141  switch (source)
142  {
143  case GL_DEBUG_SOURCE_API:
144  return "API";
145  case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
146  return "window system";
147  case GL_DEBUG_SOURCE_SHADER_COMPILER:
148  return "shader compiler";
149  case GL_DEBUG_SOURCE_THIRD_PARTY:
150  return "third party";
151  case GL_DEBUG_SOURCE_APPLICATION:
152  return "application";
153  case GL_DEBUG_SOURCE_OTHER:
154  default:
155  return "other";
156  }
157  }();
158 
159  const auto type_str = [type]() -> const char*
160  {
161  switch (type)
162  {
163  case GL_DEBUG_TYPE_ERROR:
164  return "error";
165  case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
166  return "deprecated behavior";
167  case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
168  return "undefined behavior";
169  case GL_DEBUG_TYPE_PORTABILITY:
170  return "portability";
171  case GL_DEBUG_TYPE_PERFORMANCE:
172  return "performance";
173  case GL_DEBUG_TYPE_MARKER:
174  return "marker";
175  case GL_DEBUG_TYPE_OTHER:
176  default:
177  return "message";
178  }
179  }();
180 
181  const auto severity_str = [severity]() -> const char*
182  {
183  switch (severity)
184  {
185  case GL_DEBUG_SEVERITY_LOW:
186  return "low severity";
187  case GL_DEBUG_SEVERITY_MEDIUM:
188  return "medium severity";
189  case GL_DEBUG_SEVERITY_HIGH:
190  return "high severity";
191  case GL_DEBUG_SEVERITY_NOTIFICATION:
192  default:
193  return "notification";
194  }
195  }();
196 
197  switch (type)
198  {
199  case GL_DEBUG_TYPE_ERROR:
200  {
201  std::string formatted_message;
202  std::format_to(std::back_inserter(formatted_message), "OpenGL {} {} ({}) {}: {}", src_str, type_str, severity_str, id, message);
203  debug::log_fatal("{}", formatted_message);
204  throw std::runtime_error(formatted_message);
205  break;
206  }
207 
208  case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
209  case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
210  debug::log_error("OpenGL {} {} ({}) {}: {}", src_str, type_str, severity_str, id, message);
211  break;
212 
213  case GL_DEBUG_TYPE_PORTABILITY:
214  case GL_DEBUG_TYPE_PERFORMANCE:
215  debug::log_warning("OpenGL {} {} ({}) {}: {}", src_str, type_str, severity_str, id, message);
216  break;
217 
218  case GL_DEBUG_TYPE_MARKER:
219  case GL_DEBUG_TYPE_OTHER:
220  default:
221  debug::log_debug("OpenGL {} {} ({}) {}: {}", src_str, type_str, severity_str, id, message);
222  break;
223  }
224  }
225 }
226 
227 namespace gl {
228 
230 {
231  #if defined(DEBUG)
232  glEnable(GL_DEBUG_OUTPUT);
233  glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
234  glDebugMessageCallback(gl_debug_message_callback, nullptr);
235  #endif
236 
237  // Fetch limitations
238  glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_sampler_anisotropy);
239 
240  // Fetch dimensions of default framebuffer
241  GLint gl_scissor_box[4] = {0, 0, 0, 0};
242  glGetIntegerv(GL_SCISSOR_BOX, gl_scissor_box);
243  m_default_framebuffer_dimensions[0] = static_cast<std::uint32_t>(gl_scissor_box[2]);
244  m_default_framebuffer_dimensions[1] = static_cast<std::uint32_t>(gl_scissor_box[3]);
245 
246  // Enable seamless cubemap filtering
247  glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
248 
249  // Set clip control to lower left, 0 to 1
250  glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
251 
252  // Disable multisampling
253  glDisable(GL_MULTISAMPLE);
254 
255  // Set byte-alignment for packing and unpacking pixel rows
256  glPixelStorei(GL_PACK_ALIGNMENT, 1);
257  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
258 
259  // Fetch pipeline state
260  fetch_vertex_input_state();
261  fetch_input_assembly_state();
262  fetch_viewport_state();
263  fetch_rasterization_state();
264  fetch_depth_stencil_state();
265  fetch_color_blend_state();
266  fetch_clear_value();
267 }
268 
269 // void pipeline::set_vertex_input(std::span<const vertex_input_binding> vertex_bindings, std::span<const vertex_input_attribute> vertex_attributes)
270 // {
271  // m_vertex_input_state.vertex_bindings.assign(vertex_bindings.begin(), vertex_bindings.end());
272  // m_vertex_input_state.vertex_attributes.assign(vertex_attributes.begin(), vertex_attributes.end());
273 // }
274 
276 {
277  if (m_framebuffer != framebuffer)
278  {
279  m_framebuffer = framebuffer;
280 
281  if (m_framebuffer)
282  {
283  glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer->m_gl_named_framebuffer);
284  }
285  else
286  {
287  glBindFramebuffer(GL_FRAMEBUFFER, 0);
288  }
289  }
290 }
291 
293 {
294  if (m_shader_program != shader_program)
295  {
296  m_shader_program = shader_program;
297 
298  if (m_shader_program)
299  {
300  glUseProgram(m_shader_program->gl_program_id);
301  }
302  else
303  {
304  glUseProgram(0);
305  }
306  }
307 }
308 
310 {
311  if (m_vertex_array != array)
312  {
313  m_vertex_array = array;
314 
315  if (m_vertex_array)
316  {
317  glBindVertexArray(m_vertex_array->m_gl_named_array);
318  }
319  else
320  {
321  glBindVertexArray(0);
322  }
323  }
324 }
325 
326 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)
327 {
328  if (!m_vertex_array)
329  {
330  throw std::runtime_error("Failed to bind vertex buffer: no vertex array bound.");
331  }
332 
333  if (offsets.size() < buffers.size())
334  {
335  throw std::out_of_range("Vertex binding offset out of range.");
336  }
337 
338  if (strides.size() < buffers.size())
339  {
340  throw std::out_of_range("Vertex binding stride out of range.");
341  }
342 
343  for (std::size_t i = 0; i < buffers.size(); ++i)
344  {
345  glVertexArrayVertexBuffer
346  (
347  m_vertex_array->m_gl_named_array,
348  static_cast<GLuint>(first_binding + i),
349  buffers[i]->m_gl_named_buffer,
350  static_cast<GLintptr>(offsets[i]),
351  static_cast<GLsizei>(strides[i])
352  );
353  }
354 }
355 
357 {
358  if (m_input_assembly_state.topology != topology)
359  {
360  m_input_assembly_state.topology = topology;
361  }
362 }
363 
365 {
366  if (m_input_assembly_state.primitive_restart_enabled != enabled)
367  {
368  if (enabled)
369  {
370  glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
371  }
372  else
373  {
374  glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
375  }
376 
377  m_input_assembly_state.primitive_restart_enabled = enabled;
378  }
379 }
380 
381 void pipeline::set_viewport(std::uint32_t first_viewport, std::span<const gl::viewport> viewports)
382 {
383  // Bounds check
384  if (first_viewport + viewports.size() > m_max_viewports)
385  {
386  throw std::out_of_range("Viewport index out of range.");
387  }
388 
389  // Ignore empty commands
390  if (!viewports.size())
391  {
392  return;
393  }
394 
395  const auto& active_viewport = m_viewport_state.viewports.front();
396  const auto& viewport = viewports.front();
397 
398  // Update viewport position and dimensions
399  if (active_viewport.width != viewport.width ||
400  active_viewport.height != viewport.height ||
401  active_viewport.x != viewport.x ||
402  active_viewport.y != viewport.y)
403  {
404  glViewport
405  (
406  static_cast<GLint>(viewport.x),
407  static_cast<GLint>(viewport.y),
408  std::max(0, static_cast<GLsizei>(viewport.width)),
409  std::max(0, static_cast<GLsizei>(viewport.height))
410  );
411  }
412 
413  // Update viewport depth range
414  if (active_viewport.min_depth != viewport.min_depth ||
415  active_viewport.max_depth != viewport.max_depth)
416  {
417  glDepthRange(viewport.min_depth, viewport.max_depth);
418  }
419 
420  // Update viewport state
421  std::copy(viewports.begin(), viewports.end(), m_viewport_state.viewports.begin() + first_viewport);
422 }
423 
424 void pipeline::set_scissor(std::uint32_t first_scissor, std::span<const gl::scissor_region> scissors)
425 {
426  // Bounds check
427  if (first_scissor + scissors.size() > m_max_viewports)
428  {
429  throw std::out_of_range("Scissor region index out of range.");
430  }
431 
432  // Ignore empty commands
433  if (scissors.empty())
434  {
435  return;
436  }
437 
438  const auto& active_scissor = m_viewport_state.scissors.front();
439  const auto& scissor = scissors.front();
440 
441  // Update scissor region
442  if (active_scissor.width != scissor.width ||
443  active_scissor.height != scissor.height ||
444  active_scissor.x != scissor.x ||
445  active_scissor.y != scissor.y)
446  {
447  glScissor
448  (
449  static_cast<GLint>(scissor.x),
450  static_cast<GLint>(scissor.y),
451  std::max(0, static_cast<GLsizei>(scissor.width)),
452  std::max(0, static_cast<GLsizei>(scissor.height))
453  );
454  }
455 
456  // Update viewport state
457  std::copy(scissors.begin(), scissors.end(), m_viewport_state.scissors.begin() + first_scissor);
458 }
459 
461 {
462  if (m_rasterization_state.rasterizer_discard_enabled != enabled)
463  {
464  if (enabled)
465  {
466  glEnable(GL_RASTERIZER_DISCARD);
467  }
468  else
469  {
470  glDisable(GL_RASTERIZER_DISCARD);
471  }
472 
473  m_rasterization_state.rasterizer_discard_enabled = enabled;
474  }
475 }
476 
478 {
479  if (m_rasterization_state.fill_mode != mode)
480  {
481  switch (mode)
482  {
483  case fill_mode::fill:
484  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
485  break;
486 
487  case fill_mode::line:
488  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
489  break;
490 
491  case fill_mode::point:
492  glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
493  break;
494 
495  default:
496  break;
497  }
498 
499  m_rasterization_state.fill_mode = mode;
500  }
501 }
502 
504 {
505  if (m_rasterization_state.cull_mode != mode)
506  {
507  if (mode == cull_mode::none)
508  {
509  glDisable(GL_CULL_FACE);
510  }
511  else
512  {
513  if (m_rasterization_state.cull_mode == cull_mode::none)
514  {
515  glEnable(GL_CULL_FACE);
516  }
517 
518  switch (mode)
519  {
520  case cull_mode::back:
521  glCullFace(GL_BACK);
522  break;
523 
524  case cull_mode::front:
525  glCullFace(GL_FRONT);
526  break;
527 
529  glCullFace(GL_FRONT_AND_BACK);
530  break;
531 
532  default:
533  break;
534  }
535  }
536 
537  m_rasterization_state.cull_mode = mode;
538  }
539 }
540 
542 {
543  if (m_rasterization_state.front_face != face)
544  {
545  glFrontFace(face == front_face::counter_clockwise ? GL_CCW : GL_CW);
546 
547  m_rasterization_state.front_face = face;
548  }
549 }
550 
552 {
553  if (m_rasterization_state.depth_bias_enabled != enabled)
554  {
555  if (enabled)
556  {
557  glEnable(GL_POLYGON_OFFSET_FILL);
558  glEnable(GL_POLYGON_OFFSET_LINE);
559  glEnable(GL_POLYGON_OFFSET_POINT);
560  }
561  else
562  {
563  glDisable(GL_POLYGON_OFFSET_FILL);
564  glDisable(GL_POLYGON_OFFSET_LINE);
565  glDisable(GL_POLYGON_OFFSET_POINT);
566  }
567 
568  m_rasterization_state.depth_bias_enabled = enabled;
569  }
570 }
571 
572 void pipeline::set_depth_bias_factors(float constant_factor, float slope_factor)
573 {
574  // Update depth bias factors
575  if (m_rasterization_state.depth_bias_constant_factor != constant_factor ||
576  m_rasterization_state.depth_bias_slope_factor != slope_factor)
577  {
578  glPolygonOffset(slope_factor, constant_factor);
579 
580  m_rasterization_state.depth_bias_constant_factor = constant_factor;
581  m_rasterization_state.depth_bias_slope_factor = slope_factor;
582  }
583 }
584 
586 {
587  if (m_rasterization_state.depth_clamp_enabled != enabled)
588  {
589  if (enabled)
590  {
591  glEnable(GL_DEPTH_CLAMP);
592  }
593  else
594  {
595  glDisable(GL_DEPTH_CLAMP);
596  }
597 
598  m_rasterization_state.depth_clamp_enabled = enabled;
599  }
600 }
601 
603 {
604  if (m_rasterization_state.scissor_test_enabled != enabled)
605  {
606  if (enabled)
607  {
608  glEnable(GL_SCISSOR_TEST);
609  }
610  else
611  {
612  glDisable(GL_SCISSOR_TEST);
613  }
614 
615  m_rasterization_state.scissor_test_enabled = enabled;
616  }
617 }
618 
620 {
621  if (m_rasterization_state.provoking_vertex_mode != mode)
622  {
623  const auto gl_provoking_vertex_mode = provoking_vertex_mode_lut[std::to_underlying(mode)];
624  glProvokingVertex(gl_provoking_vertex_mode);
625  m_rasterization_state.provoking_vertex_mode = mode;
626  }
627 }
628 
629 void pipeline::set_point_size(float size)
630 {
631  if (m_rasterization_state.point_size != size)
632  {
633  glPointSize(size);
634 
635  m_rasterization_state.point_size = size;
636  }
637 }
638 
639 void pipeline::set_line_width(float width)
640 {
641  if (m_rasterization_state.line_width != width)
642  {
643  glLineWidth(width);
644 
645  m_rasterization_state.line_width = width;
646  }
647 }
648 
650 {
651  if (m_depth_stencil_state.depth_test_enabled != enabled)
652  {
653  m_depth_stencil_state.depth_test_enabled = enabled;
654 
655  if (enabled)
656  {
657  glEnable(GL_DEPTH_TEST);
658  }
659  else
660  {
661  glDisable(GL_DEPTH_TEST);
662  }
663  }
664 }
665 
667 {
668  if (m_depth_stencil_state.depth_write_enabled != enabled)
669  {
670  m_depth_stencil_state.depth_write_enabled = enabled;
671  glDepthMask(enabled);
672  }
673 }
674 
676 {
677  if (m_depth_stencil_state.depth_compare_op != compare_op)
678  {
679  m_depth_stencil_state.depth_compare_op = compare_op;
680  const auto gl_compare_op = compare_op_lut[std::to_underlying(compare_op)];
681  glDepthFunc(gl_compare_op);
682  }
683 }
684 
686 {
687  if (m_depth_stencil_state.stencil_test_enabled != enabled)
688  {
689  m_depth_stencil_state.stencil_test_enabled = enabled;
690 
691  if (enabled)
692  {
693  glEnable(GL_STENCIL_TEST);
694  }
695  else
696  {
697  glDisable(GL_STENCIL_TEST);
698  }
699  }
700 }
701 
702 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)
703 {
704  bool stencil_op_updated = false;
705  bool compare_op_updated = false;
706 
707  if (face_mask & stencil_face_front_bit)
708  {
709  if (m_depth_stencil_state.stencil_front.fail_op != fail_op ||
710  m_depth_stencil_state.stencil_front.pass_op != pass_op ||
711  m_depth_stencil_state.stencil_front.depth_fail_op != depth_fail_op)
712  {
713  m_depth_stencil_state.stencil_front.fail_op = fail_op;
714  m_depth_stencil_state.stencil_front.pass_op = pass_op;
715  m_depth_stencil_state.stencil_front.depth_fail_op = depth_fail_op;
716  stencil_op_updated = true;
717  }
718 
719  if (m_depth_stencil_state.stencil_front.compare_op != compare_op)
720  {
721  m_depth_stencil_state.stencil_front.compare_op = compare_op;
722  compare_op_updated = true;
723  }
724  }
725 
726  if (face_mask & stencil_face_back_bit)
727  {
728  if (m_depth_stencil_state.stencil_back.fail_op != fail_op ||
729  m_depth_stencil_state.stencil_back.pass_op != pass_op ||
730  m_depth_stencil_state.stencil_back.depth_fail_op != depth_fail_op)
731  {
732  m_depth_stencil_state.stencil_back.fail_op = fail_op;
733  m_depth_stencil_state.stencil_back.pass_op = pass_op;
734  m_depth_stencil_state.stencil_back.depth_fail_op = depth_fail_op;
735  stencil_op_updated = true;
736  }
737 
738  if (m_depth_stencil_state.stencil_back.compare_op != compare_op)
739  {
740  m_depth_stencil_state.stencil_back.compare_op = compare_op;
741  compare_op_updated = true;
742  }
743  }
744 
745  if (stencil_op_updated || compare_op_updated)
746  {
747  const auto gl_face = stencil_face_lut[face_mask];
748 
749  if (stencil_op_updated)
750  {
751  const auto gl_fail_op = stencil_op_lut[std::to_underlying(fail_op)];
752  const auto gl_pass_op = stencil_op_lut[std::to_underlying(pass_op)];
753  const auto gl_depth_fail_op = stencil_op_lut[std::to_underlying(depth_fail_op)];
754  glStencilOpSeparate(gl_face, gl_fail_op, gl_depth_fail_op, gl_pass_op);
755  }
756 
757  if (compare_op_updated)
758  {
759  const auto gl_compare_op = compare_op_lut[std::to_underlying(compare_op)];
760 
761  if (face_mask == stencil_face_front_and_back)
762  {
763  if (m_depth_stencil_state.stencil_front.reference == m_depth_stencil_state.stencil_back.reference &&
764  m_depth_stencil_state.stencil_front.compare_mask == m_depth_stencil_state.stencil_back.compare_mask)
765  {
766  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));
767  }
768  else
769  {
770  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));
771  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));
772  }
773  }
774  else if (face_mask == stencil_face_front_bit)
775  {
776  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));
777  }
778  else
779  {
780  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));
781  }
782  }
783  }
784 }
785 
786 void pipeline::set_stencil_compare_mask(std::uint8_t face_mask, std::uint32_t compare_mask)
787 {
788  bool compare_mask_updated = false;
789 
790  if (face_mask & stencil_face_front_bit)
791  {
792  if (m_depth_stencil_state.stencil_front.compare_mask != compare_mask)
793  {
794  m_depth_stencil_state.stencil_front.compare_mask = compare_mask;
795  compare_mask_updated = true;
796  }
797  }
798 
799  if (face_mask & stencil_face_back_bit)
800  {
801  if (m_depth_stencil_state.stencil_back.compare_mask != compare_mask)
802  {
803  m_depth_stencil_state.stencil_back.compare_mask = compare_mask;
804  compare_mask_updated = true;
805  }
806  }
807 
808  if (compare_mask_updated)
809  {
810  const auto gl_face = stencil_face_lut[face_mask];
811 
812  if (face_mask == stencil_face_front_and_back)
813  {
814  if (m_depth_stencil_state.stencil_front.reference == m_depth_stencil_state.stencil_back.reference &&
815  m_depth_stencil_state.stencil_front.compare_op == m_depth_stencil_state.stencil_back.compare_op)
816  {
817  const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)];
818  glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast<GLint>(m_depth_stencil_state.stencil_front.reference), static_cast<GLuint>(compare_mask));
819  }
820  else
821  {
822  const auto gl_compare_op_front = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)];
823  const auto gl_compare_op_back = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_back.compare_op)];
824  glStencilFuncSeparate(GL_FRONT, gl_compare_op_front, std::bit_cast<GLint>(m_depth_stencil_state.stencil_front.reference), std::bit_cast<GLuint>(compare_mask));
825  glStencilFuncSeparate(GL_BACK, gl_compare_op_back, std::bit_cast<GLint>(m_depth_stencil_state.stencil_back.reference), std::bit_cast<GLuint>(compare_mask));
826  }
827  }
828  else if (face_mask == stencil_face_front_bit)
829  {
830  const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)];
831  glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast<GLint>(m_depth_stencil_state.stencil_front.reference), std::bit_cast<GLuint>(compare_mask));
832  }
833  else
834  {
835  const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_back.compare_op)];
836  glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast<GLint>(m_depth_stencil_state.stencil_back.reference), std::bit_cast<GLuint>(compare_mask));
837  }
838  }
839 }
840 
841 void pipeline::set_stencil_reference(std::uint8_t face_mask, std::uint32_t reference)
842 {
843  bool reference_updated = false;
844 
845  if (face_mask & stencil_face_front_bit)
846  {
847  if (m_depth_stencil_state.stencil_front.reference != reference)
848  {
849  m_depth_stencil_state.stencil_front.reference = reference;
850  reference_updated = true;
851  }
852  }
853 
854  if (face_mask & stencil_face_back_bit)
855  {
856  if (m_depth_stencil_state.stencil_back.reference != reference)
857  {
858  m_depth_stencil_state.stencil_back.reference = reference;
859  reference_updated = true;
860  }
861  }
862 
863  if (reference_updated)
864  {
865  const auto gl_face = stencil_face_lut[face_mask];
866 
867  if (face_mask == stencil_face_front_and_back)
868  {
869  if (m_depth_stencil_state.stencil_front.compare_mask == m_depth_stencil_state.stencil_back.compare_mask &&
870  m_depth_stencil_state.stencil_front.compare_op == m_depth_stencil_state.stencil_back.compare_op)
871  {
872  const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)];
873  glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast<GLint>(reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_front.compare_mask));
874  }
875  else
876  {
877  const auto gl_compare_op_front = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)];
878  const auto gl_compare_op_back = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_back.compare_op)];
879  glStencilFuncSeparate(GL_FRONT, gl_compare_op_front, std::bit_cast<GLint>(reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_front.compare_mask));
880  glStencilFuncSeparate(GL_BACK, gl_compare_op_back, std::bit_cast<GLint>(reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_back.compare_mask));
881  }
882  }
883  else if (face_mask == stencil_face_front_bit)
884  {
885  const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_front.compare_op)];
886  glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast<GLint>(reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_front.compare_mask));
887  }
888  else
889  {
890  const auto gl_compare_op = compare_op_lut[std::to_underlying(m_depth_stencil_state.stencil_back.compare_op)];
891  glStencilFuncSeparate(gl_face, gl_compare_op, std::bit_cast<GLint>(reference), std::bit_cast<GLuint>(m_depth_stencil_state.stencil_back.compare_mask));
892  }
893  }
894 }
895 
896 void pipeline::set_stencil_write_mask(std::uint8_t face_mask, std::uint32_t write_mask)
897 {
898  bool write_mask_updated = false;
899 
900  if (face_mask & stencil_face_front_bit)
901  {
902  if (m_depth_stencil_state.stencil_front.write_mask != write_mask)
903  {
904  m_depth_stencil_state.stencil_front.write_mask = write_mask;
905  write_mask_updated = true;
906  }
907  }
908 
909  if (face_mask & stencil_face_back_bit)
910  {
911  if (m_depth_stencil_state.stencil_back.write_mask != write_mask)
912  {
913  m_depth_stencil_state.stencil_back.write_mask = write_mask;
914  write_mask_updated = true;
915  }
916  }
917 
918  if (write_mask_updated)
919  {
920  const auto gl_face = stencil_face_lut[face_mask];
921  glStencilMaskSeparate(gl_face, std::bit_cast<GLuint>(write_mask));
922  }
923 }
924 
926 {
927  if (m_color_blend_state.logic_op_enabled != enabled)
928  {
929  m_color_blend_state.logic_op_enabled = enabled;
930 
931  if (enabled)
932  {
933  glEnable(GL_COLOR_LOGIC_OP);
934  }
935  else
936  {
937  glDisable(GL_COLOR_LOGIC_OP);
938  }
939  }
940 }
941 
943 {
944  if (m_color_blend_state.logic_op != logic_op)
945  {
946  m_color_blend_state.logic_op = logic_op;
947 
948  const auto gl_logic_op = logic_op_lut[std::to_underlying(logic_op)];
949  glLogicOp(gl_logic_op);
950  }
951 }
952 
954 {
955  if (m_color_blend_state.blend_enabled != enabled)
956  {
957  m_color_blend_state.blend_enabled = enabled;
958 
959  if (enabled)
960  {
961  glEnable(GL_BLEND);
962  }
963  else
964  {
965  glDisable(GL_BLEND);
966  }
967  }
968 }
969 
971 {
972  if (m_color_blend_state.color_blend_equation.src_color_blend_factor != equation.src_color_blend_factor ||
973  m_color_blend_state.color_blend_equation.dst_color_blend_factor != equation.dst_color_blend_factor ||
974  m_color_blend_state.color_blend_equation.src_alpha_blend_factor != equation.src_alpha_blend_factor ||
976  {
981 
982  const auto gl_src_rgb = blend_factor_lut[std::to_underlying(equation.src_color_blend_factor)];
983  const auto gl_dst_rgb = blend_factor_lut[std::to_underlying(equation.dst_color_blend_factor)];
984  const auto gl_src_alpha = blend_factor_lut[std::to_underlying(equation.src_alpha_blend_factor)];
985  const auto gl_dst_alpha = blend_factor_lut[std::to_underlying(equation.dst_alpha_blend_factor)];
986 
987  glBlendFuncSeparate(gl_src_rgb, gl_dst_rgb, gl_src_alpha, gl_dst_alpha);
988  }
989 
990  if (m_color_blend_state.color_blend_equation.color_blend_op != equation.color_blend_op ||
991  m_color_blend_state.color_blend_equation.alpha_blend_op != equation.alpha_blend_op)
992  {
993  m_color_blend_state.color_blend_equation.color_blend_op = equation.color_blend_op;
994  m_color_blend_state.color_blend_equation.alpha_blend_op = equation.alpha_blend_op;
995 
996  const auto gl_mode_rgb = blend_op_lut[std::to_underlying(equation.color_blend_op)];
997  const auto gl_mode_alpha = blend_op_lut[std::to_underlying(equation.alpha_blend_op)];
998 
999  glBlendEquationSeparate(gl_mode_rgb, gl_mode_alpha);
1000  }
1001 }
1002 
1003 void pipeline::set_color_write_mask(std::uint8_t mask)
1004 {
1005  if (m_color_blend_state.color_write_mask != mask)
1006  {
1007  m_color_blend_state.color_write_mask = mask;
1008 
1009  glColorMask
1010  (
1011  mask & color_component_r_bit,
1012  mask & color_component_g_bit,
1013  mask & color_component_b_bit,
1014  mask & color_component_a_bit
1015  );
1016  }
1017 }
1018 
1019 void pipeline::set_blend_constants(const std::array<float, 4>& blend_constants)
1020 {
1021  if (m_color_blend_state.blend_constants != blend_constants)
1022  {
1023  m_color_blend_state.blend_constants = blend_constants;
1024  glBlendColor(blend_constants[0], blend_constants[1], blend_constants[2], blend_constants[3]);
1025  }
1026 }
1027 
1028 void pipeline::draw(std::uint32_t vertex_count, std::uint32_t instance_count, std::uint32_t first_vertex, std::uint32_t first_instance)
1029 {
1030  glDrawArraysInstancedBaseInstance
1031  (
1032  primitive_topology_lut[std::to_underlying(m_input_assembly_state.topology)],
1033  static_cast<GLint>(first_vertex),
1034  static_cast<GLsizei>(vertex_count),
1035  static_cast<GLsizei>(instance_count),
1036  static_cast<GLuint>(first_instance)
1037  );
1038 }
1039 
1040 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)
1041 {
1042  glDrawElementsInstancedBaseInstance
1043  (
1044  primitive_topology_lut[std::to_underlying(m_input_assembly_state.topology)],
1045  static_cast<GLsizei>(instance_count),
1046  GL_UNSIGNED_INT,// GL_UNSIGNED_SHORT, GL_UNSIGNED_BYTE
1047  reinterpret_cast<const GLvoid*>(first_index * sizeof(unsigned int)),
1048  static_cast<GLsizei>(instance_count),
1049  static_cast<GLuint>(first_instance)
1050  );
1051 }
1052 
1053 void pipeline::clear_attachments(std::uint8_t mask, const clear_value& value)
1054 {
1055  GLbitfield gl_clear_mask = 0;
1056 
1057  if (mask & color_clear_bit)
1058  {
1059  // Add color attachment to OpenGL clear mask
1060  gl_clear_mask |= GL_COLOR_BUFFER_BIT;
1061 
1062  if (m_clear_value.color != value.color)
1063  {
1064  // Update color clear value
1065  glClearColor(value.color[0], value.color[1], value.color[2], value.color[3]);
1066  m_clear_value.color = value.color;
1067  }
1068  }
1069 
1070  if (mask & depth_clear_bit)
1071  {
1072  // Add depth attachment to OpenGL clear mask
1073  gl_clear_mask |= GL_DEPTH_BUFFER_BIT;
1074 
1075  if (m_clear_value.depth != value.depth)
1076  {
1077  // Update depth clear value
1078  glClearDepth(value.depth);
1079  m_clear_value.depth = value.depth;
1080  }
1081  }
1082 
1083  if (mask & stencil_clear_bit)
1084  {
1085  // Add stencil attachment to OpenGL clear mask
1086  gl_clear_mask |= GL_STENCIL_BUFFER_BIT;
1087 
1088  if (m_clear_value.stencil != value.stencil)
1089  {
1090  // Update stencil clear value
1091  glClearStencil(static_cast<GLint>(value.stencil));
1092  m_clear_value.stencil = value.stencil;
1093  }
1094  }
1095 
1096  // Clear attachments
1097  glClear(gl_clear_mask);
1098 }
1099 
1100 void pipeline::defaut_framebuffer_resized(std::uint32_t width, std::uint32_t height) noexcept
1101 {
1102  m_default_framebuffer_dimensions = {width, height};
1103 }
1104 
1105 void pipeline::fetch_vertex_input_state()
1106 {
1107 }
1108 
1109 void pipeline::fetch_input_assembly_state()
1110 {
1111  m_input_assembly_state.primitive_restart_enabled = glIsEnabled(GL_PRIMITIVE_RESTART);
1112 }
1113 
1114 void pipeline::fetch_viewport_state()
1115 {
1116  // Query viewport position and dimensions
1117  GLint gl_viewport[4];
1118  glGetIntegerv(GL_VIEWPORT, gl_viewport);
1119 
1120  // Query viewport depth range
1121  GLfloat gl_depth_range[2];
1122  glGetFloatv(GL_DEPTH_RANGE, gl_depth_range);
1123 
1124  // Query scissor box
1125  GLint gl_scissor_box[4] = {0, 0, 0, 0};
1126  glGetIntegerv(GL_SCISSOR_BOX, gl_scissor_box);
1127 
1128  // Match viewport state
1129  m_viewport_state.viewports =
1130  {{
1131  static_cast<float>(gl_viewport[0]),
1132  static_cast<float>(gl_viewport[1]),
1133  static_cast<float>(gl_viewport[2]),
1134  static_cast<float>(gl_viewport[3]),
1135  gl_depth_range[0],
1136  gl_depth_range[1]
1137  }};
1138  m_viewport_state.scissors =
1139  {{
1140  static_cast<std::int32_t>(gl_scissor_box[0]),
1141  static_cast<std::int32_t>(gl_scissor_box[1]),
1142  static_cast<std::uint32_t>(std::max(0, gl_scissor_box[2])),
1143  static_cast<std::uint32_t>(std::max(0, gl_scissor_box[3]))
1144  }};
1145 }
1146 
1147 void pipeline::fetch_rasterization_state()
1148 {
1149  // Query rasterizer discard
1150  bool gl_rasterizer_discard_enabled = glIsEnabled(GL_RASTERIZER_DISCARD);
1151 
1152  // Query fill mode
1153  GLint gl_fill_mode;
1154  glGetIntegerv(GL_POLYGON_MODE, &gl_fill_mode);
1155 
1156  // Query cull mode
1157  bool gl_cull_enabled = glIsEnabled(GL_CULL_FACE);
1158  GLint gl_cull_mode;
1159  glGetIntegerv(GL_CULL_FACE_MODE, &gl_cull_mode);
1160 
1161  // Query front face
1162  GLint gl_front_face;
1163  glGetIntegerv(GL_FRONT_FACE, &gl_front_face);
1164 
1165  // Query depth bias
1166  bool gl_depth_bias_enabled = glIsEnabled(GL_POLYGON_OFFSET_FILL) &&
1167  glIsEnabled(GL_POLYGON_OFFSET_LINE) &&
1168  glIsEnabled(GL_POLYGON_OFFSET_POINT);
1169  float gl_depth_bias_constant_factor;
1170  float gl_depth_bias_slope_factor;
1171  glGetFloatv(GL_POLYGON_OFFSET_UNITS, &gl_depth_bias_constant_factor);
1172  glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &gl_depth_bias_slope_factor);
1173 
1174  // Query depth clamp
1175  bool gl_depth_clamp_enabled = glIsEnabled(GL_DEPTH_CLAMP);
1176 
1177  // Query scissor test
1178  bool gl_scissor_test_enabled = glIsEnabled(GL_SCISSOR_TEST);
1179 
1180  // Query provoking vertex
1181  GLint gl_provoking_vertex;
1182  glGetIntegerv(GL_PROVOKING_VERTEX, &gl_provoking_vertex);
1183 
1184  // Query point size
1185  float gl_point_size;
1186  glGetFloatv(GL_POINT_SIZE, &gl_point_size);
1187 
1188  // Query line width
1189  float gl_line_width;
1190  glGetFloatv(GL_LINE_WIDTH, &gl_line_width);
1191 
1192  // Match rasterizer state
1193  m_rasterization_state.rasterizer_discard_enabled = gl_rasterizer_discard_enabled;
1194  m_rasterization_state.fill_mode = (gl_fill_mode == GL_POINT ? fill_mode::point : gl_fill_mode == GL_LINE ? fill_mode::line : fill_mode::fill);
1195  if (gl_cull_enabled)
1196  {
1197  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);
1198  }
1199  else
1200  {
1201  m_rasterization_state.cull_mode = cull_mode::none;
1202  }
1203  m_rasterization_state.front_face = (gl_front_face == GL_CW ? front_face::clockwise : front_face::counter_clockwise);
1204  m_rasterization_state.depth_bias_enabled = gl_depth_bias_enabled;
1205  m_rasterization_state.depth_bias_constant_factor = gl_depth_bias_constant_factor;
1206  m_rasterization_state.depth_bias_slope_factor = gl_depth_bias_slope_factor;
1207  m_rasterization_state.depth_clamp_enabled = gl_depth_clamp_enabled;
1208  m_rasterization_state.scissor_test_enabled = gl_scissor_test_enabled;
1209  m_rasterization_state.provoking_vertex_mode = (gl_provoking_vertex == GL_FIRST_VERTEX_CONVENTION ? provoking_vertex_mode::first : provoking_vertex_mode::last);
1210  m_rasterization_state.point_size = gl_point_size;
1211  m_rasterization_state.line_width = gl_line_width;
1212 }
1213 
1214 void pipeline::fetch_depth_stencil_state()
1215 {
1216  auto inv_compare_op_lut = [](GLint func) -> gl::compare_op
1217  {
1218  switch (func)
1219  {
1220  case GL_NEVER:
1221  return compare_op::never;
1222  case GL_LESS:
1223  return compare_op::less;
1224  case GL_EQUAL:
1225  return compare_op::equal;
1226  case GL_LEQUAL:
1228  case GL_GREATER:
1229  return compare_op::greater;
1230  case GL_NOTEQUAL:
1231  return compare_op::not_equal;
1232  case GL_GEQUAL:
1234  case GL_ALWAYS:
1235  default:
1236  return compare_op::always;
1237  }
1238  };
1239 
1240  auto inv_stencil_op_lut = [](GLint op) -> gl::stencil_op
1241  {
1242  switch (op)
1243  {
1244  case GL_KEEP:
1245  return stencil_op::keep;
1246  case GL_ZERO:
1247  return stencil_op::zero;
1248  case GL_REPLACE:
1249  return stencil_op::replace;
1250  case GL_INCR:
1252  case GL_DECR:
1254  case GL_INVERT:
1255  return stencil_op::invert;
1256  case GL_INCR_WRAP:
1258  case GL_DECR_WRAP:
1259  default:
1261  }
1262  };
1263 
1264  m_depth_stencil_state.depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
1265 
1266  GLboolean gl_depth_write_enabled;
1267  glGetBooleanv(GL_DEPTH_WRITEMASK, &gl_depth_write_enabled);
1268  m_depth_stencil_state.depth_write_enabled = gl_depth_write_enabled;
1269 
1270  GLint gl_depth_compare_op;
1271  glGetIntegerv(GL_DEPTH_FUNC, &gl_depth_compare_op);
1272  m_depth_stencil_state.depth_compare_op = inv_compare_op_lut(gl_depth_compare_op);
1273 
1274  m_depth_stencil_state.stencil_test_enabled = glIsEnabled(GL_STENCIL_TEST);
1275 
1276  // Stencil front
1277  {
1278  GLint gl_stencil_front_fail;
1279  GLint gl_stencil_front_pass_depth_pass;
1280  GLint gl_stencil_front_pass_depth_fail;
1281  GLint gl_stencil_front_func;
1282  GLint gl_stencil_front_value_mask;
1283  GLint gl_stencil_front_write_mask;
1284  GLint gl_stencil_front_ref;
1285 
1286  glGetIntegerv(GL_STENCIL_FAIL, &gl_stencil_front_fail);
1287  glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &gl_stencil_front_pass_depth_pass);
1288  glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &gl_stencil_front_pass_depth_fail);
1289  glGetIntegerv(GL_STENCIL_FUNC, &gl_stencil_front_func);
1290  glGetIntegerv(GL_STENCIL_VALUE_MASK, &gl_stencil_front_value_mask);
1291  glGetIntegerv(GL_STENCIL_WRITEMASK, &gl_stencil_front_write_mask);
1292  glGetIntegerv(GL_STENCIL_REF, &gl_stencil_front_ref);
1293 
1294  m_depth_stencil_state.stencil_front.fail_op = inv_stencil_op_lut(gl_stencil_front_fail);
1295  m_depth_stencil_state.stencil_front.pass_op = inv_stencil_op_lut(gl_stencil_front_pass_depth_pass);
1296  m_depth_stencil_state.stencil_front.depth_fail_op = inv_stencil_op_lut(gl_stencil_front_pass_depth_fail);
1297  m_depth_stencil_state.stencil_front.compare_op = inv_compare_op_lut(gl_stencil_front_func);
1298  m_depth_stencil_state.stencil_front.compare_mask = std::bit_cast<std::uint32_t>(gl_stencil_front_value_mask);
1299  m_depth_stencil_state.stencil_front.write_mask = std::bit_cast<std::uint32_t>(gl_stencil_front_write_mask);
1300  m_depth_stencil_state.stencil_front.reference = std::bit_cast<std::uint32_t>(gl_stencil_front_ref);
1301  }
1302 
1303  // Stencil back
1304  {
1305  GLint gl_stencil_back_fail;
1306  GLint gl_stencil_back_pass_depth_pass;
1307  GLint gl_stencil_back_pass_depth_fail;
1308  GLint gl_stencil_back_func;
1309  GLint gl_stencil_back_value_mask;
1310  GLint gl_stencil_back_write_mask;
1311  GLint gl_stencil_back_ref;
1312 
1313  glGetIntegerv(GL_STENCIL_BACK_FAIL, &gl_stencil_back_fail);
1314  glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &gl_stencil_back_pass_depth_pass);
1315  glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &gl_stencil_back_pass_depth_fail);
1316  glGetIntegerv(GL_STENCIL_BACK_FUNC, &gl_stencil_back_func);
1317  glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &gl_stencil_back_value_mask);
1318  glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &gl_stencil_back_write_mask);
1319  glGetIntegerv(GL_STENCIL_BACK_REF, &gl_stencil_back_ref);
1320 
1321  m_depth_stencil_state.stencil_back.fail_op = inv_stencil_op_lut(gl_stencil_back_fail);
1322  m_depth_stencil_state.stencil_back.pass_op = inv_stencil_op_lut(gl_stencil_back_pass_depth_pass);
1323  m_depth_stencil_state.stencil_back.depth_fail_op = inv_stencil_op_lut(gl_stencil_back_pass_depth_fail);
1324  m_depth_stencil_state.stencil_back.compare_op = inv_compare_op_lut(gl_stencil_back_func);
1325  m_depth_stencil_state.stencil_back.compare_mask = std::bit_cast<std::uint32_t>(gl_stencil_back_value_mask);
1326  m_depth_stencil_state.stencil_back.write_mask = std::bit_cast<std::uint32_t>(gl_stencil_back_write_mask);
1327  m_depth_stencil_state.stencil_back.reference = std::bit_cast<std::uint32_t>(gl_stencil_back_ref);
1328  }
1329 }
1330 
1331 void pipeline::fetch_color_blend_state()
1332 {
1333  auto inv_logic_op_lut = [](GLint op) -> gl::logic_op
1334  {
1335  switch (op)
1336  {
1337  case GL_CLEAR:
1339  case GL_AND:
1341  case GL_AND_REVERSE:
1343  case GL_COPY:
1345  case GL_AND_INVERTED:
1347  case GL_NOOP:
1349  case GL_XOR:
1351  case GL_OR:
1352  return gl::logic_op::bitwise_or;
1353  case GL_NOR:
1355  case GL_EQUIV:
1357  case GL_INVERT:
1359  case GL_OR_REVERSE:
1361  case GL_COPY_INVERTED:
1363  case GL_OR_INVERTED:
1365  case GL_NAND:
1367  case GL_SET:
1368  default:
1370  }
1371  };
1372 
1373  auto inv_blend_factor_lut = [](GLint func) -> gl::blend_factor
1374  {
1375  switch (func)
1376  {
1377  case GL_ZERO:
1378  return blend_factor::zero;
1379  case GL_ONE:
1380  return blend_factor::one;
1381  case GL_SRC_COLOR:
1382  return blend_factor::src_color;
1383  case GL_ONE_MINUS_SRC_COLOR:
1385  case GL_DST_COLOR:
1386  return blend_factor::dst_color;
1387  case GL_ONE_MINUS_DST_COLOR:
1389  case GL_SRC_ALPHA:
1390  return blend_factor::src_alpha;
1391  case GL_ONE_MINUS_SRC_ALPHA:
1393  case GL_DST_ALPHA:
1394  return blend_factor::dst_alpha;
1395  case GL_ONE_MINUS_DST_ALPHA:
1397  case GL_CONSTANT_COLOR:
1399  case GL_ONE_MINUS_CONSTANT_COLOR:
1401  case GL_CONSTANT_ALPHA:
1403  case GL_ONE_MINUS_CONSTANT_ALPHA:
1405  case GL_SRC_ALPHA_SATURATE:
1407  case GL_SRC1_COLOR:
1408  return blend_factor::src1_color;
1409  case GL_ONE_MINUS_SRC1_COLOR:
1411  case GL_SRC1_ALPHA:
1412  return blend_factor::src1_alpha;
1413  case GL_ONE_MINUS_SRC1_ALPHA:
1414  default:
1416  }
1417  };
1418 
1419  auto inv_blend_op_lut = [](GLint mode) -> gl::blend_op
1420  {
1421  switch (mode)
1422  {
1423  case GL_FUNC_ADD:
1424  return blend_op::add;
1425  case GL_FUNC_SUBTRACT:
1426  return blend_op::subtract;
1427  case GL_FUNC_REVERSE_SUBTRACT:
1429  case GL_MIN:
1430  return blend_op::min;
1431  case GL_MAX:
1432  default:
1433  return blend_op::max;
1434  }
1435  };
1436 
1437  m_color_blend_state.logic_op_enabled = glIsEnabled(GL_COLOR_LOGIC_OP);
1438 
1439  GLint gl_logic_op;
1440  glGetIntegerv(GL_LOGIC_OP_MODE, &gl_logic_op);
1441 
1442  m_color_blend_state.logic_op = inv_logic_op_lut(gl_logic_op);
1443 
1444  m_color_blend_state.blend_enabled = glIsEnabled(GL_BLEND);
1445 
1446  GLint gl_blend_src_rgb;
1447  GLint gl_blend_dst_rgb;
1448  GLint gl_blend_equation_rgb;
1449  GLint gl_blend_src_alpha;
1450  GLint gl_blend_dst_alpha;
1451  GLint gl_blend_equation_alpha;
1452  glGetIntegerv(GL_BLEND_SRC_RGB, &gl_blend_src_rgb);
1453  glGetIntegerv(GL_BLEND_DST_RGB, &gl_blend_dst_rgb);
1454  glGetIntegerv(GL_BLEND_EQUATION_RGB, &gl_blend_equation_rgb);
1455  glGetIntegerv(GL_BLEND_SRC_ALPHA, &gl_blend_src_alpha);
1456  glGetIntegerv(GL_BLEND_DST_ALPHA, &gl_blend_dst_alpha);
1457  glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &gl_blend_equation_alpha);
1458 
1459  m_color_blend_state.color_blend_equation.src_color_blend_factor = inv_blend_factor_lut(gl_blend_src_rgb);
1460  m_color_blend_state.color_blend_equation.dst_color_blend_factor = inv_blend_factor_lut(gl_blend_dst_rgb);
1461  m_color_blend_state.color_blend_equation.color_blend_op = inv_blend_op_lut(gl_blend_equation_rgb);
1462  m_color_blend_state.color_blend_equation.src_alpha_blend_factor = inv_blend_factor_lut(gl_blend_src_alpha);
1463  m_color_blend_state.color_blend_equation.dst_alpha_blend_factor = inv_blend_factor_lut(gl_blend_dst_alpha);
1464  m_color_blend_state.color_blend_equation.alpha_blend_op = inv_blend_op_lut(gl_blend_equation_alpha);
1465 
1466  GLboolean gl_color_writemask[4];
1467  glGetBooleanv(GL_COLOR_WRITEMASK, gl_color_writemask);
1468 
1469  m_color_blend_state.color_write_mask =
1470  static_cast<std::uint8_t>(gl_color_writemask[0]) |
1471  (static_cast<std::uint8_t>(gl_color_writemask[1]) << 1) |
1472  (static_cast<std::uint8_t>(gl_color_writemask[2]) << 2) |
1473  (static_cast<std::uint8_t>(gl_color_writemask[3]) << 3);
1474 
1475  glGetFloatv(GL_BLEND_COLOR, m_color_blend_state.blend_constants.data());
1476 }
1477 
1478 void pipeline::fetch_clear_value()
1479 {
1480  // Query clear values
1481  GLfloat gl_color_clear[4];
1482  GLfloat gl_depth_clear;
1483  GLint gl_stencil_clear;
1484  glGetFloatv(GL_COLOR_CLEAR_VALUE, gl_color_clear);
1485  glGetFloatv(GL_DEPTH_CLEAR_VALUE, &gl_depth_clear);
1486  glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &gl_stencil_clear);
1487 
1488  // Match clear state
1489  m_clear_value.color = {gl_color_clear[0], gl_color_clear[1], gl_color_clear[2], gl_color_clear[3]};
1490  m_clear_value.depth = gl_depth_clear;
1491  m_clear_value.stencil = static_cast<std::uint32_t>(gl_stencil_clear);
1492 }
1493 
1494 } // namespace gl
void 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)
Sets the stencil operations.
Definition: pipeline.cpp:702
void set_primitive_topology(primitive_topology topology)
Sets the primitive topology to use for drawing.
Definition: pipeline.cpp:356
void clear_attachments(std::uint8_t mask, const clear_value &value)
Clears the color, depth, or stencil buffers of current attachments.
Definition: pipeline.cpp:1053
void set_fill_mode(fill_mode mode)
Sets the polygon rasterization mode.
Definition: pipeline.cpp:477
void bind_shader_program(const gl::shader_program *shader_program)
Sets the vertex input.
Definition: pipeline.cpp:292
void set_color_write_mask(std::uint8_t mask)
Sets the color write mask.
Definition: pipeline.cpp:1003
void set_blend_constants(const std::array< float, 4 > &blend_constants)
Sets the values of the blend constants.
Definition: pipeline.cpp:1019
void bind_framebuffer(const gl::framebuffer *framebuffer)
Sets the vertex input.
Definition: pipeline.cpp:275
void set_primitive_restart_enabled(bool enabled)
Controls whether a special vertex index value is treated as restarting the assembly of primitives.
Definition: pipeline.cpp:364
void set_stencil_write_mask(std::uint8_t face_mask, std::uint32_t write_mask)
Sets the stencil write mask.
Definition: pipeline.cpp:896
void set_depth_clamp_enabled(bool enabled)
Controls whether depth clamping is enabled.
Definition: pipeline.cpp:585
void set_depth_bias_factors(float constant_factor, float slope_factor)
Sets depth bias factors.
Definition: pipeline.cpp:572
void set_color_blend_enabled(bool enabled)
Controls whether blending is enabled for the corresponding color attachment.
Definition: pipeline.cpp:953
void set_scissor_test_enabled(bool enabled)
Enables or disables scissor testing.
Definition: pipeline.cpp:602
void set_point_size(float size)
Sets the the diameter of rasterized points.
Definition: pipeline.cpp:629
void set_cull_mode(cull_mode mode)
Sets the triangle culling mode.
Definition: pipeline.cpp:503
void set_front_face(front_face face)
Sets the front-facing triangle orientation.
Definition: pipeline.cpp:541
void set_stencil_test_enabled(bool enabled)
Controls whether stencil testing is enabled.
Definition: pipeline.cpp:685
void set_viewport(std::uint32_t first_viewport, std::span< const viewport > viewports)
Sets one or more viewports.
Definition: pipeline.cpp:381
void set_provoking_vertex_mode(provoking_vertex_mode mode)
Sets the vertex to be used as the source of data for flat-shaded varyings.
Definition: pipeline.cpp:619
void 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)
Draws primitives with indexed vertices.
Definition: pipeline.cpp:1040
void set_depth_bias_enabled(bool enabled)
Controls whether to bias fragment depth values.
Definition: pipeline.cpp:551
void set_depth_write_enabled(bool enabled)
Controls whether depth writes are enabled.
Definition: pipeline.cpp:666
void set_logic_op(gl::logic_op logic_op)
Selects which logical operation to apply.
Definition: pipeline.cpp:942
void set_scissor(std::uint32_t first_scissor, std::span< const scissor_region > scissors)
Sets one or more scissor regions.
Definition: pipeline.cpp:424
void set_stencil_reference(std::uint8_t face_mask, std::uint32_t reference)
Sets the stencil reference value.
Definition: pipeline.cpp:841
void 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)
Binds vertex buffers.
Definition: pipeline.cpp:326
pipeline()
Constructs a pipeline.
Definition: pipeline.cpp:229
void set_color_blend_equation(const color_blend_equation &equation)
Sets the color blend factors and operations.
Definition: pipeline.cpp:970
void set_logic_op_enabled(bool enabled)
Controls whether whether logical operations are enabled.
Definition: pipeline.cpp:925
void set_depth_compare_op(gl::compare_op compare_op)
Sets the depth comparison operator.
Definition: pipeline.cpp:675
void draw(std::uint32_t vertex_count, std::uint32_t instance_count, std::uint32_t first_vertex, std::uint32_t first_instance)
Draws primitives.
Definition: pipeline.cpp:1028
void set_depth_test_enabled(bool enabled)
Controls whether depth testing is enabled.
Definition: pipeline.cpp:649
void bind_vertex_array(const vertex_array *array)
Binds a vertex array.
Definition: pipeline.cpp:309
void set_rasterizer_discard_enabled(bool enabled)
Controls whether primitives are discarded before the rasterization stage.
Definition: pipeline.cpp:460
void set_line_width(float width)
Sets the width of rasterized lines.
Definition: pipeline.cpp:639
void set_stencil_compare_mask(std::uint8_t face_mask, std::uint32_t compare_mask)
Sets the stencil compare mask.
Definition: pipeline.cpp:786
Shader program which can be linked to shader objects and executed.
Vertex arrays describes how vertex input attributes are stored in vertex buffers.
log_message< log_message_severity::fatal, Args... > log_fatal
Formats and logs a fatal error message.
Definition: log.hpp:158
log_message< log_message_severity::warning, Args... > log_warning
Formats and logs a warning message.
Definition: log.hpp:130
log_message< log_message_severity::debug, Args... > log_debug
Formats and logs a debug message.
Definition: log.hpp:102
log_message< log_message_severity::error, Args... > log_error
Formats and logs an error message.
Definition: log.hpp:144
Graphics library interface.
Definition: window.hpp:28
blend_factor
Source and destination color and alpha blending factors.
@ color_component_b_bit
Indicates that the B value is written to the color attachment for the appropriate sample.
@ color_component_r_bit
Indicates that the R value is written to the color attachment for the appropriate sample.
@ color_component_a_bit
Indicates that the A value is written to the color attachment for the appropriate sample.
@ color_component_g_bit
Indicates that the G value is written to the color attachment for the appropriate sample.
cull_mode
Triangle culling mode.
Definition: cull-mode.hpp:29
@ none
No triangles are discarded.
@ back
Back-facing triangles are discarded.
@ front_and_back
All triangles are discarded.
@ front
Front-facing triangles are discarded.
@ array
Image array view.
blend_op
Framebuffer blending operations.
Definition: blend-op.hpp:29
@ stencil_face_front_bit
Only the front set of stencil state is updated.
@ stencil_face_back_bit
Only the back set of stencil state is updated.
@ stencil_face_front_and_back
Both sets of stencil state are updated.
front_face
Polygon front-facing orientation.
Definition: front-face.hpp:29
@ counter_clockwise
Triangle with positive area is considered front-facing.
@ clockwise
Triangle with negative area is considered front-facing.
provoking_vertex_mode
Vertex to be used as the source of data for flat-shaded varyings.
@ first
Provoking vertex is the first non-adjacency vertex.
@ last
Provoking vertex is the last non-adjacency vertex.
stencil_op
Stencil comparison functions.
Definition: stencil-op.hpp:29
@ keep
Keeps the current value.
@ increment_and_clamp
Increments the current value and clamps to the maximum representable unsigned value.
@ invert
Bitwise-inverts the current value.
@ increment_and_wrap
Increments the current value and wraps to 0 when the maximum value would have been exceeded.
@ decrement_and_wrap
Decrements the current value and wraps to the maximum possible value when the value would go below 0.
@ replace
Sets the value to reference.
@ zero
Sets the value to 0.
@ decrement_and_clamp
Decrements the current value and clamps to 0.
primitive_topology
Primitive topologies.
logic_op
Framebuffer logical operations.
Definition: logic-op.hpp:29
fill_mode
Polygon rasterization mode.
Definition: fill-mode.hpp:29
@ line
Polygons edges are drawn as line segments.
@ point
Polygons vertices are drawn as points.
@ fill
Polygons are filled.
@ color_clear_bit
Indicates the color buffer should be cleared.
Definition: clear-bits.hpp:31
@ depth_clear_bit
Indicates the depth buffer should be cleared.
Definition: clear-bits.hpp:34
@ stencil_clear_bit
Indicates the stencil buffer should be cleared.
Definition: clear-bits.hpp:37
compare_op
Comparison operators.
Definition: compare-op.hpp:29
@ equal
Comparison evaluates reference == test.
@ less_or_equal
Comparison evaluates reference <= test.
@ greater
Comparison evaluates reference > test.
@ not_equal
Comparison evaluates reference != test.
@ greater_or_equal
Comparison evaluates reference >= test.
@ never
Comparison always evaluates false.
@ less
Comparison evaluates reference < test.
@ always
Comparison always evaluates true.
constexpr vector< T, N > max(const vector< T, N > &x, const vector< T, N > &y)
Returns a vector containing the maximum elements of two vectors.
Definition: vector.hpp:1328
T length(const quaternion< T > &q)
Calculates the length of a quaternion.
Definition: quaternion.hpp:602
Text and typography.
Definition: bitmap-font.cpp:24
Clear value.
Definition: clear-value.hpp:33
float depth
Depth clear value.
Definition: clear-value.hpp:38
std::array< float, 4 > color
Color clear values.
Definition: clear-value.hpp:35
std::uint32_t stencil
Stencil clear value.
Definition: clear-value.hpp:41
Color blend factors and operations.
blend_op color_blend_op
Selects which blend operation is used to calculate the RGB values to write to the color attachment.
blend_factor dst_color_blend_factor
Selects which blend factor is used to determine the RGB destination factors.
blend_op alpha_blend_op
Selects which blend operation is used to calculate the alpha values to write to the color attachment.
blend_factor dst_alpha_blend_factor
Selects which blend factor is used to determine the alpha destination factor.
blend_factor src_alpha_blend_factor
Selects which blend factor is used to determine the alpha source factor.
blend_factor src_color_blend_factor
Selects which blend factor is used to determine the RGB source factors.
gl::logic_op logic_op
Selects which logical operation to apply.
std::array< float, 4 > blend_constants
RGBA components of the blend constant that are used in blending, depending on the blend factor.
bool blend_enabled
Controls whether blending is enabled for the corresponding color attachment.
bool logic_op_enabled
Controls whether to apply logical operations.
gl::color_blend_equation color_blend_equation
Color blend factors and operations.
std::uint8_t color_write_mask
Bitmask indicating which of the RGBA components are enabled for writing.
stencil_op_state stencil_front
Stencil testing parameters for front faces.
compare_op depth_compare_op
Comparison operator to use in the depth comparison step of the depth test.
bool stencil_test_enabled
true if stencil testing is enabled, false otherwise.
bool depth_write_enabled
true if depth writes are enabled when depth testing is enabled, false otherwise.
bool depth_test_enabled
true if depth testing is enabled, false otherwise.
stencil_op_state stencil_back
Stencil testing parameters for back faces.
primitive_topology topology
Primitive topology.
bool primitive_restart_enabled
Controls whether a special vertex index value is treated as restarting the assembly of primitives.
bool scissor_test_enabled
true if scissor testing should be enabled, false otherwise.
bool rasterizer_discard_enabled
true if rasterizer discard should be enabled, false otherwise.
gl::cull_mode cull_mode
Triangle culling mode.
float depth_bias_constant_factor
Depth bias constant factor.
float point_size
Diameter of rasterized points.
gl::fill_mode fill_mode
Polygon rasterization mode.
gl::front_face front_face
Polygon front-facing orientation.
bool depth_clamp_enabled
true if depth clamp should be enabled, false otherwise.
gl::provoking_vertex_mode provoking_vertex_mode
Vertex to be used as the source of data for flat-shaded varyings.
bool depth_bias_enabled
true if depth bias should be enabled, false otherwise.
float line_width
Width of rasterized line segments.
float depth_bias_slope_factor
Depth bias slope factor.
std::vector< viewport > viewports
Active viewports.
std::vector< scissor_region > scissors
Active scissor regions.
stencil_op pass_op
Action performed on samples that pass both the depth and stencil tests.
std::uint32_t write_mask
Bits of the unsigned integer stencil values updated by the stencil test in the stencil framebuffer at...
stencil_op depth_fail_op
Action performed on samples that pass the stencil test and fail the depth test.
gl::compare_op compare_op
Comparison operator used in the stencil test.
std::uint32_t reference
Stencil reference value that is used in the unsigned stencil comparison.
stencil_op fail_op
Action performed on samples that fail the stencil test.
std::uint32_t compare_mask
Bits of the unsigned integer stencil values participating in the stencil test.
Viewport position, dimensions, and depth range.
Definition: viewport.hpp:31
float y
Y-coordinate of the viewport's lower left corner.
Definition: viewport.hpp:36
float x
X-coordinate of the viewport's lower left corner.
Definition: viewport.hpp:33
float max_depth
Maximum depth range of the viewport.
Definition: viewport.hpp:48
float width
Width of the viewport.
Definition: viewport.hpp:39
float min_depth
Minimum depth range of the viewport.
Definition: viewport.hpp:45
float height
Height of the viewport.
Definition: viewport.hpp:42