GL_INVALID_VALUE 在具有 ASTC 8x8 纹理的 glCompressedTexSubImage2D 上
GL_INVALID_VALUE on glCompressedTexSubImage2D with an ASTC 8x8 texture
我的 Android phone (Adreno 530 GPU) 使用 ASTC 8x8 纹理时,glCompressedTexSubImage2D
遇到了一个奇怪的问题。我的设置最初包含一个用于促进异步纹理上传的 PBO,但我已将其削减到最低限度以重现此问题。请注意,这是作为本机插件托管在 Unity 5.5.2f1 中的,因此 Unity 可能会在我调用 glCompressedTexSubImage2D
期间更改某些状态
首先,我正在针对 Android 平台 24 进行编译,以便暂时访问 <GLES3/gl32.h>
,并使用正确的标志和设置来启用 C++11 功能。
我定义了这个结构来存储通过插件更新的每个纹理的状态:
enum texture_format
{
ASTC_RGBA_8x8 = 57,
};
struct texture_data
{
void* ptr;
bool has_initialized;
uint32_t width;
uint32_t height;
texture_format format;
std::vector<uint8_t> cpu_buffer;
std::mutex lock_cpu_buffer;
uint32_t row;
};
我已经验证来自 Unity 的数据正在正确传递,这是执行纹理上传的(为清楚起见而缩短的)函数:
data.lock_cpu_buffer.lock();
int bytesPerRow = data.width * 2; //Specific to ASTC 8x8 for now: (width / 8) * 16
int num_rows = data.cpu_buffer.size() / bytesPerRow;
if (num_rows <= 0)
{
data.lock_cpu_buffer.unlock();
return;
}
glBindTexture(GL_TEXTURE_2D, (GLuint)data.ptr);
//Just in case Unity is doing something with these
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, data.row, data.width, 8 * num_rows, GL_COMPRESSED_RGBA_ASTC_8x8, bytesPerRow * num_rows, data.cpu_buffer.data());
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
#if UNITY_ANDROID
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "glCompressedTexSubImage2D error %u", err);
#endif
}
//prepare buffer for future by copying remainder to beginning of vector and resizing (no allocation should happen here)
data.row += num_rows * 8;
std::copy(data.cpu_buffer.begin() + (bytesPerRow * num_rows), data.cpu_buffer.end(), data.cpu_buffer.begin());
data.cpu_buffer.resize(data.cpu_buffer.size() - (bytesPerRow * num_rows));
glBindTexture(GL_TEXTURE_2D, 0);
data.lock_cpu_buffer.unlock();
总而言之,我将任意数量的数据从 Unity 中的流推送到本机插件中的缓冲区(代替映射的 PBO 指针),然后通过 glCompressedTexSubImage2D
为了让事情变得更简单,我跳过了流的前 16 个字节(文件头)并读取了 4096 字节的块(正好是一行的大小)。所以 std::copy 的最后一位目前实际上并没有复制任何数据。我已经通过记录尽可能多的数据来验证这一点,缓冲区的大小每次都从 4096 的精确倍数调整为 0。
如所写,此函数将成功 yOffset = 0
(无论该调用中上传了多少行,8192,或 8 的任意倍数,根据规范)。在第一次调用之后,所有其他调用都失败并显示 GL_INVALID_VALUE
。如果我反转 Y 顺序(data.height - (num_rows * 8)
的 yOffset),那么最后一次调用是唯一成功的调用。
进一步挖掘 GL_KHR_debug,我的实现返回 "image size is invalid for compressed texture":
04-10 18:35:26.218 24522 24541 V AsyncTexUpload: id=102 glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 1432, 2048, 96, GL_COMPRESSED_RGBA_ASTC_8x8, 49152, ptr)
04-10 18:35:26.218 24522 24541 V AsyncTexUpload: Logged message 8246, 824C, 7FFFFFFF, 9146, image size is invalid for compressed texture
04-10 18:35:26.218 24522 24541 V AsyncTexUpload: glCompressedTexSubImage2D error 1281
此外,这里是真正让我困惑的部分,如果我交换 xOffset 和 yOffset,以及宽度和高度,纹理上传没有错误。我的积木都在不正确的地方,但这个错误从未发生过。当 computing the imageSize from the table in the documentation、 两个变体具有相同的 imageSize 值。注销参数时,我的 imageSize 值是相同的。
你可以在下面看到,这是原始代码运行第一次上传后未初始化的内存:
现在,xOffset/yOffset 和 width/height 翻转后,整个纹理正在上传,但块未对齐(如果以错误的顺序上传块,您会期望什么)
ASTC 是否有任何会导致此行为的限制?还有其他人 运行 喜欢类似的东西吗?一些论坛帖子提到一些外部状态变化导致上传失败,我还没有找到任何东西(这包括 glActiveTexture,上面的代码中没有显示)。为什么交换参数会导致错误消失?
这些似乎是 OpenGL 特定实现中的错误。我已经在多个设备上看过这个,我上面提供的代码自 4 月以来已经显着成熟。
Qualcomm 设备似乎希望纹理的大小达到行。也就是说,公式不是我期望的 ceil(width/8) * ceil(height/8) * 16
,而是 ceil(width/8) * ceil((height + yOffset)/8) * 16
.
在上面的代码中,这将转换为
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, data.row, data.width, 8 * num_rows, GL_COMPRESSED_RGBA_ASTC_8x8, bytesPerRow * (data.row + num_rows), data.cpu_buffer.data());
Apple 设备 (imgtec) 似乎符合我对规范的解释,我目前正在尝试发现 Mali 设备的模式。
我的 Android phone (Adreno 530 GPU) 使用 ASTC 8x8 纹理时,glCompressedTexSubImage2D
遇到了一个奇怪的问题。我的设置最初包含一个用于促进异步纹理上传的 PBO,但我已将其削减到最低限度以重现此问题。请注意,这是作为本机插件托管在 Unity 5.5.2f1 中的,因此 Unity 可能会在我调用 glCompressedTexSubImage2D
首先,我正在针对 Android 平台 24 进行编译,以便暂时访问 <GLES3/gl32.h>
,并使用正确的标志和设置来启用 C++11 功能。
我定义了这个结构来存储通过插件更新的每个纹理的状态:
enum texture_format
{
ASTC_RGBA_8x8 = 57,
};
struct texture_data
{
void* ptr;
bool has_initialized;
uint32_t width;
uint32_t height;
texture_format format;
std::vector<uint8_t> cpu_buffer;
std::mutex lock_cpu_buffer;
uint32_t row;
};
我已经验证来自 Unity 的数据正在正确传递,这是执行纹理上传的(为清楚起见而缩短的)函数:
data.lock_cpu_buffer.lock();
int bytesPerRow = data.width * 2; //Specific to ASTC 8x8 for now: (width / 8) * 16
int num_rows = data.cpu_buffer.size() / bytesPerRow;
if (num_rows <= 0)
{
data.lock_cpu_buffer.unlock();
return;
}
glBindTexture(GL_TEXTURE_2D, (GLuint)data.ptr);
//Just in case Unity is doing something with these
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, data.row, data.width, 8 * num_rows, GL_COMPRESSED_RGBA_ASTC_8x8, bytesPerRow * num_rows, data.cpu_buffer.data());
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
#if UNITY_ANDROID
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "glCompressedTexSubImage2D error %u", err);
#endif
}
//prepare buffer for future by copying remainder to beginning of vector and resizing (no allocation should happen here)
data.row += num_rows * 8;
std::copy(data.cpu_buffer.begin() + (bytesPerRow * num_rows), data.cpu_buffer.end(), data.cpu_buffer.begin());
data.cpu_buffer.resize(data.cpu_buffer.size() - (bytesPerRow * num_rows));
glBindTexture(GL_TEXTURE_2D, 0);
data.lock_cpu_buffer.unlock();
总而言之,我将任意数量的数据从 Unity 中的流推送到本机插件中的缓冲区(代替映射的 PBO 指针),然后通过 glCompressedTexSubImage2D
为了让事情变得更简单,我跳过了流的前 16 个字节(文件头)并读取了 4096 字节的块(正好是一行的大小)。所以 std::copy 的最后一位目前实际上并没有复制任何数据。我已经通过记录尽可能多的数据来验证这一点,缓冲区的大小每次都从 4096 的精确倍数调整为 0。
如所写,此函数将成功 yOffset = 0
(无论该调用中上传了多少行,8192,或 8 的任意倍数,根据规范)。在第一次调用之后,所有其他调用都失败并显示 GL_INVALID_VALUE
。如果我反转 Y 顺序(data.height - (num_rows * 8)
的 yOffset),那么最后一次调用是唯一成功的调用。
进一步挖掘 GL_KHR_debug,我的实现返回 "image size is invalid for compressed texture":
04-10 18:35:26.218 24522 24541 V AsyncTexUpload: id=102 glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 1432, 2048, 96, GL_COMPRESSED_RGBA_ASTC_8x8, 49152, ptr)
04-10 18:35:26.218 24522 24541 V AsyncTexUpload: Logged message 8246, 824C, 7FFFFFFF, 9146, image size is invalid for compressed texture
04-10 18:35:26.218 24522 24541 V AsyncTexUpload: glCompressedTexSubImage2D error 1281
此外,这里是真正让我困惑的部分,如果我交换 xOffset 和 yOffset,以及宽度和高度,纹理上传没有错误。我的积木都在不正确的地方,但这个错误从未发生过。当 computing the imageSize from the table in the documentation、 两个变体具有相同的 imageSize 值。注销参数时,我的 imageSize 值是相同的。
你可以在下面看到,这是原始代码运行第一次上传后未初始化的内存:
现在,xOffset/yOffset 和 width/height 翻转后,整个纹理正在上传,但块未对齐(如果以错误的顺序上传块,您会期望什么)
ASTC 是否有任何会导致此行为的限制?还有其他人 运行 喜欢类似的东西吗?一些论坛帖子提到一些外部状态变化导致上传失败,我还没有找到任何东西(这包括 glActiveTexture,上面的代码中没有显示)。为什么交换参数会导致错误消失?
这些似乎是 OpenGL 特定实现中的错误。我已经在多个设备上看过这个,我上面提供的代码自 4 月以来已经显着成熟。
Qualcomm 设备似乎希望纹理的大小达到行。也就是说,公式不是我期望的 ceil(width/8) * ceil(height/8) * 16
,而是 ceil(width/8) * ceil((height + yOffset)/8) * 16
.
在上面的代码中,这将转换为
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, data.row, data.width, 8 * num_rows, GL_COMPRESSED_RGBA_ASTC_8x8, bytesPerRow * (data.row + num_rows), data.cpu_buffer.data());
Apple 设备 (imgtec) 似乎符合我对规范的解释,我目前正在尝试发现 Mali 设备的模式。