Текстура уничтожается слишком рано?

Рейтинг: 2Ответов: 2Опубликовано: 22.08.2023
// Sprite.
if(ImGui::CollapsingHeader("Sprite.")) {
    // Preview texture allocations.
    sf::Texture texture;

    // ...

    static char spritePath[MAX_OBJECTS_TEXT_SIZE / 2]; // (512 / 2)

    // ...

    if(ImGui::InputText("Sprite path.##sprite", spritePath, IM_ARRAYSIZE(spritePath))) {
        texture.loadFromFile(spritePath); // <- загрузка
    }

    // ...

    // Preview. (150, 150).
    ImGui::Image(texture, sf::Vector2f(DEFAULT_TEXTURE_PREVIEW_WIDTH, DEFAULT_TEXTURE_PREVIEW_HEIGHT), sf::Color::Transparent, sf::Color::White);
}

В результате получается пустой квадрат:

image

example.png - точно существует и ошибок нету (о том что не удалось загрузить текстуру).

Из документации SFML -

Вы успешно загрузили текстуру, правильно построили спрайт, и... теперь все, что вы видите на своем экране, это белый квадрат. Что случилось?

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

sf::Sprite loadSprite(std::string filename)
{
    sf::Texture texture;
    texture.loadFromFile(filename);

    return sf::Sprite(texture);
} // error: the texture is destroyed here (ошибка: спрайт уничтожен сдесь)

Я не понимаю почему текстура уничтожается, если я ее создал в одном блоке if? и как сделать что бы она не уничтожалась?

P.S: Функция ImGui::Image - рисует изображение в следующием кадре, а уже в следущем кадре текстура удаляется, как сделать чтобы она не удалялась?

UPD:

sf::Texture texture;

while(window.isOpen()) {
    // Sprite.
    if(ImGui::CollapsingHeader("Sprite.")) {
        if(ImGui::InputText("Sprite path.##sprite", spritePath, IM_ARRAYSIZE(spritePath))) {
            if(!texture.loadFromFile(spritePath)) {
                LevelDesignerLog::consoleLog("Failed to show sprite preview.");
            } else {
                LevelDesignerLog::consoleLog("Loaded sprite preview successfully.");
            }
        }

        // ...

        // Create sprite.
        if(ImGui::Button("Create sprite.")) {
            LevelObject sprite;

            sprite.lobjecttype = LevelObjectType::SPRITE;

            sprite.lobjectname = objectName;

            sprite.objectSWidth = spriteWidth;
            sprite.objectSHeight = spriteHeight;

            sprite.objectX = spriteX;
            sprite.objectY = spriteY;

            sprite.spritePath = spritePath;

            objectsBuffer.addObject(sprite);

            LevelDesignerLog::consoleLog("Sprite '" + std::string(objectName) + "' created.");
        }

        // Preview.
        ImGui::Image(texture, sf::Vector2f(DEFAULT_TEXTURE_PREVIEW_WIDTH, DEFAULT_TEXTURE_PREVIEW_HEIGHT), sf::Color::Transparent, sf::Color::White);
    }
}

Сделал обявление текстуры перед циклом, не помогло

Ответы

▲ 2Принят

У локальных переменных в стеке (те, которые создаются не через new()) ограниченное время жизни. И при этом ограничено оно блоком кода

if(ImGui::CollapsingHeader("Sprite.")) {
    // Preview texture allocations.
    sf::Texture texture; // <- Выделяется память на стеке для текстуры

    ...

    ImGui::Image(texture, sf::Vector2f(DEFAULT_TEXTURE_PREVIEW_WIDTH, DEFAULT_TEXTURE_PREVIEW_HEIGHT), sf::Color::Transparent, sf::Color::White);
} // <- Блок кода if закрывается, 
  // следовательно все переменные в данном блоке удаляются
  // и память с текстурой освобождается
  // вызывая у текстуры ~Texture() который удаляет сам объект текстуры в куче

А отрисовка скорее всего происходит в отдельном потоке, из за чего texture используется уже после того, как он был удалён

Да и загружать текстуру каждый кадр плохая идея

Вам стоит вынести переменную sf::Texture texture куда ни будь за пределы цикла кадра (например в класс, в котором это всё обрабатывается), а texture.loadFromFile(...) вынести перед циклом (там где всё инициализируется)

UPDATED!!!

Возможно в IMGUI текстуры надо хранить на куче

(UPDATED2!!! я ошибся, текстуру можно хранить на стеке, главное в нужном месте):

sf::Texture texture;
// Заменяй на
sf::Texture* texture = new sf::Texture();

!texture.loadFromFile(spritePath)
// Заменяй на 
!texture->loadFromFile(spritePath)

ImGui::Image(texture, ...)
// Заменяй на
ImGui::Image(*texture, ...)
▲ 0

Оказывается, третий аргумент tintColor в ImGui::Image окрашивает не только задний фон, но и саму текстуру, и этот tintColor я сделал прозрачным, не зная, что это делает текстуру также прозрачной.

И еще сделал, чтобы текстура хранилась на стеке, и теперь все заработало.

image