Sceny animacji zapisują się teraz tak, jak powinny były od początku
Jest taka klasa problemów, których użytkownik nigdy nie zgłasza, bo nie wygląda ona jak błąd. Program po prostu jest wolniejszy, niż powinien, w momencie, w którym akurat coś rysujesz. Nie ma komunikatu, nie ma awarii, nie ma czego wpisać do zgłoszenia. Jest tylko to drobne szarpnięcie co minutę, do którego się przywyka.
Tak właśnie zachowywały się do dziś sceny animacji w Moonlight Animate.
Dlaczego zapis sceny trwał tak długo
Scena animacji była zapisywana jako archiwum — jeden spakowany plik zawierający opis sceny i wszystkie jej materiały. Problem z archiwum jest taki, że nie da się w nim czegoś podmienić. Żeby zmienić jedną rzecz, trzeba zbudować całe archiwum od nowa.
W praktyce, przy każdym automatycznym zapisie, program: przekodowywał każdą klatkę rysunku na pełnowymiarowy obraz, kopiował wszystkie materiały, i pakował całość. Przy scenie z dwoma tysiącami rysunków zajmowało to ponad sześć minut. Autozapis, który miał chronić przed utratą pracy, sam stawał się powodem, żeby go wyłączyć.
Do tego dochodziły dwie rzeczy mniej widoczne, ale gorsze w skutkach. Po pierwsze, kopia awaryjna była jedna, wspólna dla całego programu — dwie otwarte sceny nadpisywały sobie nawzajem jedyny ratunek po awarii. Po drugie, każda klatka szła przez obraz o ośmiu bitach na kanał, więc miękkie przejścia i szeroka gama kolorów wracały z pliku zubożone. Round-trip był stratny, choć nic o tym nie mówiło.
Co się zmieniło
Scena zapisuje się teraz do kontenera bazodanowego — pliku, w którym da się zmienić jedną rzecz, nie ruszając reszty. Brzmi to technicznie, ale skutek jest bardzo praktyczny:
Zapis dopisuje wyłącznie to, co się zmieniło. Narysowanie jednej kreski kosztuje tyle samo w scenie z trzema klipami, co w scenie z trzystoma. Zmierzyliśmy to na trzech rozmiarach sceny: 20, 34 i 43 milisekundy. Dla porównania pełny zapis największej z nich to 24 sekundy. To jest cała różnica, dla której ta praca powstała.
Rysunki zapisują się bez strat. Wracają dokładnie takie, jakie były — z pełną gamą i bez pasm na gradientach. Ceną jest większy plik i mówimy o tym wprost, bo to był świadomy wybór: wolimy oddać piksele w stanie nienaruszonym.
Kopia awaryjna jest osobna dla każdej sceny. Dwie sceny otwarte naraz mają dwie niezależne kopie. Po awarii program proponuje odzyskanie każdej z nich, z czasem ostatniego zapisu czytanym z wnętrza kopii — nie z daty pliku, która przy tego rodzaju zapisie potrafi pokazywać godzinę sprzed całej ostatniej sesji.
Plik jest samowystarczalny. Można go wysłać komuś bez folderów obok: filmy, obrazy i muzyka siedzą w środku. Miniatura podglądu też, więc scena skopiowana z innego komputera od razu pokazuje podgląd na ekranie startowym. Ten sam materiał użyty przez kilka klipów zapisuje się raz — plik nie rośnie trzykrotnie, gdy to samo wideo jest w trzech miejscach osi czasu.
Stare sceny otwierają się normalnie. Format rozpoznawany jest po zawartości pliku, nie po rozszerzeniu, więc scena sprzed zmiany działa dalej, a pierwszy zapis przenosi ją na nowy kontener. Dołożyliśmy przy tym odzyskiwanie materiałów, których zapisana ścieżka nie istnieje już na tym komputerze — bajty są w archiwum, tylko nazwa się nie zgadza.
Doszła też jedna nowa pozycja w menu: Plik → Kompaktuj scenę. Usuwa z pliku dane, do których scena już się nie odwołuje — na przykład poprzednie wersje podmienionych materiałów — i realnie go zmniejsza. Pyta o potwierdzenie i ostrzega wprost, bo cofnięcie zmian po tej operacji nie przywróci usuniętych materiałów. Dlatego zwykły zapis nigdy nie robi tego sam.
Część, o której zwykle się nie pisze
Po napisaniu tej zmiany przeprowadziliśmy jej adwersaryjny przegląd — czyli świadomą próbę znalezienia w niej dziur, prowadzoną tak, jakby celem było ją zdyskredytować, a nie pochwalić. Znalazł trzydzieści jeden rzeczy. Wszystkie są naprawione, ale cztery warto wymienić, bo każda kończyła się utratą pracy, i żadnej nie widać z zewnątrz.
Główna teza tej zmiany nie działała w programie — działała tylko w pomiarze. Każda kreska zapisywała listę zmienionych fragmentów, po czym sygnał „odśwież widok" kasował tę listę i kazał przepisać cały rysunek klipu. Pomiar wydajności tego nie widział, bo ustawiał dane testowe ręcznie, z pominięciem całego okablowania. Liczby były prawdziwe dla tego, co zmierzono, i nieprawdziwe jako opis programu.
„Zachowaj kopię" potrafiło skasować odzyskaną pracę. Gdy plik docelowy był zajęty przez inny program, użytkownik dostawał komunikat o zajętym pliku — i nie wiedział, że kopia awaryjna właśnie przestała istnieć. Nie zrobił nic złego.
Otwarcie drugiej sceny zostawiało w kopii awaryjnej rysunki pierwszej. Opis był już nowej sceny, zawartość — starej.
Zabezpieczenie przed przeoczoną zmianą było tak sformułowane, że w realnej pracy nie odpalało nigdy. Zastąpiliśmy je licznikiem, który porównuje stan rysunków tuż przed zapisem: ścieżka, która zapomni zgłosić zmianę, i tak trafia do pliku. Jest na to test, który psuje kod celowo i sprawdza, że praca mimo to ocalała.
A na koniec rzecz, której przegląd nie znalazł, bo nie dało się jej wyczytać z kodu — znalazł ją dopiero test porównawczy, zapisujący tę samą scenę oboma formatami i czytający z powrotem. Czytnik starego archiwum nigdy nie odtwarzał rysunków na celach. Zapis działał bez zarzutu, każda klatka lądowała w pliku. Odczyt pytał o klatki, które „już mają obraz w pamięci" — a w trakcie wczytywania nie ma żadnej. Lista była pusta, pętla nie wykonywała się ani razu, rysunki znikały po cichu. Plik zawierał je do końca; nikt po nie nie sięgał. To też jest naprawione.
Co z tego wynika
Dla osoby rysującej: autozapis przestał być czymś, co się czuje. Rysunki wracają takie, jakie były. Dwie otwarte sceny nie odbierają sobie nawzajem szansy na odzysk. A scena wysłana komuś działa u niego bez dosyłania folderów.
Dla nas: potwierdzenie zasady, którą warto powtarzać. Zielony test nie znaczy nic, dopóki nie sprawdzisz, że potrafi zapłonąć. Cztery najpoważniejsze błędy z tej listy siedziały dokładnie tam, gdzie testy sprawdzały mechanizm, zakładając, że dostał prawdę — a nie dostawał.