update for glTexSubImage2D, now checks the active texture size and not the subimage size to draw the glTexSubImage2D pixelas into drawing with the xoffset and yoffset into the existing POT2 texture, so subimage size doesn't need to be a POT2.

This commit is contained in:
GPF 2024-10-14 13:26:17 -07:00
parent 72136bb7f5
commit a11847e513

View File

@ -1480,7 +1480,9 @@ static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalForm
return true; return true;
} }
static bool _glTexSubImage2DValidate(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type) { static bool _glTexSubImage2DValidate(GLenum target, GLint level, GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height, GLenum format, GLenum type,
GLsizei textureWidth, GLsizei textureHeight) {
if (target != GL_TEXTURE_2D) { if (target != GL_TEXTURE_2D) {
INFO_MSG("Invalid target. Only GL_TEXTURE_2D is supported."); INFO_MSG("Invalid target. Only GL_TEXTURE_2D is supported.");
_glKosThrowError(GL_INVALID_ENUM, __func__); _glKosThrowError(GL_INVALID_ENUM, __func__);
@ -1517,7 +1519,7 @@ static bool _glTexSubImage2DValidate(GLenum target, GLint level, GLint xoffset,
// Validate type // Validate type
if (format != GL_COLOR_INDEX4_EXT && format != GL_COLOR_INDEX4_TWID_KOS) { if (format != GL_COLOR_INDEX4_EXT && format != GL_COLOR_INDEX4_TWID_KOS) {
/* Abuse determineStride to see if type is valid */ /* Use determineStride to see if type is valid */
if (_determineStride(GL_RGBA, type) == -1) { if (_determineStride(GL_RGBA, type) == -1) {
INFO_MSG("Invalid pixel data type."); INFO_MSG("Invalid pixel data type.");
_glKosThrowError(GL_INVALID_ENUM, __func__); _glKosThrowError(GL_INVALID_ENUM, __func__);
@ -1525,11 +1527,10 @@ static bool _glTexSubImage2DValidate(GLenum target, GLint level, GLint xoffset,
} }
} }
// Validate offsets and dimensions // Validate offsets and dimensions using the underlying texture size
GLint maxTextureWidth = 1024; // Assuming maximum texture size if (xoffset < 0 || yoffset < 0 ||
GLint maxTextureHeight = 1024; (xoffset + width) > textureWidth ||
(yoffset + height) > textureHeight) {
if (xoffset < 0 || yoffset < 0 || (xoffset + width) > maxTextureWidth || (yoffset + height) > maxTextureHeight) {
INFO_MSG("Subimage exceeds the dimensions of the texture."); INFO_MSG("Subimage exceeds the dimensions of the texture.");
_glKosThrowError(GL_INVALID_VALUE, __func__); _glKosThrowError(GL_INVALID_VALUE, __func__);
return false; return false;
@ -1541,28 +1542,6 @@ static bool _glTexSubImage2DValidate(GLenum target, GLint level, GLint xoffset,
return false; return false;
} }
// Check for the power-of-two requirement
if (level == 0) {
if ((width < 8 || (width & -width) != width)) {
INFO_MSG("Width must be a power of two and at least 8.");
_glKosThrowError(GL_INVALID_VALUE, __func__);
return false;
}
if ((height < 8 || (height & -height) != height)) {
INFO_MSG("Height must be a power of two and at least 8.");
_glKosThrowError(GL_INVALID_VALUE, __func__);
return false;
}
} else {
// Mipmap level should not be 1x1
if ((width < 2) || (height < 2)) {
INFO_MSG("Mipmap level must be at least 2x2.");
_glKosThrowError(GL_INVALID_VALUE, __func__);
return false;
}
}
// Ensure no border (since glTexSubImage2D doesn't allow it) // Ensure no border (since glTexSubImage2D doesn't allow it)
GLint border = 0; // No border allowed GLint border = 0; // No border allowed
if (border != 0) { if (border != 0) {
@ -2097,9 +2076,8 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y
GLenum type, const GLvoid *data) { GLenum type, const GLvoid *data) {
TRACE(); TRACE();
if (!_glTexSubImage2DValidate(target, level, xoffset, yoffset, width, height, format, type)) { // printf("glTexSubImage2D called with: target=%d, level=%d, xoffset=%d, yoffset=%d, width=%d, height=%d, format=%d, type=%d\n",
return; // target, level, xoffset, yoffset, width, height, format, type);
}
gl_assert(ACTIVE_TEXTURE < MAX_GLDC_TEXTURE_UNITS); gl_assert(ACTIVE_TEXTURE < MAX_GLDC_TEXTURE_UNITS);
TextureObject* active = TEXTURE_UNITS[ACTIVE_TEXTURE]; TextureObject* active = TEXTURE_UNITS[ACTIVE_TEXTURE];
@ -2110,11 +2088,31 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y
return; return;
} }
// Ensure the subregion fits within the existing texture // Retrieve the dimensions of the currently bound texture
if (xoffset < 0 || yoffset < 0 || GLsizei textureWidth = active->width;
xoffset + width > active->width || GLsizei textureHeight = active->height;
yoffset + height > active->height) {
_glKosThrowError(GL_INVALID_VALUE, __func__); // printf("Texture dimensions: width=%d, height=%d\n", textureWidth, textureHeight);
// printf("Subimage dimensions: width=%d, height=%d\n", width, height);
// Check if the texture has been resized
GLboolean isResized = (width != textureWidth || height != textureHeight);
// if (isResized) {
// printf("Texture has been resized. Adjusting copy strategy.\n");
// }
// Validate the subregion fits within the existing texture dimensions
// if (xoffset < 0 || yoffset < 0 ||
// xoffset + width > textureWidth ||
// yoffset + height > textureHeight) {
// INFO_MSG("Error: Subregion does not fit within texture dimensions\n");
// _glKosThrowError(GL_INVALID_VALUE, __func__);
// return;
// }
// Pass the texture dimensions to the validation function
if (!_glTexSubImage2DValidate(target, level, xoffset, yoffset, width, height, format, type, textureWidth, textureHeight)) {
INFO_MSG("Error: _glTexSubImage2DValidate failed\n");
return; return;
} }
@ -2125,24 +2123,40 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y
active->internalFormat == GL_COLOR_INDEX8_TWID_KOS active->internalFormat == GL_COLOR_INDEX8_TWID_KOS
) ? GL_TRUE : GL_FALSE; ) ? GL_TRUE : GL_FALSE;
// printf("isPaletted: %s\n", isPaletted ? "true" : "false");
GLenum cleanInternalFormat = _cleanInternalFormat(active->internalFormat); GLenum cleanInternalFormat = _cleanInternalFormat(active->internalFormat);
GLint destStride = _determineStrideInternal(cleanInternalFormat); // printf("cleanInternalFormat: %d\n", cleanInternalFormat);
// Determine source stride
GLint sourceStride = _determineStride(format, type); GLint sourceStride = _determineStride(format, type);
GLuint srcBytes = (width * height * sourceStride); GLuint srcBytes = (width * height * sourceStride);
GLuint destBytes = (width * height * destStride);
// Calculate destination stride (this accounts for both POT and NPOT)
GLint destStride = _determineStrideInternal(cleanInternalFormat);
// printf("sourceStride: %d, destStride: %d, srcBytes: %u\n", sourceStride, destStride, srcBytes);
// Calculate destBytes using the texture's full dimensions
GLuint destBytes = (textureWidth * textureHeight * destStride);
TextureConversionFunc conversion; TextureConversionFunc conversion;
int needs_conversion = _determineConversion(cleanInternalFormat, format, type, &conversion); int needs_conversion = _determineConversion(cleanInternalFormat, format, type, &conversion);
// Handle 4bpp formats // printf("needs_conversion: %d\n", needs_conversion);
// Adjust source bytes for 4bpp formats
if (format == GL_COLOR_INDEX4_EXT || format == GL_COLOR_INDEX4_TWID_KOS) { if (format == GL_COLOR_INDEX4_EXT || format == GL_COLOR_INDEX4_TWID_KOS) {
srcBytes /= 2; srcBytes /= 2;
// printf("Adjusted srcBytes for 4bpp format: %u\n", srcBytes);
} }
if ((needs_conversion & CONVERSION_TYPE_PACK) == CONVERSION_TYPE_PACK) { if ((needs_conversion & CONVERSION_TYPE_PACK) == CONVERSION_TYPE_PACK) {
destBytes /= 2; destBytes /= 2;
// printf("Adjusted destBytes for CONVERSION_TYPE_PACK: %u\n", destBytes);
} else if (active->internalFormat == GL_COLOR_INDEX4_EXT || active->internalFormat == GL_COLOR_INDEX4_TWID_KOS) { } else if (active->internalFormat == GL_COLOR_INDEX4_EXT || active->internalFormat == GL_COLOR_INDEX4_TWID_KOS) {
destBytes /= 2; destBytes /= 2;
// printf("Adjusted destBytes for 4bpp internal format: %u\n", destBytes);
} }
if (needs_conversion < 0) { if (needs_conversion < 0) {
@ -2152,77 +2166,67 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y
} }
// Calculate the starting point for the subregion in the texture data // Calculate the starting point for the subregion in the texture data
GLubyte* targetData = active->data + (yoffset * active->width + xoffset) * destStride; GLubyte* targetData = active->data + (yoffset * textureWidth + xoffset) * destStride;
// printf("targetData offset: %td bytes\n", (ptrdiff_t)((uintptr_t)targetData - (uintptr_t)active->data));
if (needs_conversion > 0) { if (needs_conversion > 0) {
GLubyte* conversionBuffer = (GLubyte*) memalign(32, srcBytes); GLubyte* conversionBuffer = (GLubyte*) memalign(32, textureWidth * textureHeight * destStride);
// printf("Allocated conversionBuffer of size %u at %p\n", textureWidth * textureHeight * destStride, (void*)conversionBuffer);
const GLubyte* src = data; const GLubyte* src = data;
GLubyte* dst = conversionBuffer; GLubyte* dst = conversionBuffer;
bool pack = (needs_conversion & CONVERSION_TYPE_PACK) == CONVERSION_TYPE_PACK; bool pack = (needs_conversion & CONVERSION_TYPE_PACK) == CONVERSION_TYPE_PACK;
needs_conversion &= ~CONVERSION_TYPE_PACK; needs_conversion &= ~CONVERSION_TYPE_PACK;
// printf("Conversion type: %d, pack: %s\n", needs_conversion, pack ? "true" : "false");
if (needs_conversion == CONVERSION_TYPE_CONVERT) { if (needs_conversion == CONVERSION_TYPE_CONVERT) {
for (uint32_t i = 0; i < (width * height); ++i) { // printf("Performing conversion\n");
conversion(src, dst); for (uint32_t y = 0; y < height; ++y) {
dst += destStride; for (uint32_t x = 0; x < width; ++x) {
src += sourceStride; conversion(src, dst);
} dst += destStride;
} else if (needs_conversion == 2) {
// Twiddle
if (is4BPPFormat(active->internalFormat) && is4BPPFormat(format)) {
// Special case twiddling for 4BPP formats
twid_prepare_table(width, height);
for (uint32_t i = 0; i < (width * height); ++i) {
uint32_t newLocation = twid_location(i);
assert(newLocation < (width * height));
assert((newLocation / 2) < destBytes);
assert((i / 2) < srcBytes);
src = &((uint8_t*) data)[i / 2];
dst = &conversionBuffer[newLocation / 2];
uint8_t src_value = (i % 2) == 0 ? (*src >> 4) : (*src & 0xF);
if (newLocation % 2 == 1) {
*dst = (*dst & 0xF) | (src_value << 4);
} else {
*dst = (*dst & 0xF0) | (src_value & 0xF);
}
}
} else {
twid_prepare_table(width, height);
for (uint32_t i = 0; i < (width * height); ++i) {
uint32_t newLocation = twid_location(i);
dst = conversionBuffer + (destStride * newLocation);
for (int j = 0; j < destStride; ++j)
*dst++ = *(src + j);
src += sourceStride; src += sourceStride;
} }
// Clear the remaining part of the row if resized
if (isResized) {
memset(dst, 0, (textureWidth - width) * destStride);
dst += (textureWidth - width) * destStride;
}
} }
} else if (needs_conversion == 3) { // Clear remaining rows if resized
// Convert + twiddle if (isResized) {
twid_prepare_table(width, height); memset(dst, 0, (textureHeight - height) * textureWidth * destStride);
}
} else if (needs_conversion == 2 || needs_conversion == 3) {
// printf("Performing twiddling %s\n", needs_conversion == 3 ? "with conversion" : "");
twid_prepare_table(textureWidth, textureHeight);
for (uint32_t i = 0; i < (width * height); ++i) { for (uint32_t y = 0; y < textureHeight; ++y) {
uint32_t newLocation = twid_location(i); for (uint32_t x = 0; x < textureWidth; ++x) {
dst = conversionBuffer + (destStride * newLocation); uint32_t srcIndex = y * width + x;
src = data + (sourceStride * i); uint32_t newLocation = twid_location(y * textureWidth + x);
conversion(src, dst); dst = conversionBuffer + (destStride * newLocation);
if (x < width && y < height) {
if (needs_conversion == 3) {
conversion(src + srcIndex * sourceStride, dst);
} else {
memcpy(dst, src + srcIndex * sourceStride, destStride);
}
} else {
memset(dst, 0, destStride);
}
}
} }
} else if (pack) {
FASTCPY(conversionBuffer, data, srcBytes);
} }
if (pack) { if (pack) {
assert(isPaletted); assert(isPaletted);
// printf("Packing paletted texture\n");
size_t dst_byte = 0; size_t dst_byte = 0;
for (size_t src_byte = 0; src_byte < srcBytes; ++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];
if (src_byte % 2 == 0) { if (src_byte % 2 == 0) {
@ -2234,22 +2238,38 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y
} }
} }
// Copy the converted data to the texture, respecting the subimage dimensions // Copy the converted data to the texture
for (GLsizei y = 0; y < height; ++y) { // printf("Copying converted data to texture\n");
FASTCPY(targetData, conversionBuffer + y * width * destStride, width * destStride); FASTCPY(targetData, conversionBuffer, destBytes);
targetData += active->width * destStride; // Move to next row in target
}
free(conversionBuffer); free(conversionBuffer);
// printf("Freed conversionBuffer\n");
} else { } else {
// No conversion necessary, we can update data directly // No conversion necessary, we can update data directly
// printf("No conversion needed, copying data directly\n");
for (GLsizei y = 0; y < height; ++y) { for (GLsizei y = 0; y < height; ++y) {
FASTCPY(targetData, (GLubyte*)data + y * width * sourceStride, width * sourceStride); GLsizei srcRowWidth = width * sourceStride;
targetData += active->width * destStride; // Move to next row in target GLsizei destRowWidth = textureWidth * destStride;
FASTCPY(targetData, (GLubyte*)data + y * srcRowWidth, srcRowWidth);
// If resized, we need to clear the remaining part of the row
if (isResized && width < textureWidth) {
memset(targetData + srcRowWidth, 0, destRowWidth - srcRowWidth);
}
targetData += destRowWidth; // Move to next row in target
}
// If resized, we need to clear the remaining rows
if (isResized && height < textureHeight) {
GLsizei remainingRows = textureHeight - height;
GLsizei rowSize = textureWidth * destStride;
memset(targetData, 0, remainingRows * rowSize);
} }
} }
_glGPUStateMarkDirty(); _glGPUStateMarkDirty();
// printf("glTexSubImage2D completed\n");
} }
GLAPI void APIENTRY glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { GLAPI void APIENTRY glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {