2021-11-29 18:18:34 +01:00
|
|
|
/*
|
2022-04-15 01:08:15 +02:00
|
|
|
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
|
|
|
|
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
|
2021-11-29 18:18:34 +01:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
2022-04-15 01:08:15 +02:00
|
|
|
#include <AK/LexicalPath.h>
|
|
|
|
#include <AK/String.h>
|
|
|
|
#include <LibCore/FileStream.h>
|
2021-11-29 18:18:34 +01:00
|
|
|
#include <LibGL/GL/gl.h>
|
|
|
|
#include <LibGL/GLContext.h>
|
|
|
|
#include <LibGfx/Bitmap.h>
|
2022-04-15 01:08:15 +02:00
|
|
|
#include <LibGfx/QOIWriter.h>
|
|
|
|
#include <LibTest/TestCase.h>
|
2021-11-29 18:18:34 +01:00
|
|
|
|
2022-10-09 15:23:23 -06:00
|
|
|
#ifdef AK_OS_SERENITY
|
2022-04-15 01:08:15 +02:00
|
|
|
# define REFERENCE_IMAGE_DIR "/usr/Tests/LibGL/reference-images"
|
|
|
|
#else
|
|
|
|
# define REFERENCE_IMAGE_DIR "reference-images"
|
|
|
|
#endif
|
|
|
|
#define SAVE_OUTPUT false
|
2021-11-29 18:18:34 +01:00
|
|
|
|
2022-04-15 01:08:15 +02:00
|
|
|
static NonnullOwnPtr<GL::GLContext> create_testing_context(int width, int height)
|
2021-11-29 18:18:34 +01:00
|
|
|
{
|
2022-04-15 01:08:15 +02:00
|
|
|
auto bitmap = MUST(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRx8888, { width, height }));
|
2022-09-16 05:58:30 -06:00
|
|
|
auto context = MUST(GL::create_context(*bitmap));
|
2021-11-29 18:18:34 +01:00
|
|
|
GL::make_context_current(context);
|
2022-04-15 01:08:15 +02:00
|
|
|
return context;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void expect_bitmap_equals_reference(Gfx::Bitmap const& bitmap, StringView test_name)
|
|
|
|
{
|
|
|
|
auto reference_filename = String::formatted("{}.qoi", test_name);
|
|
|
|
|
|
|
|
if constexpr (SAVE_OUTPUT) {
|
|
|
|
auto target_path = LexicalPath("/home/anon").append(reference_filename);
|
|
|
|
auto qoi_buffer = Gfx::QOIWriter::encode(bitmap);
|
|
|
|
auto qoi_output_stream = MUST(Core::OutputFileStream::open(target_path.string()));
|
|
|
|
auto number_of_bytes_written = qoi_output_stream.write(qoi_buffer);
|
|
|
|
qoi_output_stream.close();
|
|
|
|
EXPECT_EQ(number_of_bytes_written, qoi_buffer.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
auto reference_image_path = String::formatted(REFERENCE_IMAGE_DIR "/{}", reference_filename);
|
|
|
|
auto reference_bitmap = MUST(Gfx::Bitmap::try_load_from_file(reference_image_path));
|
|
|
|
EXPECT_EQ(reference_bitmap->visually_equals(bitmap), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE(0001_simple_triangle)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
2021-11-29 18:18:34 +01:00
|
|
|
|
|
|
|
glBegin(GL_TRIANGLES);
|
2022-04-15 01:08:15 +02:00
|
|
|
glColor3f(1, 1, 1);
|
2021-11-29 18:18:34 +01:00
|
|
|
glVertex2f(0, 1);
|
|
|
|
glVertex2f(-1, -1);
|
|
|
|
glVertex2f(1, -1);
|
|
|
|
glEnd();
|
|
|
|
|
2022-04-15 01:08:15 +02:00
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
2021-11-29 18:18:34 +01:00
|
|
|
context->present();
|
2022-07-11 17:32:29 +00:00
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0001_simple_triangle"sv);
|
2022-04-15 01:08:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE(0002_quad_color_interpolation)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
|
|
|
|
glColor3f(1, 0, 0);
|
|
|
|
glVertex2i(-1, -1);
|
|
|
|
glColor3f(0, 1, 0);
|
|
|
|
glVertex2i(1, -1);
|
|
|
|
glColor3f(0, 0, 1);
|
|
|
|
glVertex2i(1, 1);
|
|
|
|
glColor3f(1, 0, 1);
|
|
|
|
glVertex2i(-1, 1);
|
|
|
|
glEnd();
|
2021-11-29 18:18:34 +01:00
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
2022-04-15 01:08:15 +02:00
|
|
|
|
|
|
|
context->present();
|
2022-07-11 17:32:29 +00:00
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0002_quad_color_interpolation"sv);
|
2021-11-29 18:18:34 +01:00
|
|
|
}
|
2022-04-18 10:57:45 +02:00
|
|
|
|
|
|
|
TEST_CASE(0003_rect_w_coordinate_regression)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
|
|
|
glColor3f(0, 1, 0);
|
|
|
|
glRectf(-0.5f, -0.5f, 0.5f, 0.5f);
|
|
|
|
|
|
|
|
glBegin(GL_TRIANGLES);
|
|
|
|
glColor3f(1, 0, 0);
|
|
|
|
glVertex2i(-1, -1);
|
|
|
|
glVertex2i(1, -1);
|
|
|
|
glVertex2i(-1, 1);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
|
|
|
context->present();
|
2022-07-11 17:32:29 +00:00
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0003_rect_w_coordinate_regression"sv);
|
2022-04-18 10:57:45 +02:00
|
|
|
}
|
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 02:13:14 +02:00
|
|
|
|
|
|
|
TEST_CASE(0004_points)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
// Aliased points
|
|
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
|
|
glPointSize(1.f + i);
|
|
|
|
glBegin(GL_POINTS);
|
|
|
|
glVertex2f(-.5f + i * .5f, .5f);
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Anti-aliased points
|
|
|
|
glEnable(GL_POINT_SMOOTH);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
|
|
glPointSize(3.f - i);
|
|
|
|
glBegin(GL_POINTS);
|
|
|
|
glVertex2f(-.5f + i * .5f, -.5f);
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
|
|
|
context->present();
|
2022-07-11 17:32:29 +00:00
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0004_points"sv);
|
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 02:13:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE(0005_lines_antialiased)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
// Draw anti-aliased lines
|
|
|
|
glEnable(GL_LINE_SMOOTH);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
glBegin(GL_LINES);
|
|
|
|
for (size_t i = 0; i < 6; ++i) {
|
|
|
|
glVertex2f(-.9f, .25f - i * .1f);
|
|
|
|
glVertex2f(.9f, .9f - i * .36f);
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
|
|
|
context->present();
|
2022-07-11 17:32:29 +00:00
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0005_lines"sv);
|
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 02:13:14 +02:00
|
|
|
}
|
2022-08-24 23:47:49 +02:00
|
|
|
|
|
|
|
TEST_CASE(0006_test_rgb565_texture)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
GLuint texture_id;
|
|
|
|
glGenTextures(1, &texture_id);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texture_id);
|
|
|
|
u16 texture_data[] = { 0xF800, 0xC000, 0x8000, 0x07E0, 0x0600, 0x0400, 0x001F, 0x0018, 0x0010 };
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 3, 3, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, texture_data);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
glTexCoord2i(0, 0);
|
|
|
|
glVertex2i(-1, 1);
|
|
|
|
glTexCoord2i(0, 1);
|
|
|
|
glVertex2i(-1, -1);
|
|
|
|
glTexCoord2i(1, 1);
|
|
|
|
glVertex2i(1, -1);
|
|
|
|
glTexCoord2i(1, 0);
|
|
|
|
glVertex2i(1, 1);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
|
|
|
context->present();
|
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0006_test_rgb565_texture"sv);
|
|
|
|
}
|
2022-08-26 15:59:51 +02:00
|
|
|
|
|
|
|
TEST_CASE(0007_test_rgba_to_rgb_texture)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
GLuint texture_id;
|
|
|
|
glGenTextures(1, &texture_id);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texture_id);
|
|
|
|
|
|
|
|
// Write RGBA data with A = 0 to an RGB texture
|
|
|
|
u32 texture_data[] = { 0x00FF0000 };
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture_data);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glBegin(GL_TRIANGLES);
|
|
|
|
glTexCoord2i(0, 0);
|
|
|
|
glVertex2i(-1, 1);
|
|
|
|
glTexCoord2i(0, 1);
|
|
|
|
glVertex2i(-1, -1);
|
|
|
|
glTexCoord2i(1, 1);
|
|
|
|
glVertex2i(1, -1);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
|
|
|
context->present();
|
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0007_test_rgba_to_rgb_texture"sv);
|
|
|
|
}
|
2022-09-05 00:40:27 +02:00
|
|
|
|
|
|
|
TEST_CASE(0008_test_pop_matrix_regression)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
// Load identity matrix after popping
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glTranslatef(10.f, 10.f, 10.f);
|
|
|
|
glPushMatrix();
|
|
|
|
glPopMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
|
|
|
|
glBegin(GL_TRIANGLES);
|
|
|
|
glColor3f(0.f, 1.f, 0.f);
|
|
|
|
glVertex2f(.5f, -.5f);
|
|
|
|
glVertex2f(.0f, .5f);
|
|
|
|
glVertex2f(-.5f, -.5f);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
|
|
|
context->present();
|
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0008_test_pop_matrix_regression"sv);
|
|
|
|
}
|
2022-10-16 19:14:53 +02:00
|
|
|
|
|
|
|
TEST_CASE(0009_test_draw_elements_in_display_list)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
glColor3f(0.f, 0.f, 1.f);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
|
|
|
auto const list_index = glGenLists(1);
|
|
|
|
glNewList(list_index, GL_COMPILE);
|
|
|
|
float vertices[] = { 0.f, .5f, -.5f, -.5f, .5f, -.5f };
|
|
|
|
glVertexPointer(2, GL_FLOAT, 0, &vertices);
|
|
|
|
u8 indices[] = { 0, 1, 2 };
|
|
|
|
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, &indices);
|
|
|
|
glEndList();
|
|
|
|
|
|
|
|
// Modifying an index here should not have an effect
|
|
|
|
indices[0] = 2;
|
|
|
|
|
|
|
|
glCallList(list_index);
|
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
|
|
|
context->present();
|
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0009_test_draw_elements_in_display_list"sv);
|
|
|
|
}
|
2022-11-14 17:02:53 -07:00
|
|
|
|
|
|
|
TEST_CASE(0010_test_store_data_in_buffer)
|
|
|
|
{
|
|
|
|
auto context = create_testing_context(64, 64);
|
|
|
|
|
|
|
|
glColor3f(1.f, 0.f, 0.f);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
|
|
|
float vertices[] = { 0.f, .5f, -.5f, -.5f, .5f, -.5f };
|
|
|
|
u8 indices[] = { 0, 1, 2 };
|
|
|
|
|
|
|
|
GLuint buffers[2];
|
|
|
|
glGenBuffers(2, buffers);
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), vertices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3, indices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
glVertexPointer(2, GL_FLOAT, 0, 0);
|
|
|
|
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, 0);
|
|
|
|
|
|
|
|
glDeleteBuffers(2, buffers);
|
|
|
|
|
|
|
|
EXPECT_EQ(glGetError(), 0u);
|
|
|
|
|
|
|
|
context->present();
|
|
|
|
expect_bitmap_equals_reference(context->frontbuffer(), "0010_test_store_data_in_buffer"sv);
|
|
|
|
}
|