Serenity Operating System
1/*
2 * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
3 * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
4 * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#include <LibGL/GLContext.h>
10
11namespace GL {
12
13void GLContext::gl_clear_stencil(GLint s)
14{
15 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_clear_stencil, s);
16 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
17
18 m_clear_stencil = static_cast<u8>(s & ((1 << m_device_info.stencil_bits) - 1));
19}
20
21void GLContext::gl_stencil_func_separate(GLenum face, GLenum func, GLint ref, GLuint mask)
22{
23 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_stencil_func_separate, face, func, ref, mask);
24 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
25
26 RETURN_WITH_ERROR_IF(!(face == GL_FRONT || face == GL_BACK || face == GL_FRONT_AND_BACK), GL_INVALID_ENUM);
27
28 RETURN_WITH_ERROR_IF(!(func == GL_NEVER
29 || func == GL_LESS
30 || func == GL_LEQUAL
31 || func == GL_GREATER
32 || func == GL_GEQUAL
33 || func == GL_EQUAL
34 || func == GL_NOTEQUAL
35 || func == GL_ALWAYS),
36 GL_INVALID_ENUM);
37
38 ref = clamp(ref, 0, (1 << m_device_info.stencil_bits) - 1);
39
40 StencilFunctionOptions new_options = { func, ref, mask };
41 if (face == GL_FRONT || face == GL_FRONT_AND_BACK)
42 m_stencil_function[Face::Front] = new_options;
43 if (face == GL_BACK || face == GL_FRONT_AND_BACK)
44 m_stencil_function[Face::Back] = new_options;
45
46 m_stencil_configuration_dirty = true;
47}
48
49void GLContext::gl_stencil_mask_separate(GLenum face, GLuint mask)
50{
51 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_stencil_mask_separate, face, mask);
52 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
53
54 if (face == GL_FRONT || face == GL_FRONT_AND_BACK)
55 m_stencil_operation[Face::Front].write_mask = mask;
56 if (face == GL_BACK || face == GL_FRONT_AND_BACK)
57 m_stencil_operation[Face::Back].write_mask = mask;
58
59 m_stencil_configuration_dirty = true;
60}
61
62void GLContext::gl_stencil_op_separate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
63{
64 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_stencil_op_separate, face, sfail, dpfail, dppass);
65 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
66
67 RETURN_WITH_ERROR_IF(!(face == GL_FRONT || face == GL_BACK || face == GL_FRONT_AND_BACK), GL_INVALID_ENUM);
68
69 auto is_valid_op = [](GLenum op) -> bool {
70 return op == GL_KEEP || op == GL_ZERO || op == GL_REPLACE || op == GL_INCR || op == GL_INCR_WRAP
71 || op == GL_DECR || op == GL_DECR_WRAP || op == GL_INVERT;
72 };
73 RETURN_WITH_ERROR_IF(!is_valid_op(sfail), GL_INVALID_ENUM);
74 RETURN_WITH_ERROR_IF(!is_valid_op(dpfail), GL_INVALID_ENUM);
75 RETURN_WITH_ERROR_IF(!is_valid_op(dppass), GL_INVALID_ENUM);
76
77 auto update_stencil_operation = [&](Face face, GLenum sfail, GLenum dpfail, GLenum dppass) {
78 auto& stencil_operation = m_stencil_operation[face];
79 stencil_operation.op_fail = sfail;
80 stencil_operation.op_depth_fail = dpfail;
81 stencil_operation.op_pass = dppass;
82 };
83 if (face == GL_FRONT || face == GL_FRONT_AND_BACK)
84 update_stencil_operation(Face::Front, sfail, dpfail, dppass);
85 if (face == GL_BACK || face == GL_FRONT_AND_BACK)
86 update_stencil_operation(Face::Back, sfail, dpfail, dppass);
87
88 m_stencil_configuration_dirty = true;
89}
90
91void GLContext::sync_stencil_configuration()
92{
93 if (!m_stencil_configuration_dirty)
94 return;
95 m_stencil_configuration_dirty = false;
96
97 auto set_device_stencil = [&](GPU::Face face, StencilFunctionOptions func, StencilOperationOptions op) {
98 GPU::StencilConfiguration device_configuration;
99
100 // Stencil test function
101 auto map_func = [](GLenum func) -> GPU::StencilTestFunction {
102 switch (func) {
103 case GL_ALWAYS:
104 return GPU::StencilTestFunction::Always;
105 case GL_EQUAL:
106 return GPU::StencilTestFunction::Equal;
107 case GL_GEQUAL:
108 return GPU::StencilTestFunction::GreaterOrEqual;
109 case GL_GREATER:
110 return GPU::StencilTestFunction::Greater;
111 case GL_LESS:
112 return GPU::StencilTestFunction::Less;
113 case GL_LEQUAL:
114 return GPU::StencilTestFunction::LessOrEqual;
115 case GL_NEVER:
116 return GPU::StencilTestFunction::Never;
117 case GL_NOTEQUAL:
118 return GPU::StencilTestFunction::NotEqual;
119 }
120 VERIFY_NOT_REACHED();
121 };
122 device_configuration.test_function = map_func(func.func);
123 device_configuration.reference_value = func.reference_value;
124 device_configuration.test_mask = func.mask;
125
126 // Stencil operation
127 auto map_operation = [](GLenum operation) -> GPU::StencilOperation {
128 switch (operation) {
129 case GL_DECR:
130 return GPU::StencilOperation::Decrement;
131 case GL_DECR_WRAP:
132 return GPU::StencilOperation::DecrementWrap;
133 case GL_INCR:
134 return GPU::StencilOperation::Increment;
135 case GL_INCR_WRAP:
136 return GPU::StencilOperation::IncrementWrap;
137 case GL_INVERT:
138 return GPU::StencilOperation::Invert;
139 case GL_KEEP:
140 return GPU::StencilOperation::Keep;
141 case GL_REPLACE:
142 return GPU::StencilOperation::Replace;
143 case GL_ZERO:
144 return GPU::StencilOperation::Zero;
145 }
146 VERIFY_NOT_REACHED();
147 };
148 device_configuration.on_stencil_test_fail = map_operation(op.op_fail);
149 device_configuration.on_depth_test_fail = map_operation(op.op_depth_fail);
150 device_configuration.on_pass = map_operation(op.op_pass);
151 device_configuration.write_mask = op.write_mask;
152
153 m_rasterizer->set_stencil_configuration(face, device_configuration);
154 };
155 set_device_stencil(GPU::Face::Front, m_stencil_function[Face::Front], m_stencil_operation[Face::Front]);
156 set_device_stencil(GPU::Face::Back, m_stencil_function[Face::Back], m_stencil_operation[Face::Back]);
157}
158
159}