Serenity Operating System
1/*
2 * Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
3 * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/DeprecatedString.h>
9#include <AK/LexicalPath.h>
10#include <LibCore/File.h>
11#include <LibGL/GL/gl.h>
12#include <LibGL/GLContext.h>
13#include <LibGfx/Bitmap.h>
14#include <LibGfx/QOIWriter.h>
15#include <LibTest/TestCase.h>
16
17#ifdef AK_OS_SERENITY
18# define REFERENCE_IMAGE_DIR "/usr/Tests/LibGL/reference-images"
19#else
20# define REFERENCE_IMAGE_DIR "reference-images"
21#endif
22#define SAVE_OUTPUT false
23
24static NonnullOwnPtr<GL::GLContext> create_testing_context(int width, int height)
25{
26 auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { width, height }));
27 auto context = MUST(GL::create_context(*bitmap));
28 GL::make_context_current(context);
29 return context;
30}
31
32static void expect_bitmap_equals_reference(Gfx::Bitmap const& bitmap, StringView test_name)
33{
34 auto reference_filename = DeprecatedString::formatted("{}.qoi", test_name);
35
36 if constexpr (SAVE_OUTPUT) {
37 auto target_path = LexicalPath("/home/anon").append(reference_filename);
38 auto qoi_buffer = MUST(Gfx::QOIWriter::encode(bitmap));
39 auto qoi_output_stream = MUST(Core::File::open(target_path.string(), Core::File::OpenMode::Write));
40 MUST(qoi_output_stream->write_until_depleted(qoi_buffer));
41 }
42
43 auto reference_image_path = DeprecatedString::formatted(REFERENCE_IMAGE_DIR "/{}", reference_filename);
44 auto reference_bitmap = MUST(Gfx::Bitmap::load_from_file(reference_image_path));
45 EXPECT_EQ(reference_bitmap->visually_equals(bitmap), true);
46}
47
48TEST_CASE(0001_simple_triangle)
49{
50 auto context = create_testing_context(64, 64);
51
52 glBegin(GL_TRIANGLES);
53 glColor3f(1, 1, 1);
54 glVertex2f(0, 1);
55 glVertex2f(-1, -1);
56 glVertex2f(1, -1);
57 glEnd();
58
59 EXPECT_EQ(glGetError(), 0u);
60
61 context->present();
62 expect_bitmap_equals_reference(context->frontbuffer(), "0001_simple_triangle"sv);
63}
64
65TEST_CASE(0002_quad_color_interpolation)
66{
67 auto context = create_testing_context(64, 64);
68
69 glBegin(GL_QUADS);
70
71 glColor3f(1, 0, 0);
72 glVertex2i(-1, -1);
73 glColor3f(0, 1, 0);
74 glVertex2i(1, -1);
75 glColor3f(0, 0, 1);
76 glVertex2i(1, 1);
77 glColor3f(1, 0, 1);
78 glVertex2i(-1, 1);
79 glEnd();
80
81 EXPECT_EQ(glGetError(), 0u);
82
83 context->present();
84 expect_bitmap_equals_reference(context->frontbuffer(), "0002_quad_color_interpolation"sv);
85}
86
87TEST_CASE(0003_rect_w_coordinate_regression)
88{
89 auto context = create_testing_context(64, 64);
90
91 glEnable(GL_DEPTH_TEST);
92 glClear(GL_DEPTH_BUFFER_BIT);
93
94 glColor3f(0, 1, 0);
95 glRectf(-0.5f, -0.5f, 0.5f, 0.5f);
96
97 glBegin(GL_TRIANGLES);
98 glColor3f(1, 0, 0);
99 glVertex2i(-1, -1);
100 glVertex2i(1, -1);
101 glVertex2i(-1, 1);
102 glEnd();
103
104 EXPECT_EQ(glGetError(), 0u);
105
106 context->present();
107 expect_bitmap_equals_reference(context->frontbuffer(), "0003_rect_w_coordinate_regression"sv);
108}
109
110TEST_CASE(0004_points)
111{
112 auto context = create_testing_context(64, 64);
113
114 // Aliased points
115 for (size_t i = 0; i < 3; ++i) {
116 glPointSize(1.f + i);
117 glBegin(GL_POINTS);
118 glVertex2f(-.5f + i * .5f, .5f);
119 glEnd();
120 }
121
122 // Anti-aliased points
123 glEnable(GL_POINT_SMOOTH);
124 glEnable(GL_BLEND);
125 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
126
127 for (size_t i = 0; i < 3; ++i) {
128 glPointSize(3.f - i);
129 glBegin(GL_POINTS);
130 glVertex2f(-.5f + i * .5f, -.5f);
131 glEnd();
132 }
133
134 EXPECT_EQ(glGetError(), 0u);
135
136 context->present();
137 expect_bitmap_equals_reference(context->frontbuffer(), "0004_points"sv);
138}
139
140TEST_CASE(0005_lines_antialiased)
141{
142 auto context = create_testing_context(64, 64);
143
144 // Draw anti-aliased lines
145 glEnable(GL_LINE_SMOOTH);
146 glEnable(GL_BLEND);
147 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
148
149 glBegin(GL_LINES);
150 for (size_t i = 0; i < 6; ++i) {
151 glVertex2f(-.9f, .25f - i * .1f);
152 glVertex2f(.9f, .9f - i * .36f);
153 }
154 glEnd();
155
156 EXPECT_EQ(glGetError(), 0u);
157
158 context->present();
159 expect_bitmap_equals_reference(context->frontbuffer(), "0005_lines"sv);
160}
161
162TEST_CASE(0006_test_rgb565_texture)
163{
164 auto context = create_testing_context(64, 64);
165
166 GLuint texture_id;
167 glGenTextures(1, &texture_id);
168 glBindTexture(GL_TEXTURE_2D, texture_id);
169 u16 texture_data[] = { 0xF800, 0xC000, 0x8000, 0x07E0, 0x0600, 0x0400, 0x001F, 0x0018, 0x0010 };
170 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
171 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 3, 3, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, texture_data);
172 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
173
174 glEnable(GL_TEXTURE_2D);
175 glBegin(GL_QUADS);
176 glTexCoord2i(0, 0);
177 glVertex2i(-1, 1);
178 glTexCoord2i(0, 1);
179 glVertex2i(-1, -1);
180 glTexCoord2i(1, 1);
181 glVertex2i(1, -1);
182 glTexCoord2i(1, 0);
183 glVertex2i(1, 1);
184 glEnd();
185
186 EXPECT_EQ(glGetError(), 0u);
187
188 context->present();
189 expect_bitmap_equals_reference(context->frontbuffer(), "0006_test_rgb565_texture"sv);
190}
191
192TEST_CASE(0007_test_rgba_to_rgb_texture)
193{
194 auto context = create_testing_context(64, 64);
195
196 GLuint texture_id;
197 glGenTextures(1, &texture_id);
198 glBindTexture(GL_TEXTURE_2D, texture_id);
199
200 // Write RGBA data with A = 0 to an RGB texture
201 u32 texture_data[] = { 0x00FF0000 };
202 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture_data);
203 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
204
205 glEnable(GL_TEXTURE_2D);
206 glBegin(GL_TRIANGLES);
207 glTexCoord2i(0, 0);
208 glVertex2i(-1, 1);
209 glTexCoord2i(0, 1);
210 glVertex2i(-1, -1);
211 glTexCoord2i(1, 1);
212 glVertex2i(1, -1);
213 glEnd();
214
215 EXPECT_EQ(glGetError(), 0u);
216
217 context->present();
218 expect_bitmap_equals_reference(context->frontbuffer(), "0007_test_rgba_to_rgb_texture"sv);
219}
220
221TEST_CASE(0008_test_pop_matrix_regression)
222{
223 auto context = create_testing_context(64, 64);
224
225 // Load identity matrix after popping
226 glMatrixMode(GL_MODELVIEW);
227 glTranslatef(10.f, 10.f, 10.f);
228 glPushMatrix();
229 glPopMatrix();
230 glLoadIdentity();
231
232 glBegin(GL_TRIANGLES);
233 glColor3f(0.f, 1.f, 0.f);
234 glVertex2f(.5f, -.5f);
235 glVertex2f(.0f, .5f);
236 glVertex2f(-.5f, -.5f);
237 glEnd();
238
239 EXPECT_EQ(glGetError(), 0u);
240
241 context->present();
242 expect_bitmap_equals_reference(context->frontbuffer(), "0008_test_pop_matrix_regression"sv);
243}
244
245TEST_CASE(0009_test_draw_elements_in_display_list)
246{
247 auto context = create_testing_context(64, 64);
248
249 glColor3f(0.f, 0.f, 1.f);
250 glEnableClientState(GL_VERTEX_ARRAY);
251
252 auto const list_index = glGenLists(1);
253 glNewList(list_index, GL_COMPILE);
254 float vertices[] = { 0.f, .5f, -.5f, -.5f, .5f, -.5f };
255 glVertexPointer(2, GL_FLOAT, 0, &vertices);
256 u8 indices[] = { 0, 1, 2 };
257 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, &indices);
258 glEndList();
259
260 // Modifying an index here should not have an effect
261 indices[0] = 2;
262
263 glCallList(list_index);
264
265 EXPECT_EQ(glGetError(), 0u);
266
267 context->present();
268 expect_bitmap_equals_reference(context->frontbuffer(), "0009_test_draw_elements_in_display_list"sv);
269}
270
271TEST_CASE(0010_test_store_data_in_buffer)
272{
273 auto context = create_testing_context(64, 64);
274
275 glColor3f(1.f, 0.f, 0.f);
276 glEnableClientState(GL_VERTEX_ARRAY);
277
278 float vertices[] = { 0.f, .5f, -.5f, -.5f, .5f, -.5f };
279 u8 indices[] = { 0, 1, 2 };
280
281 GLuint buffers[2];
282 glGenBuffers(2, buffers);
283
284 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
285 glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), vertices, GL_STATIC_DRAW);
286
287 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
288 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3, indices, GL_STATIC_DRAW);
289
290 glVertexPointer(2, GL_FLOAT, 0, 0);
291 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, 0);
292
293 glDeleteBuffers(2, buffers);
294
295 EXPECT_EQ(glGetError(), 0u);
296
297 context->present();
298 expect_bitmap_equals_reference(context->frontbuffer(), "0010_test_store_data_in_buffer"sv);
299}
300
301TEST_CASE(0011_tex_env_combine_with_constant_color)
302{
303 auto context = create_testing_context(64, 64);
304
305 glEnable(GL_TEXTURE_2D);
306 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
307 glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
308 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
309
310 float color[4] = { .3f, .5f, .7f, 1.f };
311 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
312
313 glRecti(-1, -1, 1, 1);
314
315 EXPECT_EQ(glGetError(), 0u);
316
317 context->present();
318 expect_bitmap_equals_reference(context->frontbuffer(), "0011_tex_env_combine_with_constant_color"sv);
319}