Расширение ARB_texture_storage

Обычно создание текстурного объекта и заполнение его данными в OpenGL это довольно длительный процесс. Сначала мы выделяем идентификатор для нашей текстуры при помощи glGenTextures. В этот момент создания самого текстурного объекта еще не произошло - оно произойдет при первом вызове glBindTextureю Тогда же идентификатор этой текстуры будет привязан к ее типу.

Далее мы обычно начинаем задавать уровни текстуры в mipmap-пирамиде, при этом для каждого такого уровня мы можем задать разные размеры, форматы и т.п. Это ведет к необходимости выполнения радя проверок непосредственно при рендеринге, что обладает своей ценой.

Расширение ARB_texture_storage предоставляет механизм, позволяющий всего за один вызов создать всю структуру текстуры, позволяя сразу же выполнить все необходимые прорверки и выделение памяти. После того, как мы один раз зададим формат и размер текстуры, они становятся неизменяемыми, что упрощает проверки при использовании текстуры.

Для задания размера и внутреннего формата текстуры вводятся следующие функции:

void glTexStorage1D ( GLenum targetm GLsizei levels,
                      GLenum internalFormat, GLsizei width );

void glTexStorage2D ( GLenum targetm GLsizei levels,
                      GLenum internalFormat, GLsizei width, GLsizei height );

void glTexStorage2D ( GLenum targetm GLsizei levels,
                      GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth );

Обратите внимание, что такие традиционные параметры как border и format просто отсутствуют - они просто не нужны.

Давайте рассмотрим как работают эти функции на примере gltextStorage1D. Ее вызов эквивалентен выполнению следующего фрагмента кода:

for ( int i = 0; i < levels; i++ )
{
    glTexImage1D ( target, i, internalFormat, width, 0, format, type, NULL );
    width = max ( 1, width / 2 );
}

Аналогично работает и команды glTexStorage2D и glTexStorage3D, только при работе в кубическими текстурными картами и массивами кубических текстурных карт инициализируются все грани, а при работе массивом 2-мерных текстур - инициализируются все элементы массива.

После успешного вызова glTexStorage у текстуры свойство GL_TEXTURE_IMMUTABLE_FORMAT становится равным GL_TRUE и с этого момента размер и формат тектсруы уже нельзя изменить.

Также после этого для данной текстуры использовать команды glTexImage, glCompressedTexImage, glCopyTexture и glTexStorage.

Вместо этого для задания данных для текстуры (т.е. изображения) используются команды glTexSubImage и glCompressedTexSubImage (а также glGenerateMipmap) соответственно.

Также у этого расширения есть поддержка Direct State Access, позволяющая избегать необходимости "привязывания" текстуры - для этого используются следующие функции, вошедшие в состав OpenGL 4.5:

void glTextureStorage1D ( GLuint texture, GLsizei levels, GLenum internalformat,
    GLsizei width );

void glTextureStorage2D ( GLuint texture, GLsizei levels, GLenum internalformat,
    GLsizei width, GLsizei height );

void glTextureStorage3D ( GLuint texture, GLsizei levels, GLenum internalformat,
    GLsizei width, GLsizei height, GLsizei depth );

Обратите внимание, что если вы хотите использовать эти DSA-функции, то для создания текстуры следует использовать не glGenTextures, а glCreateTextures:

void glCreateTextures ( GLenum target, GLsizei n, GLuint * textures );

Ниже приводится пример кода, создающего двухмерную текстуру и загружающей в нее изображение с использованием рассмотренных выше функций, обратите внимание на использование функции glTextureParameteri вместо glTexParameteri. За основу для этого примера взят код из OpenGL Samples Pack. В этом фрагменте кода создается двухмерная текстура и для нее задаются необходимые свойства и изображения для всех уровней пирамиды вообще не прибегая к команде glBindTexture.

gli::texture2D Texture(gli::load_dds((getDataDirectory() + TEXTURE_DIFFUSE).c_str()));
    
if(Texture.empty())
    return false;
        
glCreateTextures(GL_TEXTURE_2D, 1, &TextureName[texture::TEXTURE]);
    
glTextureParameteri(TextureName[texture::TEXTURE], GL_TEXTURE_BASE_LEVEL, 0);      
glTextureParameteri(TextureName[texture::TEXTURE], GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(Texture.levels() - 1));
glTextureParameteri(TextureName[texture::TEXTURE], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(TextureName[texture::TEXTURE], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        
glTextureStorage2D(TextureName[texture::TEXTURE], GLint(Texture.levels()), 
                   gli::internal_format(Texture.format()), 
                   GLsizei(Texture[0].dimensions().x), GLsizei(Texture[0].dimensions().y));
        
for(std::size_t Level = 0; Level < Texture.levels(); ++Level)
    glTextureSubImage2D(TextureName[texture::TEXTURE], GLint(Level), 0, 0, 
                        GLsizei(Texture[Level].dimensions().x), GLsizei(Texture[Level].dimensions().y), 
                        gli::external_format(Texture.format()), gli::type_format(Texture.format()),
                        Texture[Level].data());

return true;

Обратите внимание, что этот пример ориентирован на использование OpenGL 4.5.