Jak testować manipulacje graficzne na teksturach w pamięci?

1

Robię moduł, który rysuje po teksturach w pamięci (przez "teksturę" mam na myśli zwykły bufor, ciąg bajtów z danymi o poszczególnych pikselach. A "rysowanie" to ustawienie konkretnych bajtów. Nic wspólnego z renderingiem na tym etapie).

Moduł ten będzie miał funkcje potrzebne do zaimplementowania edytora graficznego np.

  • rysuj linię
  • rysuj prostokąt
  • rysuj kółko
  • rysuj coś specjalnym pędzlem

I zastanawiam się, jak to testować/

O ile pewne rzeczy będą trywialne - np. rysuj prostokąt - to sprawdzę, czy są odpowiednie piksele ustawione na wyjściu (tak, żeby uformowały kształt prostokąta)

To przy rysowaniu linii już może być zagwozdka. Zakładam, że linie mogą być pochylone w różnych kierunkach. A to rodzi potrzebę zastosowania jakiegoś algorytmu do ich rysowania. Ja dość naiwnie zamierzam po prostu policzyć różnicę współrzędnych x, y między 2 punktami i zaczynając od pierwszego punktu, w każdym kroku będę dodawać tę różnicę podzieloną przez długość linii.

Jednak - co jeśli zmienię algorytm rysowania linii na algorytm Bresenhama? A co jeśli dodam antyaliasing do linii? Ciężko będzie napisać testy do tego w ten sposób, żeby się nie rozwaliły. Owszem, mogę zrobić "snapshot" i testować, czy dana linia wygląda tak samo jak wcześniej, ale taki snapshot przy zmianie implementacji będzie do wyrzucenia. Podobnie rysowanie specjalnym pędzlem. Wystarczy, że lekko zmienię parametry i piksele będą całkiem inne.

Może w ogóle dać sobie spokój z pisaniem unit testów do tego i testować to wszystko ręcznie, patrząc, czy wygląda dobrze? Tylko, że to się sprawdzi na początku, a później będę miał dużo tych narzędzi i ciężko będzie co chwila wszystko przeklikiwać.

Może więc zastosować jakiś przybliżoną metodę testowania?

Np. załóżmy, że mam teksturę o wymiarach 1024x1024, to nie sprawdzam czy każdy z ponad miliona bajtów jest taki sam, tylko raczej dzieliłbym tę teksturę na mniejsze części (na kafelki, taką szachownicę zrobić) i dla każdego kafelka liczyć ile pikseli ma zapalonych (dla uproszczenia załóżmy tylko 2 kolory) i jeśli liczba pikseli w kafelku jest większa niż wartość progowa, wtedy kafelek jest ustawiony jako 1, jeśli nie, to 0.

Wtedy potencjalnie jakbym zmienił algorytm rysowania czy parametry pędzla, to trochę by się to zmieniło, ale jednak na zmniejszonej dokładności do kafelka, okazałoby się, że kafelki są jednak takie same wciąż i nie trzeba przepisywać testów?

Czyli podsumowując. Zastanawiam się, czy najlepiej testować te rzeczy:

  • ręcznie?
  • za pomocą snapshotów?
  • za pomocą podzielenia na kafelki i zmniejszenia dokładności ?
  • jeszcze inaczej?

Ktoś miał może do czynienia z tego typu problemem albo ma jakieś pomysły?

1

Czy to rysowanie linii, prostokątów i kółek to jest całe zachowanie samo w sobie, czy jest tylko pomniejszym elementem jakiegoś systemu (np edytora grafik wektorowych?).

Bo jeśli większego, to jest też pomysł, żebyś w teście narysował odpowiednik spodziewanych wartości; i znalazł biblioteke która porównuje obrazy - takich akurat jest bardzo dużo, i ustawić odpowiedni treshold.

Czyli zamiast "parsować" obraz i robić na nim asercje, możesz spróbować stworzyć obraz i porównać.

0
Riddle napisał(a):

Czy to rysowanie linii, prostokątów i kółek to jest całe zachowanie samo w sobie, czy jest tylko pomniejszym elementem jakiegoś systemu (np edytora grafik wektorowych?).

Będzie to edytor grafiki (ten moduł, o którym piszę, będzie robił grafikę rastrową). Piszę to w Rust i JS.

Część rustowa odpowiada za logikę i manipulacje danymi w buforach, a część JSowa odbiera bufory z pamięci WebAssembly i przekazuje je do WebGL, żeby je wyrenderować na ekranie.

Bo jeśli większego, to jest też pomysł, żebyś w teście narysował odpowiednik spodziewanych wartości; i znalazł biblioteke która porównuje obrazy - takich akurat jest bardzo dużo, i ustawić odpowiedni treshold.

O, to też może być dobry pomysł. Tylko znaleźć dobrą bibliotekę. Może w ekosystemie Cypress coś się znajdzie?
np. to https://github.com/cypress-visual-regression/cypress-visual-regression
Z drugiej strony to wymagałoby odpalania Cypressa, plus byłoby zależne od sposobu renderowania, więc jeśli test by sfailował, to nie wiadomo, czy dlatego, że renderer jest zepsuty, czy dlatego, że moduł do edycji grafiki jest zepsuty.

0
LukeJL napisał(a):

O, to też może być dobry pomysł. Tylko znaleźć dobrą bibliotekę. Może w ekosystemie Cypress coś się znajdzie?
np. to https://github.com/cypress-visual-regression/cypress-visual-regression

Korzystałem kiedyś z tego, i działa całkiem spoko. Do wizualnej regresji na stronie się ciężko nadaje (bo np przesunięcie przycisku pół piksela w lewo failowało), ale w Twoim przypadku przesunięcie może symbolizować jakiś niepotrzebny +1, -1, więc może to dobry pomysł.

Z drugiej strony to wymagałoby odpalania Cypressa, plus byłoby zależne od sposobu renderowania, więc jeśli test by sfailował, to nie wiadomo, czy dlatego, że renderer jest zepsuty, czy dlatego, że moduł do edycji grafiki jest zepsuty.

No tak, odpalanie cypresa faktycznie może być ciężkie. Ale tym failem bym się nie przejmował, bo możesz pokazać przecież różne błędy - tzn jak cypress się odpali i zjadzie buga to rzucasz wyjątek asercji; a jak nie uda się odpalić cypressa, to możesz rzucić zwykły wyjątek albo zcrashować runner; tak czy tak to zrozróżnisz.

Nie wiem jak w runnerach od rusta, ale we wszelkich junit, xunit, phpunit, jest; błąd asercji i nieobsłużony wyjątek pokazują się inaczej.

@LukeJL teraz jeszcze pomyślałem o dodatkowej sprawie - jeśli wchodzisz w taki temat, to coś co chyba najbardziej pomoże Ci napisać testy pod to, to jest znaczne zapoznacie się z tematem rysowania takich grafik wektorowych. ostatecznie to nie technologia decyduje o tym jak Ci to wyjdzie tylko Twoje skille, więc pewnie bez tego się nie obędzie - pierwszy krok to pewnie byłoby nauczenie się pisania algorytmów generowania takiej linii.

0

A dlaczego po prostu nie zrobisz zapiszesz sobie tego w jakimś .bmp , nie wczytasz i zwyczajnie nie sprawdzisz, czy wynik jest zgodny z oczekiwaniami? Niestety musisz sprawdzić całą "teksturę", bo jest szansa, że funkcja, która ma ci narysować kółko, wpłynie na coś jeszcze dookoła, albo wywołanie iluś tam funkcji rysujących różne rzeczy zacznie ze sobą interferować.
Masz gotowe biblioteki do porównywania obrazków, gdzie możesz ustawić prób zgodności https://www.screenster.io/image-comparison-tool/
Bo prawdziwy problem to to, że przy nieco bardziej odjechanych renderowaniach (np. tekst) wynik może się różnić w zależności od humorów narzędzia renderującego (jakieś tam OpenGL, przeglądarki itd.).

Możliwe, że są jakieś super lepsze sposoby, ale pracując w projekcie, gdzie przetwarzaliśmy dane z DB do postaci graficznej, jedyne co nam przyszło do głowy, to wygenerowanie wyników testów, ręczne ich przejrzenie pod katem poprawności i użycie jako wzorców w asercjach. Nie było to idealne rozwiązanie, bo zmiany w kodzie powodowały wysypywanie się testów, do tego dochodziły humory OpenGL, a musieliśmy porównywać obraz z dokładnością 100%, bo niestety nawet niewielkie różnice mogły spowodować skutki katastroficzne, ale to najlepsze, co przyszło nam wtedy do głowy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1