From e7d34836180cc48a0fc7c1f5b1c5d1e039b4eacc Mon Sep 17 00:00:00 2001 From: Stephan Unverwerth Date: Tue, 31 Aug 2021 22:35:52 +0200 Subject: [PATCH] LibGL: Implement glTexSubImage2D --- Userland/Libraries/LibGL/GL/gl.h | 1 + Userland/Libraries/LibGL/GLContext.h | 1 + Userland/Libraries/LibGL/GLTexture.cpp | 5 +++ .../Libraries/LibGL/SoftwareGLContext.cpp | 24 ++++++++++- Userland/Libraries/LibGL/SoftwareGLContext.h | 1 + Userland/Libraries/LibGL/Tex/Texture2D.cpp | 43 ++++++++++++------- Userland/Libraries/LibGL/Tex/Texture2D.h | 7 ++- 7 files changed, 63 insertions(+), 19 deletions(-) diff --git a/Userland/Libraries/LibGL/GL/gl.h b/Userland/Libraries/LibGL/GL/gl.h index 8e10bd0653..07e86be54e 100644 --- a/Userland/Libraries/LibGL/GL/gl.h +++ b/Userland/Libraries/LibGL/GL/gl.h @@ -389,6 +389,7 @@ GLAPI void glReadBuffer(GLenum mode); GLAPI void glDrawBuffer(GLenum buffer); GLAPI void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels); GLAPI void glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* data); +GLAPI void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* data); GLAPI void glTexCoord2f(GLfloat s, GLfloat t); GLAPI void glTexCoord4fv(const GLfloat* v); GLAPI void glTexParameteri(GLenum target, GLenum pname, GLint param); diff --git a/Userland/Libraries/LibGL/GLContext.h b/Userland/Libraries/LibGL/GLContext.h index a6429fd484..4063be2b17 100644 --- a/Userland/Libraries/LibGL/GLContext.h +++ b/Userland/Libraries/LibGL/GLContext.h @@ -59,6 +59,7 @@ public: virtual void gl_draw_buffer(GLenum buffer) = 0; virtual void gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) = 0; virtual void gl_tex_image_2d(GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* data) = 0; + virtual void gl_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* data) = 0; virtual void gl_tex_parameter(GLenum target, GLenum pname, GLfloat param) = 0; virtual void gl_tex_coord(GLfloat s, GLfloat t, GLfloat r, GLfloat q) = 0; virtual void gl_tex_env(GLenum target, GLenum pname, GLfloat param) = 0; diff --git a/Userland/Libraries/LibGL/GLTexture.cpp b/Userland/Libraries/LibGL/GLTexture.cpp index 03e86d9d6b..5fdc1e3d9c 100644 --- a/Userland/Libraries/LibGL/GLTexture.cpp +++ b/Userland/Libraries/LibGL/GLTexture.cpp @@ -25,6 +25,11 @@ void glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei widt g_gl_context->gl_tex_image_2d(target, level, internalFormat, width, height, border, format, type, data); } +void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* data) +{ + g_gl_context->gl_tex_sub_image_2d(target, level, xoffset, yoffset, width, height, format, type, data); +} + void glBindTexture(GLenum target, GLuint texture) { g_gl_context->gl_bind_texture(target, texture); diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.cpp b/Userland/Libraries/LibGL/SoftwareGLContext.cpp index 11f8d66c6e..21397ca737 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.cpp +++ b/Userland/Libraries/LibGL/SoftwareGLContext.cpp @@ -662,7 +662,29 @@ 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(target, 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); +} + +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) +{ + 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); + + // Check if there is actually a texture bound + RETURN_WITH_ERROR_IF(target == GL_TEXTURE_2D && m_active_texture_unit->currently_bound_target() != GL_TEXTURE_2D, GL_INVALID_OPERATION); + + // We only support symbolic constants for now + RETURN_WITH_ERROR_IF(type != GL_UNSIGNED_BYTE, 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); + + auto texture = m_active_texture_unit->bound_texture_2d(); + + 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); } void SoftwareGLContext::gl_tex_parameter(GLenum target, GLenum pname, GLfloat param) diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.h b/Userland/Libraries/LibGL/SoftwareGLContext.h index 3b7f466b6f..2d15b5f89f 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.h +++ b/Userland/Libraries/LibGL/SoftwareGLContext.h @@ -69,6 +69,7 @@ public: virtual void gl_draw_buffer(GLenum buffer) override; virtual void gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) override; virtual void gl_tex_image_2d(GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* data) override; + virtual void gl_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* data) override; virtual void gl_tex_parameter(GLenum target, GLenum pname, GLfloat param) override; virtual void gl_tex_coord(GLfloat s, GLfloat t, GLfloat r, GLfloat q) override; virtual void gl_tex_env(GLenum target, GLenum pname, GLfloat param) override; diff --git a/Userland/Libraries/LibGL/Tex/Texture2D.cpp b/Userland/Libraries/LibGL/Tex/Texture2D.cpp index f401f61186..6d9be66d29 100644 --- a/Userland/Libraries/LibGL/Tex/Texture2D.cpp +++ b/Userland/Libraries/LibGL/Tex/Texture2D.cpp @@ -12,7 +12,7 @@ namespace GL { -void Texture2D::upload_texture_data(GLenum, GLint lod, GLint internal_format, GLsizei width, GLsizei height, GLint, GLenum format, GLenum, 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, size_t pixels_per_row) { // 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 @@ -21,28 +21,39 @@ void Texture2D::upload_texture_data(GLenum, GLint lod, GLint internal_format, GL auto& mip = m_mipmaps[lod]; mip.set_width(width); mip.set_height(height); + mip.pixel_data().resize(width * height); - // No pixel data was supplied. Just allocate texture memory and leave it uninitialized. + // No pixel data was supplied leave the texture memory uninitialized. if (pixels == nullptr) { - mip.pixel_data().resize(width * height); return; } m_internal_format = internal_format; + replace_sub_texture_data(lod, 0, 0, width, height, format, type, pixels, 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, size_t pixels_per_row) +{ + auto& mip = m_mipmaps[lod]; + + // FIXME: We currently only support GL_UNSIGNED_BYTE pixel data + VERIFY(type == GL_UNSIGNED_BYTE); + VERIFY(lod < m_mipmaps.size()); + VERIFY(xoffset >= 0 && yoffset >= 0 && xoffset + width <= mip.width() && yoffset + height <= mip.height()); + const u8* pixel_byte_array = reinterpret_cast(pixels); - mip.pixel_data().clear(); if (format == GL_RGBA) { - for (auto y = 0; y < height; y++) { - for (auto x = 0; x < width; x++) { + for (auto y = yoffset; y < yoffset + height; y++) { + for (auto x = xoffset; x < xoffset + width; x++) { u32 r = *pixel_byte_array++; u32 g = *pixel_byte_array++; u32 b = *pixel_byte_array++; u32 a = *pixel_byte_array++; u32 pixel = ((a << 24) | (r << 16) | (g << 8) | b); - mip.pixel_data().append(pixel); + mip.pixel_data()[y * mip.width() + x] = pixel; } if (pixels_per_row > 0) { @@ -50,15 +61,15 @@ void Texture2D::upload_texture_data(GLenum, GLint lod, GLint internal_format, GL } } } else if (format == GL_BGRA) { - for (auto y = 0; y < height; y++) { - for (auto x = 0; x < width; x++) { + for (auto y = yoffset; y < yoffset + height; y++) { + for (auto x = xoffset; x < xoffset + width; x++) { u32 b = *pixel_byte_array++; u32 g = *pixel_byte_array++; u32 r = *pixel_byte_array++; u32 a = *pixel_byte_array++; u32 pixel = ((a << 24) | (r << 16) | (g << 8) | b); - mip.pixel_data().append(pixel); + mip.pixel_data()[y * mip.width() + x] = pixel; } if (pixels_per_row > 0) { @@ -66,15 +77,15 @@ void Texture2D::upload_texture_data(GLenum, GLint lod, GLint internal_format, GL } } } else if (format == GL_BGR) { - for (auto y = 0; y < height; y++) { - for (auto x = 0; x < width; x++) { + for (auto y = yoffset; y < yoffset + height; y++) { + for (auto x = xoffset; x < xoffset + width; x++) { u32 b = *pixel_byte_array++; u32 g = *pixel_byte_array++; u32 r = *pixel_byte_array++; u32 a = 255; u32 pixel = ((a << 24) | (r << 16) | (g << 8) | b); - mip.pixel_data().append(pixel); + mip.pixel_data()[y * mip.width() + x] = pixel; } if (pixels_per_row > 0) { @@ -82,15 +93,15 @@ void Texture2D::upload_texture_data(GLenum, GLint lod, GLint internal_format, GL } } } else if (format == GL_RGB) { - for (auto y = 0; y < height; y++) { - for (auto x = 0; x < width; x++) { + for (auto y = yoffset; y < yoffset + height; y++) { + for (auto x = xoffset; x < xoffset + width; x++) { u32 r = *pixel_byte_array++; u32 g = *pixel_byte_array++; u32 b = *pixel_byte_array++; u32 a = 255; u32 pixel = ((a << 24) | (r << 16) | (g << 8) | b); - mip.pixel_data().append(pixel); + mip.pixel_data()[y * mip.width() + x] = pixel; } if (pixels_per_row > 0) { diff --git a/Userland/Libraries/LibGL/Tex/Texture2D.h b/Userland/Libraries/LibGL/Tex/Texture2D.h index b94acade8c..2c79fb6e3b 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(GLenum target, GLint 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(GLint lod, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* data); + 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); MipMap const& mipmap(unsigned lod) const; @@ -44,6 +44,9 @@ public: Sampler2D const& sampler() const { return m_sampler; } Sampler2D& sampler() { return m_sampler; } + int width_at_lod(unsigned level) const { return (level >= m_mipmaps.size()) ? 0 : max(1, m_mipmaps.at(level).width() >> level); } + int height_at_lod(unsigned level) const { return (level >= m_mipmaps.size()) ? 0 : max(1, m_mipmaps.at(level).height() >> level); } + private: template void swizzle(Vector& pixels, TCallback&& callback)