GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 187, 0 excluded
0.0%
Functions:
0 of 34, 0 excluded
0.0%
Branches:
0 of 208, 0 excluded
0.0%

libs/render/src/eu/render/state.cc
Line Branch Exec Source
1 #include "eu/render/state.h"
2
3
4 #include "eu/assert/assert.h"
5 #include "eu/base/cint.h"
6
7 #include "eu/render/texture.h"
8 #include "eu/render/uniform.h"
9 #include "eu/render/opengl_utils.h"
10
11 namespace eu::render
12 {
13
14 template<typename T>
15 bool should_change(std::optional<T>* current_state, T new_state)
16 {
17 // if there is a value, and that is the same... then don't update opengl
18 if (*current_state && *current_state == new_state)
19 {
20 return false;
21 }
22
23 *current_state = new_state;
24 return true;
25 }
26
27 void apply(std::optional<bool>* current_state, bool new_state, GLenum gl_type)
28 {
29 if (should_change(current_state, new_state))
30 {
31 if (new_state)
32 {
33 glEnable(gl_type);
34 }
35 else
36 {
37 glDisable(gl_type);
38 }
39 }
40 }
41
42 GLenum enum_from_c(Compare new_state)
43 {
44 switch (new_state)
45 {
46 case Compare::always: return GL_ALWAYS;
47 case Compare::never: return GL_NEVER;
48 case Compare::less: return GL_LESS;
49 case Compare::equal: return GL_EQUAL;
50 case Compare::less_equal: return GL_LEQUAL;
51 case Compare::greater: return GL_GREATER;
52 case Compare::not_equal: return GL_NOTEQUAL;
53 case Compare::greater_equal: return GL_GEQUAL;
54 default: DIE("Invalid depth func"); return GL_LESS;
55 }
56 }
57
58 GLenum enum_from_sa (StencilAction sa)
59 {
60 switch (sa)
61 {
62 case StencilAction::keep: return GL_KEEP;
63 case StencilAction::zero: return GL_ZERO;
64 case StencilAction::replace: return GL_REPLACE;
65 case StencilAction::increase: return GL_INCR;
66 case StencilAction::increase_wrap: return GL_INCR_WRAP;
67 case StencilAction::decrease: return GL_DECR;
68 case StencilAction::decrease_wrap: return GL_DECR_WRAP;
69 case StencilAction::invert: return GL_INVERT;
70 default: DIE("Invalid stencil action"); return GL_KEEP;
71 }
72 };
73
74 StateChanger::StateChanger(State* s)
75 : states(s)
76 {
77 }
78
79 StateChanger& StateChanger::cull_face(bool new_state)
80 {
81 apply(&states->cull_face, new_state, GL_CULL_FACE);
82 return *this;
83 }
84
85 StateChanger& StateChanger::blending(bool new_state)
86 {
87 apply(&states->blending, new_state, GL_BLEND);
88 return *this;
89 }
90
91 StateChanger& StateChanger::depth_test(bool new_state)
92 {
93 apply(&states->depth_test, new_state, GL_DEPTH_TEST);
94 return *this;
95 }
96
97 StateChanger& StateChanger::depth_mask(bool new_state)
98 {
99 if (should_change(&states->depth_mask, new_state))
100 {
101 glDepthMask(new_state ? GL_TRUE : GL_FALSE);
102 }
103 return *this;
104 }
105
106 StateChanger& StateChanger::depth_func(Compare new_state)
107 {
108 if (should_change(&states->depth_func, new_state))
109 {
110 const auto mode = enum_from_c(new_state);
111 glDepthFunc(mode);
112 }
113
114 return *this;
115 }
116
117 StateChanger& StateChanger::stencil_test(bool new_state)
118 {
119 apply(&states->stencil_test, new_state, GL_STENCIL_TEST);
120 return *this;
121 }
122
123 /// Set a bitmask that is ANDed with the stencil value about to be written to the buffer.
124 StateChanger& StateChanger::stencil_mask(u32 new_state)
125 {
126 if (should_change(&states->stencil_mask, new_state))
127 {
128 glStencilMask(new_state);
129 }
130
131 return *this;
132 }
133
134 StateChanger& StateChanger::stencil_func(Compare func, i32 ref, u32 mask)
135 {
136 if (should_change(&states->stencil_func, {func, ref, mask}))
137 {
138 glStencilFunc(enum_from_c(func), ref, mask);
139 }
140
141 return *this;
142 }
143
144 StateChanger& StateChanger::render_mode(RenderMode new_state)
145 {
146 if (should_change(&states->render_mode, new_state))
147 {
148 const auto mode = ([new_state]() -> GLenum
149 {
150 switch (new_state)
151 {
152 case RenderMode::fill: return GL_FILL;
153 case RenderMode::line: return GL_LINE;
154 case RenderMode::point: return GL_POINT;
155 default: DIE("Invalid render mode"); return GL_FILL;
156 }
157 })();
158 glPolygonMode(GL_FRONT_AND_BACK, mode);
159 }
160 return *this;
161 }
162
163 StateChanger& StateChanger::stencil_op(StencilAction stencil_fail, StencilAction depth_fail, StencilAction pass)
164 {
165 if (should_change(&states->stencil_op, {stencil_fail, depth_fail, pass}))
166 {
167 // todo(Gustav): look into using glStencilOpSeparate instead to specify front and back faces
168 // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glStencilOpSeparate.xhtml
169 glStencilOp(enum_from_sa(stencil_fail), enum_from_sa(depth_fail), enum_from_sa(pass));
170 }
171
172 return *this;
173 }
174
175 StateChanger& StateChanger::cull_face_mode(CullFace new_state)
176 {
177 if (should_change(&states->cull_face_mode, new_state))
178 {
179 const auto mode = ([new_state]() -> GLenum
180 {
181 switch (new_state)
182 {
183 case CullFace::front: return GL_FRONT;
184 case CullFace::back: return GL_BACK;
185 case CullFace::front_and_back: return GL_FRONT_AND_BACK;
186 default: DIE("Invalid cull face mode"); return GL_BACK;
187 }
188 })();
189 glCullFace(mode);
190 }
191 return *this;
192 }
193
194 StateChanger& StateChanger::blend_mode(Blend src, Blend dst)
195 {
196 if (should_change(&states->blend_mode, {src, dst}))
197 {
198 const auto convert = [](Blend b) -> GLenum
199 {
200 switch (b)
201 {
202 case Blend::zero: return GL_ZERO;
203 case Blend::one: return GL_ONE;
204 case Blend::src_color: return GL_SRC_COLOR;
205 case Blend::one_minus_src_color: return GL_ONE_MINUS_SRC_COLOR;
206 case Blend::dst_color: return GL_DST_COLOR;
207 case Blend::one_minus_dst_color: return GL_ONE_MINUS_DST_COLOR;
208 case Blend::src_alpha: return GL_SRC_ALPHA;
209 case Blend::one_minus_src_alpha: return GL_ONE_MINUS_SRC_ALPHA;
210 case Blend::dst_alpha: return GL_DST_ALPHA;
211 case Blend::one_minus_dst_alpha: return GL_ONE_MINUS_DST_ALPHA;
212 case Blend::constant_color: return GL_CONSTANT_COLOR;
213 case Blend::one_minus_constant_color: return GL_ONE_MINUS_CONSTANT_COLOR;
214 case Blend::constant_alpha: return GL_CONSTANT_ALPHA;
215 case Blend::one_minus_constant_alpha: return GL_ONE_MINUS_CONSTANT_ALPHA;
216 case Blend::src_alpha_saturate: return GL_SRC_ALPHA_SATURATE;
217 case Blend::src1_color: return GL_SRC1_COLOR;
218 case Blend::one_minus_src1_color: return GL_ONE_MINUS_SRC1_COLOR;
219 case Blend::src1_alpha: return GL_SRC1_ALPHA;
220 case Blend::one_minus_src1_alpha: return GL_ONE_MINUS_SRC1_ALPHA;
221 default: DIE("Invalid blend mode"); return GL_ZERO;
222 }
223 };
224 glBlendFunc(convert(src), convert(dst));
225 }
226 return *this;
227 }
228
229 StateChanger& StateChanger::activate_texture(int new_texture)
230 {
231 if (should_change(&states->active_texture, new_texture))
232 {
233 glActiveTexture(glenum_from_int(GL_TEXTURE0 + new_texture));
234 }
235 return *this;
236 }
237
238 StateChanger& StateChanger::bind_texture_2d(int slot, unsigned int texture)
239 {
240 ASSERT(slot == states->active_texture);
241 if (should_change(&states->texture_bound[sizet_from_int(slot)], texture))
242 {
243 glBindTexture(GL_TEXTURE_2D, texture);
244 }
245 return *this;
246 }
247
248 StateChanger& StateChanger::bind_texture_cubemap(int slot, unsigned int texture)
249 {
250 ASSERT(slot == states->active_texture);
251 if (should_change(&states->texture_bound[sizet_from_int(slot)], texture))
252 {
253 glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
254 }
255 return *this;
256 }
257
258 void bind_texture_2d(State* states, const Uniform& uniform, const Texture2d& texture)
259 {
260 if (uniform.is_valid() == false)
261 {
262 return;
263 }
264 ASSERT(uniform.texture >= 0);
265
266 StateChanger{states}.activate_texture(uniform.texture).bind_texture_2d(uniform.texture, texture.id);
267 }
268
269 void bind_texture_2d(State* states, const Uniform& uniform, const FrameBuffer& texture)
270 {
271 if (uniform.is_valid() == false)
272 {
273 return;
274 }
275 ASSERT(uniform.texture >= 0);
276 ASSERT(texture.debug_is_msaa == false);
277
278 StateChanger{states}.activate_texture(uniform.texture).bind_texture_2d(uniform.texture, texture.id);
279 }
280
281 void bind_texture_cubemap(State* states, const Uniform& uniform, const TextureCubemap& texture)
282 {
283 if (uniform.is_valid() == false)
284 {
285 return;
286 }
287 ASSERT(uniform.texture >= 0);
288
289 StateChanger{states}.activate_texture(uniform.texture).bind_texture_cubemap(uniform.texture, texture.id);
290 }
291
292 } // namespace eu::render
293