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 <AK/TemporaryChange.h>
10#include <LibGL/GLContext.h>
11
12namespace GL {
13
14void GLContext::gl_call_list(GLuint list)
15{
16 if (m_gl_call_depth > max_allowed_gl_call_depth)
17 return;
18
19 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_call_list, list);
20
21 if (m_listings.size() < list)
22 return;
23
24 TemporaryChange change { m_gl_call_depth, m_gl_call_depth + 1 };
25
26 invoke_list(list);
27}
28
29void GLContext::gl_call_lists(GLsizei n, GLenum type, void const* lists)
30{
31 if (m_gl_call_depth > max_allowed_gl_call_depth)
32 return;
33
34 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_call_lists, n, type, lists);
35
36 RETURN_WITH_ERROR_IF(n < 0, GL_INVALID_VALUE);
37 RETURN_WITH_ERROR_IF(!(type == GL_BYTE
38 || type == GL_UNSIGNED_BYTE
39 || type == GL_SHORT
40 || type == GL_UNSIGNED_SHORT
41 || type == GL_INT
42 || type == GL_UNSIGNED_INT
43 || type == GL_FLOAT
44 || type == GL_2_BYTES
45 || type == GL_3_BYTES
46 || type == GL_4_BYTES),
47 GL_INVALID_ENUM);
48
49 TemporaryChange change { m_gl_call_depth, m_gl_call_depth + 1 };
50
51 auto invoke_all_lists = [&]<typename T>(T const* lists) {
52 for (int i = 0; i < n; ++i) {
53 auto list = static_cast<size_t>(lists[i]);
54 invoke_list(m_list_base + list);
55 }
56 };
57 switch (type) {
58 case GL_BYTE:
59 invoke_all_lists(static_cast<GLbyte const*>(lists));
60 break;
61 case GL_UNSIGNED_BYTE:
62 invoke_all_lists(static_cast<GLubyte const*>(lists));
63 break;
64 case GL_SHORT:
65 invoke_all_lists(static_cast<GLshort const*>(lists));
66 break;
67 case GL_UNSIGNED_SHORT:
68 invoke_all_lists(static_cast<GLushort const*>(lists));
69 break;
70 case GL_INT:
71 invoke_all_lists(static_cast<GLint const*>(lists));
72 break;
73 case GL_UNSIGNED_INT:
74 invoke_all_lists(static_cast<GLuint const*>(lists));
75 break;
76 case GL_FLOAT:
77 invoke_all_lists(static_cast<GLfloat const*>(lists));
78 break;
79 case GL_2_BYTES:
80 case GL_3_BYTES:
81 case GL_4_BYTES:
82 dbgln("GLContext FIXME: unimplemented glCallLists() with type {}", type);
83 break;
84 default:
85 VERIFY_NOT_REACHED();
86 }
87}
88
89void GLContext::gl_delete_lists(GLuint list, GLsizei range)
90{
91 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
92 RETURN_WITH_ERROR_IF(range < 0, GL_INVALID_VALUE);
93
94 if (m_listings.size() < list || m_listings.size() <= list + range)
95 return;
96
97 for (auto& entry : m_listings.span().slice(list - 1, range))
98 entry.entries.clear_with_capacity();
99}
100
101void GLContext::gl_end_list()
102{
103 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
104 RETURN_WITH_ERROR_IF(!m_current_listing_index.has_value(), GL_INVALID_OPERATION);
105
106 m_listings[m_current_listing_index->index] = move(m_current_listing_index->listing);
107 m_current_listing_index.clear();
108}
109
110GLuint GLContext::gl_gen_lists(GLsizei range)
111{
112 RETURN_VALUE_WITH_ERROR_IF(range <= 0, GL_INVALID_VALUE, 0);
113 RETURN_VALUE_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION, 0);
114
115 auto initial_entry = m_listings.size();
116 m_listings.resize(range + initial_entry);
117 return initial_entry + 1;
118}
119
120GLboolean GLContext::gl_is_list(GLuint list)
121{
122 RETURN_VALUE_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION, GL_FALSE);
123
124 return list < m_listings.size() ? GL_TRUE : GL_FALSE;
125}
126
127void GLContext::gl_list_base(GLuint base)
128{
129 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_list_base, base);
130
131 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
132
133 m_list_base = base;
134}
135
136void GLContext::gl_new_list(GLuint list, GLenum mode)
137{
138 RETURN_WITH_ERROR_IF(list == 0, GL_INVALID_VALUE);
139 RETURN_WITH_ERROR_IF(mode != GL_COMPILE && mode != GL_COMPILE_AND_EXECUTE, GL_INVALID_ENUM);
140 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
141 RETURN_WITH_ERROR_IF(m_current_listing_index.has_value(), GL_INVALID_OPERATION);
142
143 if (m_listings.size() < list)
144 return;
145
146 m_current_listing_index = CurrentListing { {}, static_cast<size_t>(list - 1), mode };
147}
148
149void GLContext::invoke_list(size_t list_index)
150{
151 auto& listing = m_listings[list_index - 1];
152 for (auto& entry : listing.entries) {
153 entry.function.visit([&](auto& function) {
154 entry.arguments.visit([&](auto& arguments) {
155 auto apply = [&]<typename... Args>(Args&&... args) {
156 if constexpr (requires { (this->*function)(forward<Args>(args)...); })
157 (this->*function)(forward<Args>(args)...);
158 };
159
160 arguments.apply_as_args(apply);
161 });
162 });
163 }
164}
165
166}