From 5065405e71ffb2990bf8c3add61aaa926eb8314f Mon Sep 17 00:00:00 2001 From: GPF Date: Fri, 5 Jun 2026 20:49:35 -0700 Subject: [PATCH] Optimize streaming texture updates --- GL/private.h | 2 ++ GL/state.c | 40 ++++++++++++++++++++++++++++++++++-- GL/texture.c | 57 +++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/GL/private.h b/GL/private.h index c43d6e3..8d24f82 100644 --- a/GL/private.h +++ b/GL/private.h @@ -379,6 +379,8 @@ GLenum _glGetCullFace(); GLenum _glGetFrontFace(); GLenum _glGetGpuBlendSrcFactor(); GLenum _glGetGpuBlendDstFactor(); +GLint _glGetUnpackRowLength(); +GLint _glGetUnpackAlignment(); extern PolyList OP_LIST; extern PolyList PT_LIST; diff --git a/GL/state.c b/GL/state.c index 6d50c2f..5c2860a 100644 --- a/GL/state.c +++ b/GL/state.c @@ -66,6 +66,9 @@ static struct { GLfloat half_point_size; GLfloat half_line_width; + + GLint unpack_row_length; + GLint unpack_alignment; } GPUState = { .is_dirty = GL_TRUE, .depth_func = GL_LESS, @@ -102,6 +105,8 @@ static struct { .current_tex_coord1 = {0.0f, 0.0f}, .half_point_size = 0.5f, .half_line_width = 0.5f, + .unpack_row_length = 0, + .unpack_alignment = 4, }; float* _glCurrentColor() { @@ -141,6 +146,14 @@ GLboolean _glGPUStateIsDirty() { return GPUState.is_dirty; } +GLint _glGetUnpackRowLength() { + return GPUState.unpack_row_length; +} + +GLint _glGetUnpackAlignment() { + return GPUState.unpack_alignment; +} + Material* _glActiveMaterial() { return &GPUState.material; } @@ -873,8 +886,25 @@ void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha } void glPixelStorei(GLenum pname, GLint param) { - _GL_UNUSED(pname); - _GL_UNUSED(param); + switch(pname) { + case GL_UNPACK_ROW_LENGTH: + if(param < 0) { + _glKosThrowError(GL_INVALID_VALUE, __func__); + return; + } + GPUState.unpack_row_length = param; + break; + case GL_UNPACK_ALIGNMENT: + if(param != 1 && param != 2 && param != 4 && param != 8) { + _glKosThrowError(GL_INVALID_VALUE, __func__); + return; + } + GPUState.unpack_alignment = param; + break; + default: + _glKosThrowError(GL_INVALID_ENUM, __func__); + break; + } } @@ -1118,6 +1148,12 @@ void APIENTRY glGetIntegerv(GLenum pname, GLint *params) { case GL_TEXTURE_INTERNAL_FORMAT_KOS: *params = _glGetTextureInternalFormat(); break; + case GL_UNPACK_ROW_LENGTH: + *params = GPUState.unpack_row_length; + break; + case GL_UNPACK_ALIGNMENT: + *params = GPUState.unpack_alignment; + break; default: _glKosThrowError(GL_INVALID_ENUM, __func__); diff --git a/GL/texture.c b/GL/texture.c index b9c8066..e706e53 100644 --- a/GL/texture.c +++ b/GL/texture.c @@ -1625,6 +1625,15 @@ static inline GLboolean is4BPPFormat(GLenum format) { return format == GL_COLOR_INDEX4_EXT || format == GL_COLOR_INDEX4_TWID_KOS; } +static GLuint _glGetUnpackRowPitch(GLsizei width, GLint sourceStride, GLenum format) { + GLint rowLength = _glGetUnpackRowLength(); + GLint unpackAlignment = _glGetUnpackAlignment(); + GLuint rowPixels = (rowLength > 0) ? (GLuint) rowLength : (GLuint) width; + GLuint rowBytes = is4BPPFormat(format) ? ((rowPixels + 1) / 2) : (rowPixels * (GLuint) sourceStride); + + return (rowBytes + (GLuint) unpackAlignment - 1) & ~((GLuint) unpackAlignment - 1); +} + void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *data) { @@ -1745,6 +1754,12 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLubyte* targetData = (active->baseDataOffset == 0) ? active->data : _glGetMipmapLocation(active, level); gl_assert(targetData); + if(!data) { + gl_assert(active->index == originalId); + _glGPUStateMarkDirty(); + return; + } + if(needs_conversion < 0) { _glKosThrowError(GL_INVALID_VALUE, __func__); INFO_MSG("Couldn't find necessary texture conversion\n"); @@ -2164,18 +2179,19 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y return; } - GLboolean isPaletted = ( - active->internalFormat == GL_COLOR_INDEX8_EXT || - active->internalFormat == GL_COLOR_INDEX4_EXT || - active->internalFormat == GL_COLOR_INDEX4_TWID_KOS || - active->internalFormat == GL_COLOR_INDEX8_TWID_KOS - ) ? GL_TRUE : GL_FALSE; - GLenum cleanInternalFormat = _cleanInternalFormat(active->internalFormat); // Determine source stride GLint sourceStride = _determineStride(format, type); - GLuint srcBytes = (width * height * sourceStride); + if (sourceStride < 0) { + INFO_MSG("Stride was not detected\n"); + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return; + } + + GLuint sourceRowWidth = is4BPPFormat(format) ? (((GLuint) width + 1) / 2) : ((GLuint) width * (GLuint) sourceStride); + GLuint sourcePitch = _glGetUnpackRowPitch(width, sourceStride, format); + GLuint srcBytes = height ? (sourcePitch * ((GLuint) height - 1) + sourceRowWidth) : 0; // Calculate destination stride (this accounts for both POT and NPOT) GLint destStride = _determineStrideInternal(cleanInternalFormat); @@ -2220,6 +2236,7 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y } if (needs_conversion == CONVERSION_TYPE_CONVERT) { for (uint32_t y = 0; y < height; ++y) { + src = (const GLubyte*) data + (y * sourcePitch); dst = conversionBuffer + ((y + yoffset) * textureWidth + xoffset) * destStride; for (uint32_t x = 0; x < width; ++x) { conversion(src, dst); @@ -2233,21 +2250,24 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y for (uint32_t y = yoffset; y < yoffset + height; ++y) { for (uint32_t x = xoffset; x < xoffset + width; ++x) { - uint32_t srcIndex = (y - yoffset) * width + (x - xoffset); uint32_t newLocation = twid_compute_index(x, y, maskX, maskY); dst = conversionBuffer + (destStride * newLocation); + src = (const GLubyte*) data + ((y - yoffset) * sourcePitch) + ((x - xoffset) * sourceStride); if (needs_conversion == 3) { - conversion(src + srcIndex * sourceStride, dst); + conversion(src, dst); } else { - memcpy(dst, src + srcIndex * sourceStride, destStride); + memcpy(dst, src, destStride); } } } } if (pack) { - assert(isPaletted); + assert(active->internalFormat == GL_COLOR_INDEX8_EXT || + active->internalFormat == GL_COLOR_INDEX4_EXT || + active->internalFormat == GL_COLOR_INDEX4_TWID_KOS || + active->internalFormat == GL_COLOR_INDEX8_TWID_KOS); size_t dst_byte = 0; for (size_t src_byte = 0; src_byte < destBytes; ++src_byte) { uint8_t v = conversionBuffer[src_byte]; @@ -2267,10 +2287,19 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y free(conversionBuffer); } else { // No conversion necessary, we can update data directly + if (xoffset == 0 && + yoffset == 0 && + width == textureWidth && + sourceStride == destStride && + sourcePitch == textureWidth * destStride) { + FASTCPY(targetData, data, height * sourcePitch); + _glGPUStateMarkDirty(); + return; + } + for (GLsizei y = 0; y < height; ++y) { - GLsizei srcRowWidth = width * sourceStride; GLubyte* destRow = targetData + ((y + yoffset) * textureWidth + xoffset) * destStride; - FASTCPY(destRow, (GLubyte*)data + y * srcRowWidth, srcRowWidth); + FASTCPY(destRow, (GLubyte*)data + y * sourcePitch, sourceRowWidth); } }