Reputation: 14688
I have been experiencing a strange problem with glCompressedTexSubImage2D
on my Android phone (Adreno 530 GPU) with an ASTC 8x8 texture. My setup originally contained a PBO used to facilitate asynchronous texture uploading, but I have pared it down to the bare minimum to reproduce this issue. Note that this is hosted inside Unity 5.5.2f1 as a native plugin, so Unity could be changing some state between my calls to glCompressedTexSubImage2D
To start I am compiling against Android platform 24 to have access to <GLES3/gl32.h>
for now, and with the correct flags and settings to enable C++11 features.
I have this struct defined that stores state for each texture that is being updated through the plugin:
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;
};
I have already verified that the data from Unity is being passed in correctly, this is the (shortened for clarity) function that does the texture uploading:
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();
In summary, I am pushing an arbitrary amount of data from a stream in Unity to a buffer in a native plugin (in place of a mapped PBO pointer), and then uploading a number of rows at a time via glCompressedTexSubImage2D
To make matters even simpler, I am skipping the first 16 bytes of the stream (file header) and reading in 4096 byte chunks (exactly the size of a row). So the last bit with std::copy doesn't actually copy any data at the moment. I have verified this by logging as much data as I can, the buffer gets resized from an exact multiple of 4096 to 0 every time.
This function, as written, will succeed for yOffset = 0
(no matter how many rows are being uploaded in that call, 8 192, or any multiple of 8, as per the spec). Following this first call, all other calls fail with a GL_INVALID_VALUE
. If I reverse the Y order (yOffset of data.height - (num_rows * 8)
), then it is the last call that is the only successful one.
Digging further with GL_KHR_debug, my implementation is returning "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
Additionally, and here's the part that has really confused me, if I swap xOffset and yOffset, as well as width and height, the texture uploads without error. My blocks are all in the incorrect spots, but this error never occurs. When computing the imageSize from the table in the documentation, both variants have identical imageSize values. When logging arguments out, my imageSize values are the same.
You can see below, this is the original code running with uninitialized memory past the first upload:
And now, with xOffset/yOffset and width/height flipped, the entire texture is getting uploaded but the blocks are misaligned (what you would expect if uploading the blocks in the wrong order)
Are there any restrictions on ASTC that would cause this behavior? Has anyone else run into something similar? A few forum posts mentioned some external state changing causing the upload to fail, I have yet to find anything (this includes glActiveTexture, not shown in the code above). Why would swapping the arguments cause the error to disappear?
Upvotes: 1
Views: 960
Reputation: 14688
It appears as though these are bugs in specific implementations of OpenGL. I've looked at this with multiple devices and the code I've provided above has matured significantly since April.
Qualcomm devices seem to want the size of the texture up to the row. That is, instead of what I would expect to be ceil(width/8) * ceil(height/8) * 16
, the formula is ceil(width/8) * ceil((height + yOffset)/8) * 16
.
In the above code, this would translate to
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 devices (imgtec) seem to follow my interpretation of the spec, I'm currently trying to discover the pattern for Mali devices.
Upvotes: 2