From 5e370e6f96864325d75286dcafaf85f893f7b3d4 Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Sun, 28 Nov 2021 01:48:21 +0100 Subject: [PATCH] LibGL: Implement `GL_(UN)PACK_ALIGNMENT` These enums are used to indicate byte-alignment when reading from and to textures. The `GL_UNPACK_ROW_LENGTH` value was reimplemented to support overriding the source data row width. --- Userland/Libraries/LibGL/GL/gl.h | 4 +- Userland/Libraries/LibGL/GLContext.h | 2 +- Userland/Libraries/LibGL/GLUtils.cpp | 2 +- .../Libraries/LibGL/SoftwareGLContext.cpp | 79 ++++++++++++------- Userland/Libraries/LibGL/SoftwareGLContext.h | 6 +- Userland/Libraries/LibGL/Tex/Texture2D.cpp | 38 +++++---- Userland/Libraries/LibGL/Tex/Texture2D.h | 4 +- 7 files changed, 81 insertions(+), 54 deletions(-) diff --git a/Userland/Libraries/LibGL/GL/gl.h b/Userland/Libraries/LibGL/GL/gl.h index bf942633490..61aed595651 100644 --- a/Userland/Libraries/LibGL/GL/gl.h +++ b/Userland/Libraries/LibGL/GL/gl.h @@ -129,8 +129,10 @@ extern "C" { #define GL_GENERATE_MIPMAP_HINT 0x8192 #define GL_TEXTURE_COMPRESSION_HINT 0x84EF -// Read pixels +// Reading pixels & unpacking texture patterns #define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 // Listing enums #define GL_COMPILE 0x1300 diff --git a/Userland/Libraries/LibGL/GLContext.h b/Userland/Libraries/LibGL/GLContext.h index b33c77cdbb6..d085c737edf 100644 --- a/Userland/Libraries/LibGL/GLContext.h +++ b/Userland/Libraries/LibGL/GLContext.h @@ -85,7 +85,7 @@ public: virtual void gl_fogfv(GLenum pname, GLfloat* params) = 0; virtual void gl_fogf(GLenum pname, GLfloat params) = 0; virtual void gl_fogi(GLenum pname, GLint param) = 0; - virtual void gl_pixel_store(GLenum pname, GLfloat param) = 0; + virtual void gl_pixel_storei(GLenum pname, GLint param) = 0; virtual void gl_scissor(GLint x, GLint y, GLsizei width, GLsizei height) = 0; virtual void present() = 0; diff --git a/Userland/Libraries/LibGL/GLUtils.cpp b/Userland/Libraries/LibGL/GLUtils.cpp index 19556c9b5cd..5afd172f5b9 100644 --- a/Userland/Libraries/LibGL/GLUtils.cpp +++ b/Userland/Libraries/LibGL/GLUtils.cpp @@ -152,7 +152,7 @@ void glPolygonOffset(GLfloat factor, GLfloat units) void glPixelStorei(GLenum pname, GLint param) { - g_gl_context->gl_pixel_store(pname, param); + g_gl_context->gl_pixel_storei(pname, param); } void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.cpp b/Userland/Libraries/LibGL/SoftwareGLContext.cpp index d042e200580..db303f84570 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.cpp +++ b/Userland/Libraries/LibGL/SoftwareGLContext.cpp @@ -722,7 +722,7 @@ void SoftwareGLContext::gl_tex_image_2d(GLenum target, GLint level, GLint intern RETURN_WITH_ERROR_IF((height & (height - 1)) != 0, GL_INVALID_VALUE); RETURN_WITH_ERROR_IF(border < 0 || border > 1, GL_INVALID_VALUE); - m_active_texture_unit->bound_texture_2d()->upload_texture_data(level, internal_format, width, height, border, format, type, data, m_unpack_row_length); + m_active_texture_unit->bound_texture_2d()->upload_texture_data(level, internal_format, width, height, border, format, type, data, m_unpack_row_length, m_unpack_alignment); } void SoftwareGLContext::gl_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* data) @@ -744,7 +744,7 @@ void SoftwareGLContext::gl_tex_sub_image_2d(GLenum target, GLint level, GLint xo RETURN_WITH_ERROR_IF(xoffset < 0 || yoffset < 0 || xoffset + width > texture->width_at_lod(level) || yoffset + height > texture->height_at_lod(level), GL_INVALID_VALUE); - texture->replace_sub_texture_data(level, xoffset, yoffset, width, height, format, type, data, m_unpack_row_length); + texture->replace_sub_texture_data(level, xoffset, yoffset, width, height, format, type, data, m_unpack_row_length, m_unpack_alignment); } void SoftwareGLContext::gl_tex_parameter(GLenum target, GLenum pname, GLfloat param) @@ -1167,33 +1167,53 @@ void SoftwareGLContext::gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei return static_cast(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(pixels) + i * row_stride + j * component_size; switch (type) { case GL_BYTE: - reinterpret_cast(pixels)[i * width + j] = float_to_i8(depth); + *reinterpret_cast(char_ptr) = float_to_i8(depth); break; case GL_SHORT: - reinterpret_cast(pixels)[i * width + j] = float_to_i16(depth); + *reinterpret_cast(char_ptr) = float_to_i16(depth); break; case GL_INT: - reinterpret_cast(pixels)[i * width + j] = float_to_i32(depth); + *reinterpret_cast(char_ptr) = float_to_i32(depth); break; case GL_UNSIGNED_BYTE: - reinterpret_cast(pixels)[i * width + j] = float_to_u8(depth); + *reinterpret_cast(char_ptr) = float_to_u8(depth); break; case GL_UNSIGNED_SHORT: - reinterpret_cast(pixels)[i * width + j] = float_to_u16(depth); + *reinterpret_cast(char_ptr) = float_to_u16(depth); break; case GL_UNSIGNED_INT: - reinterpret_cast(pixels)[i * width + j] = float_to_u32(depth); + *reinterpret_cast(char_ptr) = float_to_u32(depth); break; case GL_FLOAT: - reinterpret_cast(pixels)[i * width + j] = min(max(depth, 0.0f), 1.0f); + *reinterpret_cast(char_ptr) = min(max(depth, 0.0f), 1.0f); break; } } @@ -1206,7 +1226,6 @@ void SoftwareGLContext::gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei bool write_blue = false; bool write_alpha = false; size_t component_count = 0; - size_t component_size = 0; size_t red_offset = 0; size_t green_offset = 0; size_t blue_offset = 0; @@ -1259,21 +1278,8 @@ void SoftwareGLContext::gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei break; } - 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; - } + 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(pixels); for (int i = 0; i < (int)height; ++i) { @@ -1372,8 +1378,10 @@ void SoftwareGLContext::gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei break; } - out_ptr += component_size * component_count; + out_ptr += pixel_bytes; } + + out_ptr += row_alignment_bytes; } } @@ -1518,6 +1526,9 @@ void SoftwareGLContext::gl_get_integerv(GLenum pname, GLint* data) case GL_MAX_TEXTURE_SIZE: *data = 4096; break; + case GL_PACK_ALIGNMENT: + *data = m_pack_alignment; + break; case GL_SCISSOR_BOX: { auto scissor_box = m_rasterizer.options().scissor_box; *(data + 0) = scissor_box.x(); @@ -1526,6 +1537,12 @@ void SoftwareGLContext::gl_get_integerv(GLenum pname, GLint* data) *(data + 3) = scissor_box.height(); break; } + case GL_UNPACK_ALIGNMENT: + *data = m_unpack_alignment; + break; + case GL_UNPACK_ROW_LENGTH: + *data = m_unpack_row_length; + break; default: // According to the Khronos docs, we always return GL_INVALID_ENUM if we encounter a non-accepted value // for `pname` @@ -2006,14 +2023,22 @@ void SoftwareGLContext::gl_fogi(GLenum pname, GLint param) m_rasterizer.set_options(options); } -void SoftwareGLContext::gl_pixel_store(GLenum pname, GLfloat param) +void SoftwareGLContext::gl_pixel_storei(GLenum pname, GLint param) { // FIXME: Implement missing parameters switch (pname) { + case GL_PACK_ALIGNMENT: + RETURN_WITH_ERROR_IF(param != 1 && param != 2 && param != 4 && param != 8, GL_INVALID_VALUE); + m_pack_alignment = param; + break; case GL_UNPACK_ROW_LENGTH: RETURN_WITH_ERROR_IF(param < 0, GL_INVALID_VALUE); m_unpack_row_length = static_cast(param); break; + case GL_UNPACK_ALIGNMENT: + RETURN_WITH_ERROR_IF(param != 1 && param != 2 && param != 4 && param != 8, GL_INVALID_VALUE); + m_unpack_alignment = param; + break; default: RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); break; diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.h b/Userland/Libraries/LibGL/SoftwareGLContext.h index 18f1243538f..2faf5bd7b0b 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.h +++ b/Userland/Libraries/LibGL/SoftwareGLContext.h @@ -96,7 +96,7 @@ public: virtual void gl_fogfv(GLenum pname, GLfloat* params) override; virtual void gl_fogf(GLenum pname, GLfloat param) override; virtual void gl_fogi(GLenum pname, GLint param) override; - virtual void gl_pixel_store(GLenum pname, GLfloat param) override; + virtual void gl_pixel_storei(GLenum pname, GLint param) override; virtual void gl_scissor(GLint x, GLint y, GLsizei width, GLsizei height) override; virtual void present() override; @@ -265,7 +265,9 @@ private: VertexAttribPointer m_client_color_pointer; VertexAttribPointer m_client_tex_coord_pointer; - size_t m_unpack_row_length { 0 }; + u8 m_pack_alignment { 4 }; + GLsizei m_unpack_row_length { 0 }; + u8 m_unpack_alignment { 4 }; }; } diff --git a/Userland/Libraries/LibGL/Tex/Texture2D.cpp b/Userland/Libraries/LibGL/Tex/Texture2D.cpp index 86e58e27848..9300b7a98e9 100644 --- a/Userland/Libraries/LibGL/Tex/Texture2D.cpp +++ b/Userland/Libraries/LibGL/Tex/Texture2D.cpp @@ -5,14 +5,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include -#include namespace GL { -void Texture2D::upload_texture_data(GLuint lod, GLint internal_format, GLsizei width, GLsizei height, GLint, GLenum format, GLenum type, const GLvoid* pixels, size_t pixels_per_row) +void Texture2D::upload_texture_data(GLuint lod, GLint internal_format, GLsizei width, GLsizei height, GLint, GLenum format, GLenum type, const GLvoid* pixels, GLsizei pixels_per_row, u8 byte_alignment) { // 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 @@ -24,16 +22,15 @@ void Texture2D::upload_texture_data(GLuint lod, GLint internal_format, GLsizei w mip.pixel_data().resize(width * height); // No pixel data was supplied leave the texture memory uninitialized. - if (pixels == nullptr) { + if (pixels == nullptr) return; - } m_internal_format = internal_format; - replace_sub_texture_data(lod, 0, 0, width, height, format, type, pixels, pixels_per_row); + replace_sub_texture_data(lod, 0, 0, width, height, format, type, pixels, pixels_per_row, byte_alignment); } -void Texture2D::replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels, size_t pixels_per_row) +void Texture2D::replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels, GLsizei pixels_per_row, u8 byte_alignment) { auto& mip = m_mipmaps[lod]; @@ -41,8 +38,17 @@ void Texture2D::replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffse VERIFY(type == GL_UNSIGNED_BYTE); VERIFY(lod < m_mipmaps.size()); VERIFY(xoffset >= 0 && yoffset >= 0 && xoffset + width <= mip.width() && yoffset + height <= mip.height()); + VERIFY(pixels_per_row == 0 || pixels_per_row >= xoffset + width); - const u8* pixel_byte_array = reinterpret_cast(pixels); + // Calculate row offset at end to fit alignment + int const physical_width = pixels_per_row > 0 ? pixels_per_row : width; + u8 const component_size_bytes = sizeof(u8); + u8 const component_count = (format == GL_RGBA || format == GL_BGRA) ? 4 : 3; + size_t const physical_width_bytes = physical_width * component_count * component_size_bytes; + size_t const row_remainder_bytes = (physical_width - width) * component_count * component_size_bytes + + (byte_alignment - physical_width_bytes % byte_alignment) % byte_alignment; + + u8 const* pixel_byte_array = reinterpret_cast(pixels); if (format == GL_RGBA) { for (auto y = yoffset; y < yoffset + height; y++) { @@ -56,9 +62,7 @@ void Texture2D::replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffse mip.pixel_data()[y * mip.width() + x] = pixel; } - if (pixels_per_row > 0) { - pixel_byte_array += (pixels_per_row - width) * 4; - } + pixel_byte_array += row_remainder_bytes; } } else if (format == GL_BGRA) { for (auto y = yoffset; y < yoffset + height; y++) { @@ -72,9 +76,7 @@ void Texture2D::replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffse mip.pixel_data()[y * mip.width() + x] = pixel; } - if (pixels_per_row > 0) { - pixel_byte_array += (pixels_per_row - width) * 4; - } + pixel_byte_array += row_remainder_bytes; } } else if (format == GL_BGR) { for (auto y = yoffset; y < yoffset + height; y++) { @@ -88,9 +90,7 @@ void Texture2D::replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffse mip.pixel_data()[y * mip.width() + x] = pixel; } - if (pixels_per_row > 0) { - pixel_byte_array += (pixels_per_row - width) * 3; - } + pixel_byte_array += row_remainder_bytes; } } else if (format == GL_RGB) { for (auto y = yoffset; y < yoffset + height; y++) { @@ -104,9 +104,7 @@ void Texture2D::replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffse mip.pixel_data()[y * mip.width() + x] = pixel; } - if (pixels_per_row > 0) { - pixel_byte_array += (pixels_per_row - width) * 3; - } + pixel_byte_array += row_remainder_bytes; } } else { VERIFY_NOT_REACHED(); diff --git a/Userland/Libraries/LibGL/Tex/Texture2D.h b/Userland/Libraries/LibGL/Tex/Texture2D.h index 2c79fb6e3b7..618c8c7086a 100644 --- a/Userland/Libraries/LibGL/Tex/Texture2D.h +++ b/Userland/Libraries/LibGL/Tex/Texture2D.h @@ -35,8 +35,8 @@ public: virtual bool is_texture_2d() const override { return true; } - void upload_texture_data(GLuint lod, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels, size_t pixels_per_row); - void replace_sub_texture_data(GLuint lod, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels, size_t pixels_per_row); + void upload_texture_data(GLuint lod, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* 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, const GLvoid* pixels, GLsizei pixels_per_row, u8 byte_alignment); MipMap const& mipmap(unsigned lod) const;