Optimize streaming texture updates

This commit is contained in:
GPF 2026-06-05 20:49:35 -07:00
parent c94bfd7c9c
commit 5065405e71
3 changed files with 83 additions and 16 deletions

View File

@ -379,6 +379,8 @@ GLenum _glGetCullFace();
GLenum _glGetFrontFace(); GLenum _glGetFrontFace();
GLenum _glGetGpuBlendSrcFactor(); GLenum _glGetGpuBlendSrcFactor();
GLenum _glGetGpuBlendDstFactor(); GLenum _glGetGpuBlendDstFactor();
GLint _glGetUnpackRowLength();
GLint _glGetUnpackAlignment();
extern PolyList OP_LIST; extern PolyList OP_LIST;
extern PolyList PT_LIST; extern PolyList PT_LIST;

View File

@ -66,6 +66,9 @@ static struct {
GLfloat half_point_size; GLfloat half_point_size;
GLfloat half_line_width; GLfloat half_line_width;
GLint unpack_row_length;
GLint unpack_alignment;
} GPUState = { } GPUState = {
.is_dirty = GL_TRUE, .is_dirty = GL_TRUE,
.depth_func = GL_LESS, .depth_func = GL_LESS,
@ -102,6 +105,8 @@ static struct {
.current_tex_coord1 = {0.0f, 0.0f}, .current_tex_coord1 = {0.0f, 0.0f},
.half_point_size = 0.5f, .half_point_size = 0.5f,
.half_line_width = 0.5f, .half_line_width = 0.5f,
.unpack_row_length = 0,
.unpack_alignment = 4,
}; };
float* _glCurrentColor() { float* _glCurrentColor() {
@ -141,6 +146,14 @@ GLboolean _glGPUStateIsDirty() {
return GPUState.is_dirty; return GPUState.is_dirty;
} }
GLint _glGetUnpackRowLength() {
return GPUState.unpack_row_length;
}
GLint _glGetUnpackAlignment() {
return GPUState.unpack_alignment;
}
Material* _glActiveMaterial() { Material* _glActiveMaterial() {
return &GPUState.material; return &GPUState.material;
} }
@ -873,8 +886,25 @@ void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha
} }
void glPixelStorei(GLenum pname, GLint param) { void glPixelStorei(GLenum pname, GLint param) {
_GL_UNUSED(pname); switch(pname) {
_GL_UNUSED(param); 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: case GL_TEXTURE_INTERNAL_FORMAT_KOS:
*params = _glGetTextureInternalFormat(); *params = _glGetTextureInternalFormat();
break; break;
case GL_UNPACK_ROW_LENGTH:
*params = GPUState.unpack_row_length;
break;
case GL_UNPACK_ALIGNMENT:
*params = GPUState.unpack_alignment;
break;
default: default:
_glKosThrowError(GL_INVALID_ENUM, __func__); _glKosThrowError(GL_INVALID_ENUM, __func__);

View File

@ -1625,6 +1625,15 @@ static inline GLboolean is4BPPFormat(GLenum format) {
return format == GL_COLOR_INDEX4_EXT || format == GL_COLOR_INDEX4_TWID_KOS; 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, void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat,
GLsizei width, GLsizei height, GLint border, GLsizei width, GLsizei height, GLint border,
GLenum format, GLenum type, const GLvoid *data) { 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); GLubyte* targetData = (active->baseDataOffset == 0) ? active->data : _glGetMipmapLocation(active, level);
gl_assert(targetData); gl_assert(targetData);
if(!data) {
gl_assert(active->index == originalId);
_glGPUStateMarkDirty();
return;
}
if(needs_conversion < 0) { if(needs_conversion < 0) {
_glKosThrowError(GL_INVALID_VALUE, __func__); _glKosThrowError(GL_INVALID_VALUE, __func__);
INFO_MSG("Couldn't find necessary texture conversion\n"); 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; 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); GLenum cleanInternalFormat = _cleanInternalFormat(active->internalFormat);
// Determine source stride // Determine source stride
GLint sourceStride = _determineStride(format, type); 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) // Calculate destination stride (this accounts for both POT and NPOT)
GLint destStride = _determineStrideInternal(cleanInternalFormat); 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) { if (needs_conversion == CONVERSION_TYPE_CONVERT) {
for (uint32_t y = 0; y < height; ++y) { for (uint32_t y = 0; y < height; ++y) {
src = (const GLubyte*) data + (y * sourcePitch);
dst = conversionBuffer + ((y + yoffset) * textureWidth + xoffset) * destStride; dst = conversionBuffer + ((y + yoffset) * textureWidth + xoffset) * destStride;
for (uint32_t x = 0; x < width; ++x) { for (uint32_t x = 0; x < width; ++x) {
conversion(src, dst); 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 y = yoffset; y < yoffset + height; ++y) {
for (uint32_t x = xoffset; x < xoffset + width; ++x) { 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); uint32_t newLocation = twid_compute_index(x, y, maskX, maskY);
dst = conversionBuffer + (destStride * newLocation); dst = conversionBuffer + (destStride * newLocation);
src = (const GLubyte*) data + ((y - yoffset) * sourcePitch) + ((x - xoffset) * sourceStride);
if (needs_conversion == 3) { if (needs_conversion == 3) {
conversion(src + srcIndex * sourceStride, dst); conversion(src, dst);
} else { } else {
memcpy(dst, src + srcIndex * sourceStride, destStride); memcpy(dst, src, destStride);
} }
} }
} }
} }
if (pack) { 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; size_t dst_byte = 0;
for (size_t src_byte = 0; src_byte < destBytes; ++src_byte) { for (size_t src_byte = 0; src_byte < destBytes; ++src_byte) {
uint8_t v = conversionBuffer[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); free(conversionBuffer);
} else { } else {
// No conversion necessary, we can update data directly // 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) { for (GLsizei y = 0; y < height; ++y) {
GLsizei srcRowWidth = width * sourceStride;
GLubyte* destRow = targetData + ((y + yoffset) * textureWidth + xoffset) * destStride; GLubyte* destRow = targetData + ((y + yoffset) * textureWidth + xoffset) * destStride;
FASTCPY(destRow, (GLubyte*)data + y * srcRowWidth, srcRowWidth); FASTCPY(destRow, (GLubyte*)data + y * sourcePitch, sourceRowWidth);
} }
} }