summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibGL
diff options
context:
space:
mode:
authorJelle Raaijmakers <jelle@gmta.nl>2022-08-24 23:47:49 +0200
committerAndreas Kling <kling@serenityos.org>2022-08-27 12:28:05 +0200
commiteb7c3d16fbfd805f9fbb3b819a661db10088fb56 (patch)
tree51e65bff9fead51d7c8f367d5e522d2f24deec31 /Userland/Libraries/LibGL
parentd7cfdfe6335de83f25d205cd9863fc18e2854763 (diff)
downloadserenity-eb7c3d16fbfd805f9fbb3b819a661db10088fb56.zip
LibGL+LibGPU+LibSoftGPU: Implement flexible pixel format conversion
A GPU (driver) is now responsible for reading and writing pixels from and to user data. The client (LibGL) is responsible for specifying how the user data must be interpreted or written to. This allows us to centralize all pixel format conversion in one class, `LibSoftGPU::PixelConverter`. For both the input and output image, it takes a specification containing the image dimensions, the pixel type and the selection (basically a clipping rect), and converts the pixels from the input image to the output image. Effectively this means we now support almost all OpenGL 1.5 formats, and all custom logic has disappeared from: - `glDrawPixels` - `glReadPixels` - `glTexImage2D` - `glTexSubImage2D` The new logic is still unoptimized, but on my machine I experienced no noticeable slowdown. :^)
Diffstat (limited to 'Userland/Libraries/LibGL')
-rw-r--r--Userland/Libraries/LibGL/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibGL/ContextParameter.cpp24
-rw-r--r--Userland/Libraries/LibGL/GL/gl.h97
-rw-r--r--Userland/Libraries/LibGL/GLContext.cpp392
-rw-r--r--Userland/Libraries/LibGL/GLContext.h6
-rw-r--r--Userland/Libraries/LibGL/Image.cpp223
-rw-r--r--Userland/Libraries/LibGL/Image.h20
-rw-r--r--Userland/Libraries/LibGL/Tex/Texture2D.cpp67
-rw-r--r--Userland/Libraries/LibGL/Tex/Texture2D.h11
-rw-r--r--Userland/Libraries/LibGL/Texture.cpp70
10 files changed, 462 insertions, 449 deletions
diff --git a/Userland/Libraries/LibGL/CMakeLists.txt b/Userland/Libraries/LibGL/CMakeLists.txt
index 511bbecdba..347ba5bb36 100644
--- a/Userland/Libraries/LibGL/CMakeLists.txt
+++ b/Userland/Libraries/LibGL/CMakeLists.txt
@@ -3,6 +3,7 @@ set(SOURCES
ContextParameter.cpp
GLAPI.cpp
GLContext.cpp
+ Image.cpp
Lighting.cpp
List.cpp
Matrix.cpp
diff --git a/Userland/Libraries/LibGL/ContextParameter.cpp b/Userland/Libraries/LibGL/ContextParameter.cpp
index 83a0326f29..c35048c7bd 100644
--- a/Userland/Libraries/LibGL/ContextParameter.cpp
+++ b/Userland/Libraries/LibGL/ContextParameter.cpp
@@ -584,4 +584,28 @@ GLboolean GLContext::gl_is_enabled(GLenum capability)
return parameter.value.boolean_value;
}
+GPU::PackingSpecification GLContext::get_packing_specification(PackingType packing_type)
+{
+ // Make use of the fact that the GL_PACK_* and GL_UNPACK_* enum constants are in the exact same order
+ auto const offset = (packing_type == PackingType::Unpack) ? 0 : (GL_PACK_SWAP_BYTES - GL_UNPACK_SWAP_BYTES);
+ auto get_packing_value = [&](GLenum packing_parameter) -> GLint {
+ GLint value;
+ gl_get_integerv(packing_parameter + offset, &value);
+ return value;
+ };
+
+ // FIXME: add support for GL_UNPACK_SKIP_PIXELS, GL_UNPACK_SKIP_ROWS and GL_UNPACK_LSB_FIRST
+ GLint byte_alignment { get_packing_value(GL_UNPACK_ALIGNMENT) };
+ GLint swap_bytes { get_packing_value(GL_UNPACK_SWAP_BYTES) };
+ GLint depth_stride { get_packing_value(GL_UNPACK_IMAGE_HEIGHT) };
+ GLint row_stride { get_packing_value(GL_UNPACK_ROW_LENGTH) };
+
+ return {
+ .depth_stride = static_cast<u32>(depth_stride),
+ .row_stride = static_cast<u32>(row_stride),
+ .byte_alignment = static_cast<u8>(byte_alignment),
+ .component_bytes_order = swap_bytes == GL_TRUE ? GPU::ComponentBytesOrder::Reversed : GPU::ComponentBytesOrder::Normal,
+ };
+}
+
}
diff --git a/Userland/Libraries/LibGL/GL/gl.h b/Userland/Libraries/LibGL/GL/gl.h
index 55bef5aafd..70052cfd5d 100644
--- a/Userland/Libraries/LibGL/GL/gl.h
+++ b/Userland/Libraries/LibGL/GL/gl.h
@@ -217,6 +217,7 @@ extern "C" {
#define GL_COMPILE_AND_EXECUTE 0x1301
// Type enums
+#define GL_BITMAP 0x1A00
#define GL_BYTE 0x1400
#define GL_UNSIGNED_BYTE 0x1401
#define GL_SHORT 0x1402
@@ -228,11 +229,23 @@ extern "C" {
#define GL_3_BYTES 0x1408
#define GL_4_BYTES 0x1409
#define GL_DOUBLE 0x140A
+#define GL_HALF_FLOAT 0x140B
+#define GL_UNSIGNED_BYTE_3_3_2 0x8032
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#define GL_UNSIGNED_INT_10_10_10_2 0x8036
+#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
#define GL_BOOL 0x8B56
// Format enums
#define GL_COLOR_INDEX 0x1900
-#define GL_COLOR_INDEX8_EXT 0x80E5
#define GL_STENCIL_INDEX 0x1901
#define GL_DEPTH_COMPONENT 0x1902
#define GL_RED 0x1903
@@ -244,21 +257,80 @@ extern "C" {
#define GL_LUMINANCE 0x1909
#define GL_LUMINANCE8 0x8040
#define GL_LUMINANCE_ALPHA 0x190A
-#define GL_LUMINANCE8_ALPHA8 0x8045
+#define GL_R3_G3_B2 0x2A10
#define GL_BGR 0x80E0
#define GL_BGRA 0x80E1
-#define GL_BITMAP 0x1A00
-
+#define GL_ALPHA4 0x803B
+#define GL_ALPHA8 0x803C
+#define GL_ALPHA12 0x803D
+#define GL_ALPHA16 0x803E
+#define GL_LUMINANCE4 0x803F
#define GL_LUMINANCE8 0x8040
+#define GL_LUMINANCE12 0x8041
+#define GL_LUMINANCE16 0x8042
+#define GL_LUMINANCE4_ALPHA4 0x8043
+#define GL_LUMINANCE6_ALPHA2 0x8044
+#define GL_LUMINANCE8_ALPHA8 0x8045
+#define GL_LUMINANCE12_ALPHA4 0x8046
+#define GL_LUMINANCE12_ALPHA12 0x8047
+#define GL_LUMINANCE16_ALPHA16 0x8048
+#define GL_INTENSITY 0x8049
+#define GL_INTENSITY4 0x804A
#define GL_INTENSITY8 0x804B
-#define GL_R3_G3_B2 0x2A10
+#define GL_INTENSITY12 0x804C
+#define GL_INTENSITY16 0x804D
#define GL_RGB4 0x804F
#define GL_RGB5 0x8050
#define GL_RGB8 0x8051
+#define GL_RGB10 0x8052
+#define GL_RGB12 0x8053
+#define GL_RGB16 0x8054
#define GL_RGBA2 0x8055
#define GL_RGBA4 0x8056
#define GL_RGB5_A1 0x8057
#define GL_RGBA8 0x8058
+#define GL_RGB10_A2 0x8059
+#define GL_RGBA12 0x805A
+#define GL_RGBA16 0x805B
+#define GL_COLOR_INDEX8_EXT 0x80E5
+#define GL_DEPTH_COMPONENT16 0x81A5
+#define GL_DEPTH_COMPONENT16_SGIX 0x81A5
+#define GL_DEPTH_COMPONENT24 0x81A6
+#define GL_DEPTH_COMPONENT24_SGIX 0x81A6
+#define GL_DEPTH_COMPONENT32 0x81A7
+#define GL_DEPTH_COMPONENT32_SGIX 0x81A7
+#define GL_RG 0x8227
+#define GL_COMPRESSED_ALPHA 0x84E9
+#define GL_COMPRESSED_ALPHA_ARB 0x84E9
+#define GL_COMPRESSED_LUMINANCE 0x84EA
+#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA
+#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB
+#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB
+#define GL_COMPRESSED_INTENSITY 0x84EC
+#define GL_COMPRESSED_INTENSITY_ARB 0x84EC
+#define GL_COMPRESSED_RGB 0x84ED
+#define GL_COMPRESSED_RGB_ARB 0x84ED
+#define GL_COMPRESSED_RGBA 0x84EE
+#define GL_COMPRESSED_RGBA_ARB 0x84EE
+#define GL_DEPTH_STENCIL 0x84F9
+#define GL_DEPTH_STENCIL_EXT 0x84F9
+#define GL_DEPTH_STENCIL_NV 0x84F9
+#define GL_SRGB 0x8C40
+#define GL_SRGB_EXT 0x8C40
+#define GL_SRGB8 0x8C41
+#define GL_SRGB8_EXT 0x8C41
+#define GL_SRGB_ALPHA 0x8C42
+#define GL_SRGB_ALPHA_EXT 0x8C42
+#define GL_SRGB8_ALPHA8 0x8C43
+#define GL_SRGB8_ALPHA8_EXT 0x8C43
+#define GL_SLUMINANCE_ALPHA 0x8C44
+#define GL_SLUMINANCE_ALPHA_EXT 0x8C44
+#define GL_SLUMINANCE8_ALPHA8 0x8C45
+#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45
+#define GL_SLUMINANCE 0x8C46
+#define GL_SLUMINANCE_EXT 0x8C46
+#define GL_SLUMINANCE8 0x8C47
+#define GL_SLUMINANCE8_EXT 0x8C47
// Lighting related defines
#define GL_LIGHTING 0x0B50
@@ -313,21 +385,6 @@ extern "C" {
#define GL_LINE 0x1B01
#define GL_FILL 0x1B02
-// Source pixel data format
-#define GL_UNSIGNED_BYTE 0x1401
-#define GL_UNSIGNED_BYTE_3_3_2 0x8032
-#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
-#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
-#define GL_UNSIGNED_INT_8_8_8_8 0x8035
-#define GL_UNSIGNED_INT_10_10_10_2 0x8036
-#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
-#define GL_UNSIGNED_SHORT_5_6_5 0x8363
-#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
-#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
-#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
-#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
-#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
-
// Stencil buffer operations
#define GL_KEEP 0x1E00
#define GL_REPLACE 0x1E01
diff --git a/Userland/Libraries/LibGL/GLContext.cpp b/Userland/Libraries/LibGL/GLContext.cpp
index 729ff0e09e..d31b28122a 100644
--- a/Userland/Libraries/LibGL/GLContext.cpp
+++ b/Userland/Libraries/LibGL/GLContext.cpp
@@ -11,8 +11,10 @@
#include <AK/Format.h>
#include <AK/Vector.h>
#include <LibGL/GLContext.h>
+#include <LibGL/Image.h>
#include <LibGPU/Device.h>
#include <LibGPU/Enums.h>
+#include <LibGPU/ImageDataLayout.h>
#include <LibGPU/ImageFormat.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Vector3.h>
@@ -497,289 +499,40 @@ void GLContext::gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei height,
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
RETURN_WITH_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
- RETURN_WITH_ERROR_IF(format != GL_COLOR_INDEX
- && format != GL_STENCIL_INDEX
- && format != GL_DEPTH_COMPONENT
- && format != GL_RED
- && format != GL_GREEN
- && format != GL_BLUE
- && format != GL_ALPHA
- && format != GL_RGB
- && format != GL_RGBA
- && format != GL_LUMINANCE
- && format != GL_LUMINANCE_ALPHA,
- GL_INVALID_ENUM);
-
- RETURN_WITH_ERROR_IF(type != GL_UNSIGNED_BYTE
- && type != GL_BYTE
- && type != GL_BITMAP
- && type != GL_UNSIGNED_SHORT
- && type != GL_SHORT
- && type != GL_BLUE
- && type != GL_UNSIGNED_INT
- && type != GL_INT
- && type != GL_FLOAT,
- GL_INVALID_ENUM);
-
- // FIXME: We only support RGBA buffers for now.
- // Once we add support for indexed color modes do the correct check here
- RETURN_WITH_ERROR_IF(format == GL_COLOR_INDEX, GL_INVALID_OPERATION);
-
- // FIXME: We do not have stencil buffers yet
- // Once we add support for stencil buffers do the correct check here
- RETURN_WITH_ERROR_IF(format == GL_STENCIL_INDEX, GL_INVALID_OPERATION);
+ auto pixel_type_or_error = get_validated_pixel_type(GL_NONE, GL_NONE, format, type);
+ RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
+
+ auto pixel_type = pixel_type_or_error.release_value();
+ GPU::ImageDataLayout output_layout = {
+ .pixel_type = pixel_type,
+ .packing = get_packing_specification(PackingType::Pack),
+ .dimensions = {
+ .width = static_cast<u32>(width),
+ .height = static_cast<u32>(height),
+ .depth = 1,
+ },
+ .selection = {
+ .width = static_cast<u32>(width),
+ .height = static_cast<u32>(height),
+ .depth = 1,
+ },
+ };
- if (format == GL_DEPTH_COMPONENT) {
- // FIXME: This check needs to be a bit more sophisticated. Currently the buffers
- // are hardcoded. Once we add proper structures for them we need to correct this check
+ if (pixel_type.format == GPU::PixelFormat::DepthComponent) {
+ // FIXME: This check needs to be a bit more sophisticated. Currently the buffers are
+ // hardcoded. Once we add proper structures for them we need to correct this check
// Error because only back buffer has a depth buffer
RETURN_WITH_ERROR_IF(m_current_read_buffer == GL_FRONT
|| m_current_read_buffer == GL_FRONT_LEFT
|| m_current_read_buffer == GL_FRONT_RIGHT,
GL_INVALID_OPERATION);
- }
-
- // Some helper functions for converting float values to integer types
- auto float_to_i8 = [](float f) -> GLchar {
- return static_cast<GLchar>((0x7f * min(max(f, 0.0f), 1.0f) - 1) / 2);
- };
-
- auto float_to_i16 = [](float f) -> GLshort {
- return static_cast<GLshort>((0x7fff * min(max(f, 0.0f), 1.0f) - 1) / 2);
- };
-
- auto float_to_i32 = [](float f) -> GLint {
- return static_cast<GLint>((0x7fffffff * min(max(f, 0.0f), 1.0f) - 1) / 2);
- };
-
- auto float_to_u8 = [](float f) -> GLubyte {
- return static_cast<GLubyte>(0xff * min(max(f, 0.0f), 1.0f));
- };
-
- auto float_to_u16 = [](float f) -> GLushort {
- return static_cast<GLushort>(0xffff * min(max(f, 0.0f), 1.0f));
- };
-
- auto float_to_u32 = [](float f) -> GLuint {
- return static_cast<GLuint>(0xffffffff * min(max(f, 0.0f), 1.0f));
- };
-
- u8 component_size = 0;
- switch (type) {
- case GL_BYTE:
- case GL_UNSIGNED_BYTE:
- component_size = 1;
- break;
- case GL_SHORT:
- case GL_UNSIGNED_SHORT:
- component_size = 2;
- break;
- case GL_INT:
- case GL_UNSIGNED_INT:
- case GL_FLOAT:
- component_size = 4;
- break;
- }
-
- if (format == GL_DEPTH_COMPONENT) {
- auto const row_stride = (width * component_size + m_pack_alignment - 1) / m_pack_alignment * m_pack_alignment;
-
- // Read from depth buffer
- for (GLsizei i = 0; i < height; ++i) {
- for (GLsizei j = 0; j < width; ++j) {
- float depth = m_rasterizer->get_depthbuffer_value(x + j, y + i);
- auto char_ptr = reinterpret_cast<char*>(pixels) + i * row_stride + j * component_size;
-
- switch (type) {
- case GL_BYTE:
- *reinterpret_cast<GLchar*>(char_ptr) = float_to_i8(depth);
- break;
- case GL_SHORT:
- *reinterpret_cast<GLshort*>(char_ptr) = float_to_i16(depth);
- break;
- case GL_INT:
- *reinterpret_cast<GLint*>(char_ptr) = float_to_i32(depth);
- break;
- case GL_UNSIGNED_BYTE:
- *reinterpret_cast<GLubyte*>(char_ptr) = float_to_u8(depth);
- break;
- case GL_UNSIGNED_SHORT:
- *reinterpret_cast<GLushort*>(char_ptr) = float_to_u16(depth);
- break;
- case GL_UNSIGNED_INT:
- *reinterpret_cast<GLuint*>(char_ptr) = float_to_u32(depth);
- break;
- case GL_FLOAT:
- *reinterpret_cast<GLfloat*>(char_ptr) = min(max(depth, 0.0f), 1.0f);
- break;
- }
- }
- }
- return;
- }
- bool write_red = false;
- bool write_green = false;
- bool write_blue = false;
- bool write_alpha = false;
- size_t component_count = 0;
- size_t red_offset = 0;
- size_t green_offset = 0;
- size_t blue_offset = 0;
- size_t alpha_offset = 0;
- char* red_ptr = nullptr;
- char* green_ptr = nullptr;
- char* blue_ptr = nullptr;
- char* alpha_ptr = nullptr;
-
- switch (format) {
- case GL_RGB:
- write_red = true;
- write_green = true;
- write_blue = true;
- component_count = 3;
- red_offset = 2;
- green_offset = 1;
- blue_offset = 0;
- break;
- case GL_RGBA:
- write_red = true;
- write_green = true;
- write_blue = true;
- write_alpha = true;
- component_count = 4;
- red_offset = 3;
- green_offset = 2;
- blue_offset = 1;
- alpha_offset = 0;
- break;
- case GL_RED:
- write_red = true;
- component_count = 1;
- red_offset = 0;
- break;
- case GL_GREEN:
- write_green = true;
- component_count = 1;
- green_offset = 0;
- break;
- case GL_BLUE:
- write_blue = true;
- component_count = 1;
- blue_offset = 0;
- break;
- case GL_ALPHA:
- write_alpha = true;
- component_count = 1;
- alpha_offset = 0;
- break;
- }
-
- auto const pixel_bytes = component_size * component_count;
- auto const row_alignment_bytes = (m_pack_alignment - ((width * pixel_bytes) % m_pack_alignment)) % m_pack_alignment;
-
- char* out_ptr = reinterpret_cast<char*>(pixels);
- for (int i = 0; i < (int)height; ++i) {
- for (int j = 0; j < (int)width; ++j) {
- Gfx::ARGB32 color {};
- if (m_current_read_buffer == GL_FRONT || m_current_read_buffer == GL_LEFT || m_current_read_buffer == GL_FRONT_LEFT) {
- if (y + i >= m_frontbuffer->width() || x + j >= m_frontbuffer->height())
- color = 0;
- else
- color = m_frontbuffer->scanline(y + i)[x + j];
- } else {
- color = m_rasterizer->get_color_buffer_pixel(x + j, y + i);
- }
-
- float red = ((color >> 24) & 0xff) / 255.0f;
- float green = ((color >> 16) & 0xff) / 255.0f;
- float blue = ((color >> 8) & 0xff) / 255.0f;
- float alpha = (color & 0xff) / 255.0f;
-
- // FIXME: Set up write pointers based on selected endianness (glPixelStore)
- red_ptr = out_ptr + (component_size * red_offset);
- green_ptr = out_ptr + (component_size * green_offset);
- blue_ptr = out_ptr + (component_size * blue_offset);
- alpha_ptr = out_ptr + (component_size * alpha_offset);
-
- switch (type) {
- case GL_BYTE:
- if (write_red)
- *reinterpret_cast<GLchar*>(red_ptr) = float_to_i8(red);
- if (write_green)
- *reinterpret_cast<GLchar*>(green_ptr) = float_to_i8(green);
- if (write_blue)
- *reinterpret_cast<GLchar*>(blue_ptr) = float_to_i8(blue);
- if (write_alpha)
- *reinterpret_cast<GLchar*>(alpha_ptr) = float_to_i8(alpha);
- break;
- case GL_UNSIGNED_BYTE:
- if (write_red)
- *reinterpret_cast<GLubyte*>(red_ptr) = float_to_u8(red);
- if (write_green)
- *reinterpret_cast<GLubyte*>(green_ptr) = float_to_u8(green);
- if (write_blue)
- *reinterpret_cast<GLubyte*>(blue_ptr) = float_to_u8(blue);
- if (write_alpha)
- *reinterpret_cast<GLubyte*>(alpha_ptr) = float_to_u8(alpha);
- break;
- case GL_SHORT:
- if (write_red)
- *reinterpret_cast<GLshort*>(red_ptr) = float_to_i16(red);
- if (write_green)
- *reinterpret_cast<GLshort*>(green_ptr) = float_to_i16(green);
- if (write_blue)
- *reinterpret_cast<GLshort*>(blue_ptr) = float_to_i16(blue);
- if (write_alpha)
- *reinterpret_cast<GLshort*>(alpha_ptr) = float_to_i16(alpha);
- break;
- case GL_UNSIGNED_SHORT:
- if (write_red)
- *reinterpret_cast<GLushort*>(red_ptr) = float_to_u16(red);
- if (write_green)
- *reinterpret_cast<GLushort*>(green_ptr) = float_to_u16(green);
- if (write_blue)
- *reinterpret_cast<GLushort*>(blue_ptr) = float_to_u16(blue);
- if (write_alpha)
- *reinterpret_cast<GLushort*>(alpha_ptr) = float_to_u16(alpha);
- break;
- case GL_INT:
- if (write_red)
- *reinterpret_cast<GLint*>(red_ptr) = float_to_i32(red);
- if (write_green)
- *reinterpret_cast<GLint*>(green_ptr) = float_to_i32(green);
- if (write_blue)
- *reinterpret_cast<GLint*>(blue_ptr) = float_to_i32(blue);
- if (write_alpha)
- *reinterpret_cast<GLint*>(alpha_ptr) = float_to_i32(alpha);
- break;
- case GL_UNSIGNED_INT:
- if (write_red)
- *reinterpret_cast<GLuint*>(red_ptr) = float_to_u32(red);
- if (write_green)
- *reinterpret_cast<GLuint*>(green_ptr) = float_to_u32(green);
- if (write_blue)
- *reinterpret_cast<GLuint*>(blue_ptr) = float_to_u32(blue);
- if (write_alpha)
- *reinterpret_cast<GLuint*>(alpha_ptr) = float_to_u32(alpha);
- break;
- case GL_FLOAT:
- if (write_red)
- *reinterpret_cast<GLfloat*>(red_ptr) = min(max(red, 0.0f), 1.0f);
- if (write_green)
- *reinterpret_cast<GLfloat*>(green_ptr) = min(max(green, 0.0f), 1.0f);
- if (write_blue)
- *reinterpret_cast<GLfloat*>(blue_ptr) = min(max(blue, 0.0f), 1.0f);
- if (write_alpha)
- *reinterpret_cast<GLfloat*>(alpha_ptr) = min(max(alpha, 0.0f), 1.0f);
- break;
- }
-
- out_ptr += pixel_bytes;
- }
-
- out_ptr += row_alignment_bytes;
+ m_rasterizer->blit_from_depth_buffer(pixels, { x, y }, output_layout);
+ } else if (pixel_type.format == GPU::PixelFormat::StencilIndex) {
+ dbgln("gl_read_pixels(): GL_STENCIL_INDEX is not yet supported");
+ } else {
+ m_rasterizer->blit_from_color_buffer(pixels, { x, y }, output_layout);
}
}
@@ -798,39 +551,10 @@ void GLContext::gl_draw_pixels(GLsizei width, GLsizei height, GLenum format, GLe
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_draw_pixels, width, height, format, type, data);
- RETURN_WITH_ERROR_IF(format < GL_COLOR_INDEX || format > GL_BGRA, GL_INVALID_ENUM);
-
- RETURN_WITH_ERROR_IF((type < GL_BYTE || type > GL_FLOAT)
- && (type < GL_UNSIGNED_BYTE_3_3_2 || type > GL_UNSIGNED_INT_10_10_10_2)
- && (type < GL_UNSIGNED_BYTE_2_3_3_REV || type > GL_UNSIGNED_INT_2_10_10_10_REV),
- GL_INVALID_ENUM);
-
- RETURN_WITH_ERROR_IF(type == GL_BITMAP && !(format == GL_COLOR_INDEX || format == GL_STENCIL_INDEX), GL_INVALID_ENUM);
-
+ RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
RETURN_WITH_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
// FIXME: GL_INVALID_OPERATION is generated if format is GL_STENCIL_INDEX and there is no stencil buffer
- // FIXME: GL_INVALID_OPERATION is generated if format is GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_RGB, GL_RGBA,
- // GL_BGR, GL_BGRA, GL_LUMINANCE, or GL_LUMINANCE_ALPHA, and the GL is in color index mode
-
- RETURN_WITH_ERROR_IF(format != GL_RGB
- && (type == GL_UNSIGNED_BYTE_3_3_2
- || type == GL_UNSIGNED_BYTE_2_3_3_REV
- || type == GL_UNSIGNED_SHORT_5_6_5
- || type == GL_UNSIGNED_SHORT_5_6_5_REV),
- GL_INVALID_OPERATION);
-
- RETURN_WITH_ERROR_IF(!(format == GL_RGBA || format == GL_BGRA)
- && (type == GL_UNSIGNED_SHORT_4_4_4_4
- || type == GL_UNSIGNED_SHORT_4_4_4_4_REV
- || type == GL_UNSIGNED_SHORT_5_5_5_1
- || type == GL_UNSIGNED_SHORT_1_5_5_5_REV
- || type == GL_UNSIGNED_INT_8_8_8_8
- || type == GL_UNSIGNED_INT_8_8_8_8_REV
- || type == GL_UNSIGNED_INT_10_10_10_2
- || type == GL_UNSIGNED_INT_2_10_10_10_REV),
- GL_INVALID_OPERATION);
-
// FIXME: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
// target and the buffer object's data store is currently mapped.
// FIXME: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
@@ -840,43 +564,31 @@ void GLContext::gl_draw_pixels(GLsizei width, GLsizei height, GLenum format, GLe
// target and data is not evenly divisible into the number of bytes needed to store in memory a datum
// indicated by type.
- RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
-
- // FIXME: we only support RGBA + UNSIGNED_BYTE and DEPTH_COMPONENT + UNSIGNED_SHORT, implement all combinations!
- if (!((format == GL_RGBA && type == GL_UNSIGNED_BYTE) || (format == GL_DEPTH_COMPONENT && type == GL_UNSIGNED_SHORT))) {
- dbgln_if(GL_DEBUG, "gl_draw_pixels(): support for format {:#x} and/or type {:#x} not implemented", format, type);
- return;
- }
-
- // FIXME: implement support for pixel parameters such as GL_UNPACK_ALIGNMENT
-
- if (format == GL_RGBA) {
- auto bitmap_or_error = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, { width, height });
- RETURN_WITH_ERROR_IF(bitmap_or_error.is_error(), GL_OUT_OF_MEMORY);
- auto bitmap = bitmap_or_error.release_value();
-
- auto pixel_data = static_cast<u32 const*>(data);
- for (int y = 0; y < height; ++y)
- for (int x = 0; x < width; ++x)
- bitmap->set_pixel(x, y, Color::from_argb(*(pixel_data++)));
-
- m_rasterizer->blit_to_color_buffer_at_raster_position(bitmap);
- } else if (format == GL_DEPTH_COMPONENT) {
- Vector<float> depth_values;
- depth_values.ensure_capacity(width * height);
-
- auto depth_data = static_cast<u16 const*>(data);
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- auto u16_value = *(depth_data++);
- auto float_value = static_cast<float>(u16_value) / NumericLimits<u16>::max();
- depth_values.append(float_value);
- }
- }
+ auto pixel_type_or_error = get_validated_pixel_type(GL_NONE, GL_NONE, format, type);
+ RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
+
+ auto pixel_type = pixel_type_or_error.release_value();
+ GPU::ImageDataLayout input_layout = {
+ .pixel_type = pixel_type,
+ .packing = get_packing_specification(PackingType::Unpack),
+ .dimensions = {
+ .width = static_cast<u32>(width),
+ .height = static_cast<u32>(height),
+ .depth = 1,
+ },
+ .selection = {
+ .width = static_cast<u32>(width),
+ .height = static_cast<u32>(height),
+ .depth = 1,
+ },
+ };
- m_rasterizer->blit_to_depth_buffer_at_raster_position(depth_values, width, height);
+ if (pixel_type.format == GPU::PixelFormat::DepthComponent) {
+ m_rasterizer->blit_to_depth_buffer_at_raster_position(data, input_layout);
+ } else if (pixel_type.format == GPU::PixelFormat::StencilIndex) {
+ dbgln("gl_draw_pixels(): GL_STENCIL_INDEX is not yet supported");
} else {
- VERIFY_NOT_REACHED();
+ m_rasterizer->blit_to_color_buffer_at_raster_position(data, input_layout);
}
}
diff --git a/Userland/Libraries/LibGL/GLContext.h b/Userland/Libraries/LibGL/GLContext.h
index 69bbc9aba5..e520d13703 100644
--- a/Userland/Libraries/LibGL/GLContext.h
+++ b/Userland/Libraries/LibGL/GLContext.h
@@ -83,6 +83,11 @@ enum Face {
Back = 1,
};
+enum class PackingType {
+ Pack,
+ Unpack,
+};
+
class GLContext final {
public:
GLContext(RefPtr<GPU::Driver> driver, NonnullOwnPtr<GPU::Device>, Gfx::Bitmap&);
@@ -235,6 +240,7 @@ private:
}
Optional<ContextParameter> get_context_parameter(GLenum pname);
+ GPU::PackingSpecification get_packing_specification(PackingType);
template<typename T>
void get_floating_point(GLenum pname, T* params);
diff --git a/Userland/Libraries/LibGL/Image.cpp b/Userland/Libraries/LibGL/Image.cpp
new file mode 100644
index 0000000000..aaff615eac
--- /dev/null
+++ b/Userland/Libraries/LibGL/Image.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibGL/Image.h>
+
+namespace GL {
+
+ErrorOr<GPU::PixelType> get_validated_pixel_type(GLenum target, GLenum internal_format, GLenum format, GLenum type)
+{
+ // We accept GL_NONE as target for non-texture related calls (such as `glDrawPixels`)
+ if (target != GL_NONE
+ && target != GL_TEXTURE_1D
+ && target != GL_TEXTURE_2D
+ && target != GL_TEXTURE_3D
+ && target != GL_TEXTURE_1D_ARRAY
+ && target != GL_TEXTURE_2D_ARRAY
+ && target != GL_TEXTURE_CUBE_MAP
+ && target != GL_PROXY_TEXTURE_1D
+ && target != GL_PROXY_TEXTURE_2D
+ && target != GL_PROXY_TEXTURE_3D)
+ return Error::from_errno(GL_INVALID_ENUM);
+
+ // Internal format can be a number between 1 and 4. Symbolic formats were only added with EXT_texture, promoted to core in OpenGL 1.1
+ if (internal_format == 1)
+ internal_format = GL_ALPHA;
+ else if (internal_format == 2)
+ internal_format = GL_LUMINANCE_ALPHA;
+ else if (internal_format == 3)
+ internal_format = GL_RGB;
+ else if (internal_format == 4)
+ internal_format = GL_RGBA;
+
+ if (internal_format != GL_NONE
+ && internal_format != GL_ALPHA
+ && internal_format != GL_ALPHA4
+ && internal_format != GL_ALPHA8
+ && internal_format != GL_ALPHA12
+ && internal_format != GL_ALPHA16
+ && internal_format != GL_COMPRESSED_ALPHA
+ && internal_format != GL_COMPRESSED_LUMINANCE
+ && internal_format != GL_COMPRESSED_LUMINANCE_ALPHA
+ && internal_format != GL_COMPRESSED_INTENSITY
+ && internal_format != GL_COMPRESSED_RGB
+ && internal_format != GL_COMPRESSED_RGBA
+ && internal_format != GL_DEPTH_COMPONENT
+ && internal_format != GL_DEPTH_COMPONENT16
+ && internal_format != GL_DEPTH_COMPONENT24
+ && internal_format != GL_DEPTH_COMPONENT32
+ && internal_format != GL_DEPTH_STENCIL
+ && internal_format != GL_LUMINANCE
+ && internal_format != GL_LUMINANCE4
+ && internal_format != GL_LUMINANCE8
+ && internal_format != GL_LUMINANCE12
+ && internal_format != GL_LUMINANCE16
+ && internal_format != GL_LUMINANCE_ALPHA
+ && internal_format != GL_LUMINANCE4_ALPHA4
+ && internal_format != GL_LUMINANCE6_ALPHA2
+ && internal_format != GL_LUMINANCE8_ALPHA8
+ && internal_format != GL_LUMINANCE12_ALPHA4
+ && internal_format != GL_LUMINANCE12_ALPHA12
+ && internal_format != GL_LUMINANCE16_ALPHA16
+ && internal_format != GL_INTENSITY
+ && internal_format != GL_INTENSITY4
+ && internal_format != GL_INTENSITY8
+ && internal_format != GL_INTENSITY12
+ && internal_format != GL_INTENSITY16
+ && internal_format != GL_R3_G3_B2
+ && internal_format != GL_RED
+ && internal_format != GL_RG
+ && internal_format != GL_RGB
+ && internal_format != GL_RGB4
+ && internal_format != GL_RGB5
+ && internal_format != GL_RGB8
+ && internal_format != GL_RGB10
+ && internal_format != GL_RGB12
+ && internal_format != GL_RGB16
+ && internal_format != GL_RGBA
+ && internal_format != GL_RGBA2
+ && internal_format != GL_RGBA4
+ && internal_format != GL_RGB5_A1
+ && internal_format != GL_RGBA8
+ && internal_format != GL_RGB10_A2
+ && internal_format != GL_RGBA12
+ && internal_format != GL_RGBA16
+ && internal_format != GL_SLUMINANCE
+ && internal_format != GL_SLUMINANCE8
+ && internal_format != GL_SLUMINANCE_ALPHA
+ && internal_format != GL_SLUMINANCE8_ALPHA8
+ && internal_format != GL_SRGB
+ && internal_format != GL_SRGB8
+ && internal_format != GL_SRGB_ALPHA
+ && internal_format != GL_SRGB8_ALPHA8)
+ return Error::from_errno(GL_INVALID_ENUM);
+
+ if ((format < GL_COLOR_INDEX || format > GL_LUMINANCE_ALPHA) && format != GL_BGR && format != GL_BGRA)
+ return Error::from_errno(GL_INVALID_ENUM);
+
+ if (type != GL_BITMAP
+ && (type < GL_BYTE || type > GL_FLOAT)
+ && type != GL_HALF_FLOAT
+ && (type < GL_UNSIGNED_BYTE_3_3_2 || type > GL_UNSIGNED_INT_10_10_10_2)
+ && (type < GL_UNSIGNED_BYTE_2_3_3_REV || type > GL_UNSIGNED_INT_2_10_10_10_REV))
+ return Error::from_errno(GL_INVALID_ENUM);
+
+ if (type == GL_BITMAP && format != GL_COLOR_INDEX && format != GL_STENCIL_INDEX)
+ return Error::from_errno(GL_INVALID_ENUM);
+
+ if (format != GL_RGB && (type == GL_UNSIGNED_BYTE_3_3_2 || type == GL_UNSIGNED_BYTE_2_3_3_REV || type == GL_UNSIGNED_SHORT_5_6_5 || type == GL_UNSIGNED_SHORT_5_6_5_REV))
+ return Error::from_errno(GL_INVALID_OPERATION);
+
+ if ((type == GL_UNSIGNED_SHORT_4_4_4_4
+ || type == GL_UNSIGNED_SHORT_4_4_4_4_REV
+ || type == GL_UNSIGNED_SHORT_5_5_5_1
+ || type == GL_UNSIGNED_SHORT_1_5_5_5_REV
+ || type == GL_UNSIGNED_INT_8_8_8_8
+ || type == GL_UNSIGNED_INT_8_8_8_8_REV
+ || type == GL_UNSIGNED_INT_10_10_10_2
+ || type == GL_UNSIGNED_INT_2_10_10_10_REV)
+ && format != GL_RGBA
+ && format != GL_BGRA)
+ return Error::from_errno(GL_INVALID_OPERATION);
+
+ if (internal_format != GL_NONE) {
+ auto const internal_format_is_depth = internal_format == GL_DEPTH_COMPONENT
+ || internal_format == GL_DEPTH_COMPONENT16
+ || internal_format == GL_DEPTH_COMPONENT24
+ || internal_format == GL_DEPTH_COMPONENT32;
+
+ if ((target != GL_TEXTURE_2D && target != GL_PROXY_TEXTURE_2D && internal_format_is_depth)
+ || (format == GL_DEPTH_COMPONENT && !internal_format_is_depth)
+ || (format != GL_DEPTH_COMPONENT && internal_format_is_depth))
+ return Error::from_errno(GL_INVALID_OPERATION);
+ }
+
+ return get_format_specification(format, type);
+}
+
+GPU::PixelType get_format_specification(GLenum format, GLenum type)
+{
+ auto get_format = [](GLenum format) -> GPU::PixelFormat {
+ switch (format) {
+ case GL_ALPHA:
+ return GPU::PixelFormat::Alpha;
+ case GL_BGR:
+ return GPU::PixelFormat::BGR;
+ case GL_BGRA:
+ return GPU::PixelFormat::BGRA;
+ case GL_BLUE:
+ return GPU::PixelFormat::Blue;
+ case GL_COLOR_INDEX:
+ return GPU::PixelFormat::ColorIndex;
+ case GL_DEPTH_COMPONENT:
+ return GPU::PixelFormat::DepthComponent;
+ case GL_GREEN:
+ return GPU::PixelFormat::Green;
+ case GL_LUMINANCE:
+ return GPU::PixelFormat::Luminance;
+ case GL_LUMINANCE_ALPHA:
+ return GPU::PixelFormat::LuminanceAlpha;
+ case GL_RED:
+ return GPU::PixelFormat::Red;
+ case GL_RGB:
+ return GPU::PixelFormat::RGB;
+ case GL_RGBA:
+ return GPU::PixelFormat::RGBA;
+ case GL_STENCIL_INDEX:
+ return GPU::PixelFormat::StencilIndex;
+ }
+ VERIFY_NOT_REACHED();
+ };
+ auto pixel_format = get_format(format);
+
+ switch (type) {
+ case GL_BITMAP:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::Bitmap, GPU::ComponentsOrder::Normal };
+ case GL_BYTE:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::Byte, GPU::ComponentsOrder::Normal };
+ case GL_FLOAT:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::Float, GPU::ComponentsOrder::Normal };
+ case GL_HALF_FLOAT:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::HalfFloat, GPU::ComponentsOrder::Normal };
+ case GL_INT:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::Int, GPU::ComponentsOrder::Normal };
+ case GL_SHORT:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::Short, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_BYTE:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::UnsignedByte, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_BYTE_2_3_3_REV:
+ return { pixel_format, GPU::PixelComponentBits::B2_3_3, GPU::PixelDataType::UnsignedByte, GPU::ComponentsOrder::Reversed };
+ case GL_UNSIGNED_BYTE_3_3_2:
+ return { pixel_format, GPU::PixelComponentBits::B3_3_2, GPU::PixelDataType::UnsignedByte, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_INT:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::UnsignedInt, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_INT_2_10_10_10_REV:
+ return { pixel_format, GPU::PixelComponentBits::B2_10_10_10, GPU::PixelDataType::UnsignedInt, GPU::ComponentsOrder::Reversed };
+ case GL_UNSIGNED_INT_8_8_8_8:
+ return { pixel_format, GPU::PixelComponentBits::B8_8_8_8, GPU::PixelDataType::UnsignedInt, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_INT_8_8_8_8_REV:
+ return { pixel_format, GPU::PixelComponentBits::B8_8_8_8, GPU::PixelDataType::UnsignedInt, GPU::ComponentsOrder::Reversed };
+ case GL_UNSIGNED_INT_10_10_10_2:
+ return { pixel_format, GPU::PixelComponentBits::B10_10_10_2, GPU::PixelDataType::UnsignedInt, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_SHORT:
+ return { pixel_format, GPU::PixelComponentBits::AllBits, GPU::PixelDataType::UnsignedShort, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_SHORT_1_5_5_5_REV:
+ return { pixel_format, GPU::PixelComponentBits::B1_5_5_5, GPU::PixelDataType::UnsignedShort, GPU::ComponentsOrder::Reversed };
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ return { pixel_format, GPU::PixelComponentBits::B4_4_4_4, GPU::PixelDataType::UnsignedShort, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_SHORT_4_4_4_4_REV:
+ return { pixel_format, GPU::PixelComponentBits::B4_4_4_4, GPU::PixelDataType::UnsignedShort, GPU::ComponentsOrder::Reversed };
+ case GL_UNSIGNED_SHORT_5_6_5:
+ return { pixel_format, GPU::PixelComponentBits::B5_6_5, GPU::PixelDataType::UnsignedShort, GPU::ComponentsOrder::Normal };
+ case GL_UNSIGNED_SHORT_5_6_5_REV:
+ return { pixel_format, GPU::PixelComponentBits::B5_6_5, GPU::PixelDataType::UnsignedShort, GPU::ComponentsOrder::Reversed };
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ return { pixel_format, GPU::PixelComponentBits::B5_5_5_1, GPU::PixelDataType::UnsignedShort, GPU::ComponentsOrder::Normal };
+ }
+ VERIFY_NOT_REACHED();
+}
+
+}
diff --git a/Userland/Libraries/LibGL/Image.h b/Userland/Libraries/LibGL/Image.h
new file mode 100644
index 0000000000..aed64d30c0
--- /dev/null
+++ b/Userland/Libraries/LibGL/Image.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Error.h>
+#include <LibGL/GL/gl.h>
+#include <LibGL/GLContext.h>
+#include <LibGPU/ImageDataLayout.h>
+#include <LibGPU/ImageFormat.h>
+
+namespace GL {
+
+GPU::PixelType get_format_specification(GLenum format, GLenum type);
+ErrorOr<GPU::PixelType> get_validated_pixel_type(GLenum target, GLenum internal_format, GLenum format, GLenum type);
+
+}
diff --git a/Userland/Libraries/LibGL/Tex/Texture2D.cpp b/Userland/Libraries/LibGL/Tex/Texture2D.cpp
index 061cbe9090..49e6b33bc2 100644
--- a/Userland/Libraries/LibGL/Tex/Texture2D.cpp
+++ b/Userland/Libraries/LibGL/Tex/Texture2D.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
* Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
+ * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -10,87 +11,33 @@
namespace GL {
-void Texture2D::upload_texture_data(GLuint lod, GLint internal_format, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid const* pixels, GLsizei pixels_per_row, u8 byte_alignment)
+void Texture2D::upload_texture_data(GLuint lod, GLenum internal_format, GPU::ImageDataLayout input_layout, GLvoid const* pixels)
{
// NOTE: Some target, format, and internal formats are currently unsupported.
// Considering we control this library, and `gl.h` itself, we don't need to add any
// checks here to see if we support them; the program will simply fail to compile..
auto& mip = m_mipmaps[lod];
- mip.set_width(width);
- mip.set_height(height);
-
m_internal_format = internal_format;
+ mip.set_width(input_layout.selection.width);
+ mip.set_height(input_layout.selection.height);
// No pixel data was supplied; leave the texture memory uninitialized.
if (pixels == nullptr)
return;
- replace_sub_texture_data(lod, 0, 0, width, height, format, type, pixels, pixels_per_row, byte_alignment);
+ replace_sub_texture_data(lod, input_layout, { 0, 0, 0 }, pixels);
}
-void Texture2D::replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid const* pixels, GLsizei pixels_per_row, u8 byte_alignment)
+void Texture2D::replace_sub_texture_data(GLuint lod, GPU::ImageDataLayout input_layout, Vector3<i32> const& output_offset, GLvoid const* pixels)
{
- auto& mip = m_mipmaps[lod];
-
- // FIXME: We currently only support GL_UNSIGNED_BYTE and GL_UNSIGNED_SHORT_5_6_5 pixel data
- VERIFY(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT_5_6_5);
- VERIFY(xoffset >= 0 && yoffset >= 0 && xoffset + width <= mip.width() && yoffset + height <= mip.height());
- VERIFY(pixels_per_row == 0 || pixels_per_row >= xoffset + width);
-
// FIXME: We currently depend on the first glTexImage2D call to attach an image to mipmap level 0, which initializes the GPU image
// Ideally we would create separate GPU images for each level and merge them into a final image
// once used for rendering for the first time.
if (device_image().is_null())
return;
- u8 pixel_size_bytes;
- switch (type) {
- case GL_UNSIGNED_BYTE:
- pixel_size_bytes = (format == GL_RGBA || format == GL_BGRA) ? 4 : 3;
- break;
- case GL_UNSIGNED_SHORT_5_6_5:
- pixel_size_bytes = sizeof(u16);
- break;
- default:
- VERIFY_NOT_REACHED();
- }
-
- // Calculate row offset at end to fit alignment
- int const physical_width = pixels_per_row > 0 ? pixels_per_row : width;
- size_t const physical_width_bytes = physical_width * pixel_size_bytes;
-
- GPU::ImageDataLayout layout;
- layout.column_stride = pixel_size_bytes;
- layout.row_stride = physical_width_bytes + (byte_alignment - physical_width_bytes % byte_alignment) % byte_alignment;
- layout.depth_stride = 0;
-
- if (type == GL_UNSIGNED_SHORT_5_6_5) {
- layout.format = GPU::ImageFormat::RGB565;
- } else if (type == GL_UNSIGNED_BYTE) {
- if (format == GL_RGB)
- layout.format = GPU::ImageFormat::RGB888;
- else if (format == GL_BGR)
- layout.format = GPU::ImageFormat::BGR888;
- else if (format == GL_RGBA)
- layout.format = GPU::ImageFormat::RGBA8888;
- else if (format == GL_BGRA)
- layout.format = GPU::ImageFormat::BGRA8888;
- }
-
- Vector3<unsigned> offset {
- static_cast<unsigned>(xoffset),
- static_cast<unsigned>(yoffset),
- 0
- };
-
- Vector3<unsigned> size {
- static_cast<unsigned>(width),
- static_cast<unsigned>(height),
- 1
- };
-
- device_image()->write_texels(0, lod, offset, size, pixels, layout);
+ device_image()->write_texels(0, lod, output_offset, pixels, input_layout);
}
}
diff --git a/Userland/Libraries/LibGL/Tex/Texture2D.h b/Userland/Libraries/LibGL/Tex/Texture2D.h
index e471e18d16..5ef8a3b740 100644
--- a/Userland/Libraries/LibGL/Tex/Texture2D.h
+++ b/Userland/Libraries/LibGL/Tex/Texture2D.h
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
* Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
+ * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -10,13 +11,11 @@
#include "Texture.h"
#include <AK/Array.h>
-#include <AK/RefCounted.h>
-#include <AK/Vector.h>
#include <LibGL/GL/gl.h>
#include <LibGL/Tex/MipMap.h>
#include <LibGL/Tex/Sampler2D.h>
-#include <LibGfx/Vector2.h>
-#include <LibGfx/Vector4.h>
+#include <LibGPU/ImageDataLayout.h>
+#include <LibGfx/Vector3.h>
namespace GL {
@@ -28,8 +27,8 @@ public:
virtual bool is_texture_2d() const override { return true; }
- void upload_texture_data(GLuint lod, GLint internal_format, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid const* pixels, GLsizei pixels_per_row, u8 byte_alignment);
- void replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid const* pixels, GLsizei pixels_per_row, u8 byte_alignment);
+ void upload_texture_data(GLuint lod, GLenum internal_format, GPU::ImageDataLayout input_layout, GLvoid const* pixels);
+ void replace_sub_texture_data(GLuint lod, GPU::ImageDataLayout input_layout, Vector3<i32> const& output_offset, GLvoid const* pixels);
MipMap const& mipmap(unsigned lod) const
{
diff --git a/Userland/Libraries/LibGL/Texture.cpp b/Userland/Libraries/LibGL/Texture.cpp
index 019ba87d35..4e80a7bff6 100644
--- a/Userland/Libraries/LibGL/Texture.cpp
+++ b/Userland/Libraries/LibGL/Texture.cpp
@@ -8,6 +8,8 @@
#include <AK/Debug.h>
#include <LibGL/GLContext.h>
+#include <LibGL/Image.h>
+#include <LibGPU/ImageDataLayout.h>
namespace GL {
@@ -306,26 +308,20 @@ void GLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* par
m_texcoord_generation_dirty = true;
}
+// FIXME: talk to GPU::Device to determine supported GPU::PixelTypes
+constexpr GPU::PixelType texture_fixed_pixel_type = {
+ .format = GPU::PixelFormat::RGBA,
+ .bits = GPU::PixelComponentBits::AllBits,
+ .data_type = GPU::PixelDataType::Float,
+};
+
void GLContext::gl_tex_image_2d(GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid const* data)
{
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
- // We only support GL_TEXTURE_2D for now
- RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
-
- // Internal format can also be a number between 1 and 4. Symbolic formats were only added with EXT_texture, promoted to core in OpenGL 1.1
- if (internal_format == 1)
- internal_format = GL_ALPHA;
- else if (internal_format == 2)
- internal_format = GL_LUMINANCE_ALPHA;
- else if (internal_format == 3)
- internal_format = GL_RGB;
- else if (internal_format == 4)
- internal_format = GL_RGBA;
+ auto pixel_type_or_error = get_validated_pixel_type(target, internal_format, format, type);
+ RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
- // We only support symbolic constants for now
- RETURN_WITH_ERROR_IF(!(internal_format == GL_RGB || internal_format == GL_RGBA || internal_format == GL_LUMINANCE8 || internal_format == GL_LUMINANCE8_ALPHA8), GL_INVALID_ENUM);
- RETURN_WITH_ERROR_IF(!(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT_5_6_5), GL_INVALID_VALUE);
RETURN_WITH_ERROR_IF(level < 0 || level > Texture2D::LOG2_MAX_TEXTURE_SIZE, GL_INVALID_VALUE);
RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + Texture2D::MAX_TEXTURE_SIZE) || height > (2 + Texture2D::MAX_TEXTURE_SIZE), GL_INVALID_VALUE);
// Check if width and height are a power of 2
@@ -345,11 +341,26 @@ void GLContext::gl_tex_image_2d(GLenum target, GLint level, GLint internal_forma
// that constructing GL textures in any but the default mipmap order, going from level 0 upwards will cause mip levels to stay uninitialized.
// To be spec compliant we should create the device image once the texture has become complete and is used for rendering the first time.
// All images that were attached before the device image was created need to be stored somewhere to be used to initialize the device image once complete.
- texture_2d->set_device_image(m_rasterizer->create_image(GPU::ImageFormat::BGRA8888, width, height, 1, 999, 1));
+ texture_2d->set_device_image(m_rasterizer->create_image(texture_fixed_pixel_type, width, height, 1, 999, 1));
m_sampler_config_is_dirty = true;
}
- texture_2d->upload_texture_data(level, internal_format, width, height, format, type, data, m_unpack_row_length, m_unpack_alignment);
+ GPU::ImageDataLayout input_layout = {
+ .pixel_type = pixel_type_or_error.release_value(),
+ .packing = get_packing_specification(PackingType::Unpack),
+ .dimensions = {
+ .width = static_cast<u32>(width),
+ .height = static_cast<u32>(height),
+ .depth = 1,
+ },
+ .selection = {
+ .width = static_cast<u32>(width),
+ .height = static_cast<u32>(height),
+ .depth = 1,
+ },
+ };
+
+ texture_2d->upload_texture_data(level, internal_format, input_layout, data);
}
void GLContext::gl_tex_parameter(GLenum target, GLenum pname, GLfloat param)
@@ -454,12 +465,7 @@ void GLContext::gl_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, G
{
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
- // We only support GL_TEXTURE_2D for now
- RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
-
// We only support symbolic constants for now
- RETURN_WITH_ERROR_IF(!(format == GL_RGBA || format == GL_RGB), GL_INVALID_VALUE);
- RETURN_WITH_ERROR_IF(!(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT_5_6_5), GL_INVALID_VALUE);
RETURN_WITH_ERROR_IF(level < 0 || level > Texture2D::LOG2_MAX_TEXTURE_SIZE, GL_INVALID_VALUE);
RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + Texture2D::MAX_TEXTURE_SIZE) || height > (2 + Texture2D::MAX_TEXTURE_SIZE), GL_INVALID_VALUE);
@@ -467,9 +473,27 @@ void GLContext::gl_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, G
auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
RETURN_WITH_ERROR_IF(texture_2d.is_null(), GL_INVALID_OPERATION);
+ auto pixel_type_or_error = get_validated_pixel_type(target, texture_2d->internal_format(), format, type);
+ RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
+
RETURN_WITH_ERROR_IF(xoffset < 0 || yoffset < 0 || xoffset + width > texture_2d->width_at_lod(level) || yoffset + height > texture_2d->height_at_lod(level), GL_INVALID_VALUE);
- texture_2d->replace_sub_texture_data(level, xoffset, yoffset, width, height, format, type, data, m_unpack_row_length, m_unpack_alignment);
+ GPU::ImageDataLayout input_layout = {
+ .pixel_type = pixel_type_or_error.release_value(),
+ .packing = get_packing_specification(PackingType::Unpack),
+ .dimensions = {
+ .width = static_cast<u32>(width),
+ .height = static_cast<u32>(height),
+ .depth = 1,
+ },
+ .selection = {
+ .width = static_cast<u32>(width),
+ .height = static_cast<u32>(height),
+ .depth = 1,
+ },
+ };
+
+ texture_2d->replace_sub_texture_data(level, input_layout, { xoffset, yoffset, 0 }, data);
}
void GLContext::sync_device_sampler_config()