У меня была странная проблема с glCompressedTexSubImage2D
на моем телефоне Android (графический процессор Adreno 530) с текстурой ASTC 8×8. Моя установка изначально содержала 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
(независимо от того, сколько строк загружается в этом вызове, 8 192 или любое кратное 8, согласно спецификации). После этого первого вызова все остальные вызовы завершаются с GL_INVALID_VALUE
, Если я поменяю порядок Y (yOffset of data.height - (num_rows * 8)
), то это последний вызов, который является единственным успешным.
Копаем дальше с GL_KHR_debug, моя реализация возвращается «размер изображения недопустим для сжатой текстуры»:
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, а также ширину и высоту, текстура загрузится без ошибок. Все мои блоки находятся в неправильных местах, но эта ошибка никогда не возникает. когда вычисление imageSize из таблицы в документации, оба варианта имеют одинаковые значения imageSize. При выходе из аргументов мои значения imageSize одинаковы.
Вы можете видеть ниже, это оригинальный код, запущенный с неинициализированной памятью после первой загрузки:
И теперь, когда xOffset / yOffset и width / height перевернуты, вся текстура загружается, но блоки смещены (что можно ожидать, если загружать блоки в неправильном порядке)
Существуют ли какие-либо ограничения на ASTC, которые могут вызвать такое поведение? Кто-нибудь еще сталкивался с чем-то подобным? В нескольких сообщениях на форуме упоминалось о каком-то внешнем изменении состояния, из-за которого загрузка не удалась, я пока ничего не нашел (включая glActiveTexture, не показанный в приведенном выше коде). Почему замена аргументов приводит к исчезновению ошибки?
Похоже, что это ошибки в конкретных реализациях OpenGL. Я смотрел на это с несколькими устройствами, и код, который я предоставил выше, значительно вырос с апреля.
Устройства 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.
Других решений пока нет …