Исходник GIF in mimgui v0.1 - нейросетевая библиотека запуска .gif в mimgui

Орк

Известный
Автор темы
416
358

НЕБЕЗОПАСНАЯ ХУЙНЯ

GIF in mimgui - это библиотека написанная нейросетью, которая позволяет воспроизводить gif в окне mimgui.

b6SjWDE.gif

Пример использования::
local imgui = require 'mimgui'
local giflib = require 'GIFinmimgui'
local ffi = require 'ffi'
local vkeys = require 'vkeys'

local wm = require 'windows.message'
local new, str, sizeof = imgui.new, ffi.string, ffi.sizeof

local renderWindow = new.bool()

local sizeX, sizeY = getScreenResolution()

imgui.OnInitialize(function()
    imgui.GetIO().IniFilename = nil
end)


local newFrame = imgui.OnFrame(
    function() return renderWindow[0] end,
    function(player)
        imgui.SetNextWindowPos(imgui.ImVec2(sizeX / 2, sizeY / 2), imgui.Cond.FirstUseEver, imgui.ImVec2(0.5, 0.5))
        imgui.SetNextWindowSize(imgui.ImVec2(220, 200), imgui.Cond.FirstUseEver)
        imgui.Begin("Main Window", renderWindow)
        imgui.Text("Hello")
        
        giflib.draw('moonloader/resource/demo.gif', 256, 256)
        
        imgui.End()
    end
)

function main()
    addEventHandler('onWindowMessage', function(msg, wparam, lparam)
        if msg == wm.WM_KEYDOWN or msg == wm.WM_SYSKEYDOWN then
            if wparam == vkeys.VK_X then
                renderWindow[0] = not renderWindow[0]
            end
        end
    end)
    wait(-1)
end

Описание тоже написано нейросетью​

Что она делает​

  • Загружает GIF через Windows GDI+ (читает кадры, задержки между кадрами)
  • Каждый кадр конвертирует во временный PNG “в памяти”
  • Создает из этого PNG текстуру для mimgui (CreateTextureFromFileInMemory)
  • Рисует текущий кадр через giflib.draw(path, w, h)
  • Подгружает кадры по одному “за кадр”, чтобы не было фриза

Где хранится то, что “декодировано”​

PNG-файлы на диск не пишутся.
Кадр сохраняется в PNG-байты как Lua-строка только на момент создания текстуры, потом эта строка становится не нужна и уйдет сборщиком мусора.

Основная память уходит в:
  • VRAM: массив текстур (по одной на кадр)
  • RAM: таблицы Lua с метаданными (кадры, задержки, счетчики) и, иногда, зеркала ресурсов драйвера

Как работает кэш​

  • Кэш ключуется по path (один путь = один объект в кэше)
  • Если ты снова рисуешь тот же GIF, он не перечитывается заново, пока не выгружен

Как работает выгрузка​

Когда окно закрыто и giflib.draw(path, ...) перестает вызываться:
  • библиотека через unload_after секунд вызывает imgui.ReleaseTexture(tex) для всех кадров и очищает frames
  • тем самым освобождается GPU-память (на практике драйвер может освободить не “в ту же миллисекунду”, но обычно быстро)

Почему при первом открытии вместо гифки “Loading…”?​

Потому что загрузка идет инкрементально: по 1 кадру за тик (или сколько ты поставишь в load_steps_per_frame). Это специально, чтобы не висла игра.

Почему память не падает мгновенно после закрытия окна?​


  1. стоит таймер unload_after
  2. драйвер GPU и сама игра могут держать свои кэши
  3. Lua GC не обязан прямо сейчас вычищать временные строки/таблицы

Можно выгрузить сразу принудительно?​

Да:
  • giflib.unload(path) для одной
  • giflib.clear() для всего кэша

Как сильно жрет память?​

Зависит почти линейно от frames * width * height. Если гифка 512x512 на 300 кадров, это будет очень жирно.

Обновление?​

Как лимит спадет, то может быть обновлю по вами комментариями.
1770810395246.png
 

Вложения

  • GIFinmimgui.lua
    15 KB · Просмотры: 6
  • GIFinmimgui_demo.zip
    1.6 MB · Просмотры: 3

Орк

Известный
Автор темы
416
358
Возможно стоит сделать подгрузку кадров в отдельном потоке, как я это делал тут: https://www.blast.hk/threads/95117/post-1591507
А я и не видел этого коммента. Не хочу тянуть dll'ку, а на чистом луа хз как сделать асинхронный поток. Ну а кадры и так в потоке подгружаются, чтобы фриза не было.
Помню у библиотеки была проблема с гифками у которых есть альфа, плохо рендерилось.
Ну и только что попробовал, у меня на лаунчере крашит игру при local player = gif.GifPlayer.new(imageData, #imageData)
 
Последнее редактирование:

вайега52

Налуашил состояние
Модератор
2,988
3,108
А я и не видел этого коммента. Не хочу тянуть dll'ку, а на чистом луа хз как сделать асинхронный поток
Попробуй effil, она предлагает нативные потоки, которые и используются в моей реализации (единственное, я что-то ломал в репозитории и не факт, что скомпилируется, но хотя бы пример кода)
 
  • Нравится
Реакции: Орк

Deps

Известный
258
192
Годно, но есть мысль по оптимизации. Сейчас маршалинг с png создает лишний оверхед на проц. У тебя по сути происходит кодирование на стороне gdi+, а потом mimgui его снова декодирует

Если заюзать GdipBitmapLockBits, можно прокидывать сырой буфер пикселей сразу в текстуру D3D через LockRect. На выходе получишь тот же указатель для imgui, но без лишних аллокаций и нагрузки на проц

 
  • Влюблен
Реакции: Орк

Grafffik

Известный
8
1
«Круто, что реализовал воспроизведение GIF-анимаций в mimgui на Lua без внешних DLL! Очень практично для интерфейсов в модах/сриптах — особенно когда хочется добавить живости UI, а не тянуть тяжёлые зависимости. Но стоит внимательно следить за потреблением памяти при больших GIF-ках (VRAM + таблицы кадров), и возможно действительно имеет смысл подумать об async-подгрузке кадров как предложили в обсуждении — это могло бы убрать лишние тормоза и увеличить отзывчивость интерфейса.»

Комментарий написан нейросетью
 

Орк

Известный
Автор темы
416
358
Годно, но есть мысль по оптимизации. Сейчас маршалинг с png создает лишний оверхед на проц. У тебя по сути происходит кодирование на стороне gdi+, а потом mimgui его снова декодирует

Если заюзать GdipBitmapLockBits, можно прокидывать сырой буфер пикселей сразу в текстуру D3D через LockRect. На выходе получишь тот же указатель для imgui, но без лишних аллокаций и нагрузки на проц

Надо тестить. У меня вроде норм.
Обновил:
Используется GdipBitmapLockBits
Изменил логику, теперь не загружаются все кадры, а используется одна текстура, которая обновляется. Потом обновлю тему.
Гифка на 2к кадров вроде отображается нормально
Странно показывать гифку на 2к кадров гифкой на 150 кадров, но всё же
jNUexGy.gif
сама гифка
OYuznDJ.gif



GIFinmimgui
Анимированные GIF в MoonLoader + mimgui без фризов, с авто-кэшем и аккуратным аплоадом в D3D9.

Что делает библиотека

  • []Грузит GIF с диска и достает кадры через GDI+ (LockBits) в 32bpp ARGB буфер.
    [
    ]Не вешает игру при открытии окна: декод и заливка идут пошагово на корутинах.
    []Показывает GIF в mimgui через одну “живую” текстуру D3D9.
    [
    ]Чтобы не было “разрыва картинки” внутри кадра, использует 2 текстуры (front/back): заливает кадр в back, потом делает swap.
    []Авто-кэш: если ты перестал вызывать draw(), гифка сама выгрузится (и RAM, и VRAM), чтобы не жрало память.
    [
    ]Задержки между кадрами берутся из самого GIF (Graphic Control Extension). Если там пусто или нули, есть fallback и дефолт.


API (коротко по функциям)
  • giflib.draw(path, w, h)
    Рисует GIF в текущем окне ImGui. Если гифка еще не загружена, запускает загрузку и пишет “loading…”.
    path это путь к .gif (строка). w/h опционально, иначе берется размер гифки.

  • giflib.preload(path)
    Начинает загрузку заранее (до открытия окна), чтобы потом появлялось быстрее.

  • giflib.unload(path)
    Принудительно выгружает конкретную гифку (текстуры + буферы).

  • giflib.clear()
    Выгружает вообще всё, что было в кэше.

  • giflib.set_active(true/false)
    Глобальный рубильник. Выключил и всё сразу выгрузилось. Удобно, когда ты точно знаешь, что GIF больше не нужен.

  • giflib.stats(path)
    Диагностика по конкретной гифке: состояние, размеры, сколько кадров декодировано, источник задержек, оценка RAM/VRAM и т.д.



Настройки (giflib.config)

  • []unload_after_sec - через сколько секунд без draw() выгружать гифку
    [
    ]cancel_load_after_sec - через сколько секунд без draw() отменять загрузку (если она еще идет)
    []tick_budget_ms - сколько времени в кадр можно тратить на корутины
    [
    ]resume_max - сколько раз максимум резюмить корутины за кадр
    []upload_rows_chunk - сколько строк копировать в текстуру за один шаг (меньше = мягче, больше = быстрее)
    [
    ]decode_rows_chunk - сколько строк декодировать за один шаг
    []default_delay_sec - если в GIF delay=0 или задержки нет
    [
    ]min_delay_sec - ограничение, чтобы гифка не стала слишком “фпсной”



Пример использования
Lua:
local imgui = require 'mimgui'
local giflib = require 'GIFinmimgui'


local wnd = imgui.new.bool(false)
local gifPath = 'moonloader/resource/demo.gif'


imgui.OnFrame(function() return wnd[0] end, function()
imgui.Begin('GIF demo', wnd)

giflib.draw(gifPath, 256, 256)

imgui.End()
end)


function main()
while true do
wait(0)
if isKeyJustPressed(0x7A) then -- F11
wnd[0] = not wnd[0]
if wnd[0] then
giflib.preload(gifPath)
giflib.set_active(true)
else
-- можно и не вызывать, оно само выгрузит через unload_after_sec
-- но если хочешь моментально:
-- giflib.unload(gifPath)
end
end
end
end


FAQ
В RAM: каждый кадр это буфер wh4 байта (ARGB 32bpp). Плюс 2 D3D9 текстуры (front/back) в VRAM примерно wh4*2.


  1. Быстро внутри игры: вызови stats().
    Lua:
    local st = giflib.stats(gifPath)
    if st then
    print('state='..tostring(st.state))
    print('RAM est bytes='..tostring(st.ram_bytes_est))
    print('VRAM est bytes='..tostring(st.vram_bytes_est))
    print('delay_src='..tostring(st.delay_src))
    end
  2. По процессу целиком: Диспетчер задач / Process Explorer. Но там это будет общая память игры, поэтому удобнее сравнивать “до/после” загрузки гифки.

В GIF задержка хранится в сотых долях секунды. Часто в файлах delay=0 или слишком маленький, а некоторые плееры “приукрашивают” поведение. В таких случаях библиотека подставляет default_delay_sec и ограничивает минимальную задержку.


Если обновлять одну и ту же текстуру частями и параллельно рисовать ее, кадр может попасть на экран недозалитым (верх новый, низ старый). Поэтому используется front/back: на экран идет только полностью готовый кадр.


Да, тут нет сторонних dll. Используются системные вещи Windows (GDI+, D3D9 через текущий device). Основные риски только технические: очень большие GIF могут занять много RAM/VRAM, и если поставить слишком агрессивные chunk-значения, можно получить микроподлагивания.



Коротко про производительность

  • []CPU грузится в двух местах: декод (GDI+ LockBits) и заливка в текстуру (LockRect + copy).
    [
    ]Корутины режут работу на шаги, чтобы не было фризов.
  • Двойной буфер текстуры убирает “tearing” внутри самой гифки.
[/spoiler]
 

Вложения

  • GIFinmimgui.lua
    26.2 KB · Просмотры: 1
  • Нравится
Реакции: Deps