Optymalizacja 9175 17

O temacie

Autor inż. Avallach

Zaczęty 6.12.2014 roku

Wyświetleń 9175

Odpowiedzi 17

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
[to nie jest pytanie ani problem, po prostu ogólna dyskusja]

Stało się właśnie coś tak zaskakującego że aż muszę się z tym podzielić. Tworzę aplikację która na podstawie serii zdjęć z jakiegośtam rezonansu ma zrobić model 3d. Metodę ładującą zdjęcia napisałem w parę minut, ale dziś pomyślałem że pobawię się w jej optymalizację. Testowałem różne podejścia, nierzadko dostając mało oczywiste, ale niewielie wzrosty wydajności (rzędu 10%). Między innymi mam taką metodę:
private void LoadSlice(int z)
{
byte[] bytes = File.ReadAllBytes(_filePaths[z]);
Texture2D texture = new Texture2D(0, 0, TextureFormat.ARGB32, false);
texture.LoadImage(bytes);
Color32[] pixels = texture.GetPixels32();
for (int x = 0; x < texture.width; x++)
for (int y = 0; y < texture.height; y++)
voxels[x, y, z] = GetAlpha32(pixels[x + y * texture.width]);
}
Jak widać, obiekt texture od połowy jest właściwie niepotrzebny, bo dane o pixelach są przekonwertowane na prostą tablicę 1d (odwoływanie się do niej jest nieco szybsze). Nie jestem pewien czy kompilator / maszyna wirtualna to zauważają i od razu zwalniają pamięć przez nią zajmowaną. Spróbowałem zrobić coś takiego:
private void LoadSlice(int z)
{
byte[] bytes = File.ReadAllBytes(_filePaths[z]);
Texture2D texture = new Texture2D(0, 0, TextureFormat.ARGB32, false);
texture.LoadImage(bytes);
Color32[] pixels = texture.GetPixels32();
int width = texture.width;
int height = texture.height;
texture = null;
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
voxels[x, y, z] = GetAlpha32(pixels[x+y*width]);
}
Potrafilbyście zgadnąć efekt? Dodatkowa operacja (sugerująca zwalnianie pamięci) powinna nie wpłynąć na wydajność (co najwyżej prawie niezauważalnie ją zmniejszyć), za to przy wielowątkowym wykonywaniu mogłaby nieco zmniejszyć średnie zużycie pamięci. Pamięci w końcu nie sprawdzałem, ponieważ... algorytm przyspieszył ponad dwukrotnie. Wcześniej byłem pewien że większość czasu schodziła na czytanie plików i iterowanie po wszystkich pixelach (obecnie 32 pliki 512x512 > 8 milionów iteracji) i nie da się tego przyspieszyć inaczej niż zrównoleglając.
W ramach eksperymentu usunąłem linijkę texture = null;. Czas nadal był ten sam (dwa razy niższy niż początkowo). Okazało się że boost wynikł z linijek:
int width = texture.width;
int height = texture.height;
Być może są to propercje obliczane w jakiś kosztowny sposób, nie wiem.

Morał: jeśli czytasz coś > 8 mln razy i nie jesteś pewien czy to zwykły odczyt zmiennej, spróbuj cache'owania :D

Adanos

Adanos

Administrator
Szara eminencja
posty5204
Propsy3870
ProfesjaProgramista
  • Administrator
  • Szara eminencja
Wydaje mi się, że może to być spowodowane tym, że dostęp do obiektów jest wolniejszy niż do zmiennych lokalnych. Aby dostać się do pola obiektu, to najpierw trzeba odnaleźć, gdzie się znajduje obiekt w pamięci i wyszukać dane pole. Ze zmiennymi lokalnymi typu prostego nie ma tego problemu, od razu odczytujemy wartość. Ale tu powinien się wypowiedzieć jakiś specjalista od C++, oni zajmują się pamięcią. :D

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
(tu spec od c++)
To co piszesz jest prawdą, zwłaszcza w językach zarządzanych gdzie narzut od takich operacji jest większy (a .NET to właśnie języki zarządzane, tutaj C#). Wydaje mi się jednak że i tak nie powinno zajmować to aż tak wielkiej ilości czasu (w przybliżeniu tyle co ładowanie obrazów png z dysku do tekstury).


Tu problemem jest nie sam dostęp do pola obiektu, ale to że to co wygląda jak pole jest w rzeczywistości propercją która wywołuje kolejną metodę, a z kolei ta metoda to wywołanie funkcji natywnej w c++, która zapewne dopiero czyta odpowiednie pole itd. Trzeba po prostu pamiętać że nawet tak niewinny kod jak int width = texture.width;
może w C# ukrywać całkiem sporą ilość roboty dla procesora. Tutaj w najbardziej optymistycznym przypadku tego "ukrytego" kodu jest tyle:

public class Texture : Object
{
   public virtual int width
   {
      get
      {
         return Texture.Internal_GetWidth(this);
      }
     
...
     
   [WrapperlessIcall]
   [MethodImpl(MethodImplOptions.InternalCall)]
   private static int Internal_GetWidth(Texture mono);
     
...
   //c++
   int Texture::Internal_GetWidth(Texture* mono)
   {
      return mono->width;
   }
Przy czym funkcja C++ na dole może mieć go nieco więcej (dwie pierwsze są znane na pewno, bo C# w odróżnieniu od C++ ładnie się dekompiluje).


No, w każdym razie dzięki cache'owaniu, funkcyjnemu stylowi i zrównolegleniu tego co mogłem (bo kodu który używa klas Unity nie można) udało mi się przyspieszyć ładowanie ponad 6x :D
Gdyby Unity w pełni wspierało multithreading (i nowszego .NET Frameworka) to pewnie dałoby się jeszcze ze dwa razy.

mgr Fartuess

mgr Fartuess

Użytkownicy
Kiedyś to były czasy!
posty1485
Propsy890
ProfesjaProgramista
  • Użytkownicy
  • Kiedyś to były czasy!

mgr Fartuess

Optymalizacja
#3 2014-12-07, 13:12(Ostatnia zmiana: 2014-12-07, 13:22)
Najprawdopodobniej wywołania, operacje na stosie spowodowane wywoływaniem gettera sprawdzającego rozmiar tekstury co piksel miały taki gigantyczny narzut. Lepiej co każdy piksel po prostu sprawdzać wartość pod podanym adresem pamięci, a nie napierdalać operacje na stosie :D. Jakbyś chciał zoptymalizować to wszystko pamięciowo, to w C# można pisać metody/klasy "unsafe" w których pisze się prawie jak w C++ - wskaźniki i manualne zarządzanie pamięcią. Pewnie mógłbyś od razu zwalniać pamięć po texture, a nie czekać na garbage collectora (chyba, że C# ogarnia, że jest to zmienna lokalna nie używana poza scopem wywołanej funkcji i traktuje to jako pamięć stosu).

Co do samego projektu... Bierzesz zdjęcia i po prostu wrzucasz w siatkę wokselową i dajesz do renderingu?
 
Popisuje się ciągle menda jedna...

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator

inż. Avallach
Administrator

Optymalizacja
#4 2014-12-07, 13:30(Ostatnia zmiana: 2014-12-07, 13:53)
Zależy co rozumiesz przez "siatka voxelowa". Generalnie - tak, ale to dopiero początkowa funkcjonalność aplikacji, właściwie dodatek do tego co ma robić przede wszystkim.

1. Ładuję zdjęcia do tablicy 3d byte[,,] (każda komórka mówi o stopniu nieprzezroczystości, te zdjęcia to coś w stylu rentgena)
2. Dla każdego z chunków 8x8x8 generuję mesh (gdzie każdy voxel to jeden sześcian o ustalonej przezroczystości)
3. Chunki dodaję jako obiekty do sceny w Unity

Wygląda to tak:

Pierwsze to widok czaszki z góry, drugie to przekrój z boku. Kamera jest na razie całkowicie wolna, jest do tego suwak pozwalający wybrać jak blisko od kamery ma się zaczynać ewentualny przekrój.

Optymalizacja jest tutaj nietrywialna. Tak jak pisałem wcześniej, przy 32 zdjęciach 512x512 px dostajemy ponad 8 milionów voxeli. Każdy z nich to sześcian, tak więc 16 milionów wierzchołków / 96 milionów tris.
Nie byłby to problem dla Unity gdyby nie to że z bardzo rozsądnych powodów ilość wierzchołków na mesh jest limitowana do 64k.

Dlatego:
 - obrazy ładuję wielowątkowo
 - napisałem minimalistyczny shader obsługujący takie "rentgenowe" półprzejrzyste galarety
 - nie generuję przezroczystych / prawie przezroczystych voxeli
 - używam stworzonej z góry puli współdzielonych materiałów dla voxeli o takiej samej alphie (zamiast ustalania przezroczystości każdemu voxelowi z osobna)
 - dzielę model na chunki 8x8 tak żeby mieć gwarancję że żaden nie przekroczy limitu
 - od razu niszczę chunki które okazały się puste (jest ich trochę)
 - ładuję asynchronicznie (zamiast zawiechy, użytkownik widzi na żywo jak model się buduje)

Obecnie ładowanie trwa ~10s, a załadowany model renderuje się z 40+ fps.
Problemem jest tutaj już overhead idący z Unity, którego mogę zredukować jedynie redukując ilość wywołań kosztownych funkcji Unity - a taką jest tworzenie obiektu na scenie (tutaj powtarzane kilkanaście tysięcy razy).
Biorę pod uwagę jeszcze jedną optymalizację: współdzielenie wierzchołków między voxelami z jednego chunka. Pozwoliłoby to na ok 7 krotne zredukowanie ich ilości, przez to umożliwiło korzystanie z większych chunków, a to z kolei korzystanie z mniejszej ilości chunków. Z drugiej strony trzeba by poświęcić dodatkowe obliczenia na sprawdzanie czy akurat potrzebne wierzchołki już istnieją i znajdywanie ich indeksów - ale to, jakkolwiek złożone od strony kodu, nie powinno stanowić wyzwania dla procesora.

Bloki unsafe znam, ale tutaj nic nie pomogą.
C# ogarnia dealokację pamięci bardzo dobrze, nie byłem tylko pewien czy bierze ją pod uwagę od razu po linijce kiedy obiekt był ostatni raz używany, czy dopiero na koniec bloku. W każdym razie ustawienie texture = null nie dało zauważalnych efektów (gc i tak jest odpalany dopiero w razie potrzeby).
Overhead przy czytaniu texture.width wynikał z tego że pod spodem siedział getter i przynajmniej kilka wywołań, w tym jedno C# -> C++.

@Fartuess edytowałem.

mgr Fartuess

mgr Fartuess

Użytkownicy
Kiedyś to były czasy!
posty1485
Propsy890
ProfesjaProgramista
  • Użytkownicy
  • Kiedyś to były czasy!

mgr Fartuess

Optymalizacja
#5 2014-12-07, 15:25(Ostatnia zmiana: 2014-12-07, 15:33)
Wypowiem się w stylu wowoza: "Strasznie chujowo to robisz"

  • texture = null mogło nie dawać zauważalnych efektów, bo wyzerowałeś tylko referencję, a faktyczny obiekt zostanie skasowany dopiero gdy garbage collector stwierdzi, że mu wygodnie.
  • Wygodniej by ci pewnie było to pisać w czystym OpenGLu niż w Unity.
  • Jak masz pizdyliard wokseli, które zżerają ci za dużo pamięci to możesz zamiast trójwymiarowej tablicy wokseli, w których masa komórek  jest pusta, możesz spróbować użyć innej struktury. Nie wiem jak w tym przypadku, ale czasem możesz spróbować użyć jakiegoś typu wektora struktur składających się z alphy i z vec3 przechowującego pozycję. Pamięciowo było by to opłacalne dopiero od pewnego progu, bo trzeba by przechowywać vec3, ale 3 inty / shorty spokojnie by styknęły. Nie wiem też na ile duży byłby narzut szybkości wynikający z przerzucenia się z tablicy na wektor. Teoretycznie można też zrobić Sparse Octree, ale to już więcej roboty z kodzeniem i dodatkowy narzut wynikający przekonwertowaniem tego na drzewo ósemkowe i potem korzystanie z niego najprawdopodobniej przerośnie zyski.
  • Powinieneś to zrealizować tak, żeby wystarczył ci do wszystkiego jeden Unitowy GameObject (Ludzie wymyślają wzorce takie jak Flyweight, żeby nie mieć niepotrzebnych narzutów wynikających z istnienia masy prawie takich samych obiektów).
  • Wektor wokseli mógłbyś wrzucić do geometry shadera i z każdego z nich wygenerować wierzchołki. Zamiast masy draw calli miałbyś jeden. Tu można by znów próbować optymalizować wychodząc z założenia, że na raz można widzieć co najwyżej 3 ścianki kostki. Jednak geometry shader jest znacznie wolniejszy od vertex shadera, więc może to działać trochę wolno.
  • Zamiast użycia geometry shadera można użyć vertex shadera i od razu do niego wepchnąć zwielokrotnione dane o wokselach przeplecione dodatkowym atrybutem mówiącym który to jest wierzchołek w kostce. Pytanie tylko czy możesz w unity wywołać własnego ręcznie zmontowanego draw calla własnego shadera dla samodzielnie zbindowanych danych.
Ogólne przesłanie jest takie, że najoptymalniej to można załatwić, gdy przerzuci się całą robotę związaną z przechowywaniem i zarządzaniem całą strukturą na kartę graficzną i ograniczyć wszystko do jedengo obiektu na CPU.
Nie oczekuje od Ciebie tego, ze to tak zrobisz, bo to jednak sporo roboty jest i jeszcze więcej z ogarnięciem co da nam faktyczny zysk. Po prostu dawno nie widziałem tu na forum jakiegoś pocisku, że ktoś coś chujowo robi. Poza tym mało znam osób, którym mogę zaszpanować, że takie rzeczy ogarniam, bo mało kto zrozumie o czym w ogóle gadam i chciałem wykorzystać okazję do pohejtowania i poszpanowania :D. Przekichane jest posiadać bardzo specjalistyczną wiedzę ekspercką. Nawet nie ma się przed kim pochwalić, bo nikt nie rozumie o co chodzi.
 
Popisuje się ciągle menda jedna...

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator

inż. Avallach
Administrator

Optymalizacja
#6 2014-12-07, 15:58(Ostatnia zmiana: 2014-12-07, 16:03)
texture = null mogło nie dawać zauważalnych efektów, bo wyzerowałeś tylko referencję, a faktyczny obiekt zostanie skasowany dopiero gdy garbage collector stwierdzi, że mu wygodnie.
Wiem, przecież dokładnie to napisałem.

Cytuj
Wygodniej by ci pewnie było to pisać w czystym OpenGLu niż w Unity.
Pisałem że chcę robić to wieloplatformowo. W Unity zmiana platformy to kliknięcie. Z OpenGL nie byłoby tak lekko. Poza tym aplikacja ma też interfejs, musiałbym korzystać z jakichś dodatkowych bibliotek. Poza tym Unity jest o tyle wygodniejsze że korzysta z C#, nie muszę się martwić np o czas życia obiektów.

Cytuj
Jak masz pizdyliard wokseli, które zżerają ci za dużo pamięci
To równo 8mb (512*512*32*byte). Wcale nie tak dużo. Już nieporównywalnie więcej zjada mesh (nie mam pojęcia ile ale może to być rząd wielkości).

Cytuj
Powinieneś to zrealizować tak, żeby wystarczył ci do wszystkiego jeden Unitowy GameObject
Tak jak pisałem Unity ma twardy limit 64k wierzchołków na mesh (łącznie z submeshami). Nie da się zawrzeć mesha o potencjalnie nawet 67 108 864 wierzchołkach w jednym GameObjectcie. Chyba że Ty znasz jakiś sposób - strasznie ułatwiłby mi robotę. Na razie jedyne co mi zostało to algorytm redukcji ilości wierzchołków o którym pisałem, ale nawet z nim będzie to kilkaset GameObjectów. Nigdy jeden.

Cytuj
Wektor wokseli mógłbyś wrzucić do geometry shadera i z każdego z nich wygenerować wierzchołki. Zamiast masy draw calli miałbyś jeden. Tu można by znów próbować optymalizować wychodząc z założenia, że na raz można widzieć co najwyżej 3 ścianki kostki. Jednak geometry shader jest znacznie wolniejszy od vertex shadera, więc może to działać trochę wolno.
Cytuj
Zamiast użycia geometry shadera można użyć vertex shadera i od razu do niego wepchnąć zwielokrotnione dane o wokselach przeplecione dodatkowym atrybutem mówiącym który to jest wierzchołek w kostce. Pytanie tylko czy możesz w unity wywołać własnego ręcznie zmontowanego draw calla własnego shadera dla samodzielnie zbindowanych danych.
Cytuj
Ogólne przesłanie jest takie, że najoptymalniej to można załatwić, gdy przerzuci się całą robotę związaną z przechowywaniem i zarządzaniem całą strukturą na kartę graficzną i ograniczyć wszystko do jedengo obiektu na CPU.
Ze wszystkimi trzema się całkowicie i absolutnie zgadzam. Nie wiem jak z fps, ale czas ładowania na pewno by drastycznie spadł bo większość obliczeń dałoby się zrównoleglić. Są tylko trzy problemy:
 - czy wierzchołki generowane po stronie GPU na pewno nie podlegają normalnemu limitowi 64k?
 - czy geometry shader nie podwyższył by ostro wymagań aplikacji?
 - czy z vertex shaderem, czy geometry shaderem, zwyczajnie nie potrafię realizować takich algorytmów na gpu :D

Dotąd widziałem równo jeden artykuł o proceduralnym generowaniu mesha do Unity po stronie GPU i była to dla mnie czarna magia xD (chociaż to zamiast prostych voxeli były marching cubes, które robione gdziekolwiek są czarną magią). Jak masz jakieś przykłady to chętnie ogarnę. Na razie nie jestem pewien czy jest sens to robić - to zadanie na uczelnię na grafikę komputerową (tak czy inaczej wykraczające poza to co robią inni). Na pewno przydałoby się to gdyby aplikacja miała znaleźć konkretne zastosowania komercyjne.


Nie jestem pewien jak miałoby wyglądać ręczne wołanie draw calli. Najbardziej niskopoziomowe api do którego Unity daje dostęp i które kojarzę to klasa GL ("Low-level graphics library") (http://docs.unity3d.com/ScriptReference/GL.html).

mgr Fartuess

mgr Fartuess

Użytkownicy
Kiedyś to były czasy!
posty1485
Propsy890
ProfesjaProgramista
  • Użytkownicy
  • Kiedyś to były czasy!

mgr Fartuess

Optymalizacja
#7 2014-12-07, 22:36(Ostatnia zmiana: 2014-12-07, 22:45)
texture = null mogło nie dawać zauważalnych efektów, bo wyzerowałeś tylko referencję, a faktyczny obiekt zostanie skasowany dopiero gdy garbage collector stwierdzi, że mu wygodnie.
Wiem, przecież dokładnie to napisałem.
...a jeśli zrobiłbyś to w trybie unsafe i usuwał obiekt manualnie to mógłbyś zauważyć efekty.

Cytuj
Wygodniej by ci pewnie było to pisać w czystym OpenGLu niż w Unity.
Pisałem że chcę robić to wieloplatformowo. W Unity zmiana platformy to kliknięcie. Z OpenGL nie byłoby tak lekko. Poza tym aplikacja ma też interfejs, musiałbym korzystać z jakichś dodatkowych bibliotek. Poza tym Unity jest o tyle wygodniejsze że korzysta z C#, nie muszę się martwić np o czas życia obiektów.
Niby prawda. Sam OpenGL powinien być w miarę wieloplatformowy, a na pewno dałoby się ogarnąć jak napisać aplikację, która w zależności od wyboru kompilowanej platformy by się dostosowywała i odpowiednio kompilowała. Interfejs załatwił bym biblioteką Qt, która ma własny kontekst OpenGLa i jest multiplatformowa. Zarządzanie czasem życia obiektów w przypadku takiej aplikacji nie powinno być takie trudne. Głównie z tą wygodą miałem na myśli to, że mógłbyś ręcznie grzebać i bawić się buforami i shaderami tak by uzyskać zamierzone efekty. Ale fakt jest taki, że to jest wygodne dopiero gdy się jest w tym dobrym. Jeśli jest to coś nowego, to zdecydowanie nie jest to tego warte, chyba że chcesz poświęcić na projekt prawie pół roku :P.
Swoją drogą ta mulitplatformowość miała by obejmować jedynie platformy desktopowe?

Cytuj
Jak masz pizdyliard wokseli, które zżerają ci za dużo pamięci
To równo 8mb (512*512*32*byte). Wcale nie tak dużo. Już nieporównywalnie więcej zjada mesh (nie mam pojęcia ile ale może to być rząd wielkości).
Zgubiłeś jeden wymiar. 512x512x512 * 4 bajty (na alfę) = 512MB. Ale pewnie już samych przekrojów nie masz 512stu więc będzie mniej. Tego byś już nie dał rady przerzucić na słabsze GPU z jednym wywołaniem shadera. A samą siatkę w OpenGLu można po przerzuceniu na GPU wyrzucić z CPU, bo już ne jest potrzebna, skoro GPU ma ją w swojej pamięci.

Cytuj
Powinieneś to zrealizować tak, żeby wystarczył ci do wszystkiego jeden Unitowy GameObject
Tak jak pisałem Unity ma twardy limit 64k wierzchołków na mesh (łącznie z submeshami). Nie da się zawrzeć mesha o potencjalnie nawet 67 108 864 wierzchołkach w jednym GameObjectcie. Chyba że Ty znasz jakiś sposób - strasznie ułatwiłby mi robotę. Na razie jedyne co mi zostało to algorytm redukcji ilości wierzchołków o którym pisałem, ale nawet z nim będzie to kilkaset GameObjectów. Nigdy jeden.
No to fakt, że jakieś dzielenie będzie potrzebne w takim przypadku. Chyba, że uda ci się to ograniczenie w jakiś sposób zdjąć. Może w unity się gdzieś da to wyłączyć, bo to raczej nie jest ograniczenie po stronie możliwości shaderów. W internecie narzekąją na problemy w OpenGL ES (mobilki), ale dopiero gdy dochodzę do 2m vertów.

Cytuj
Wektor wokseli mógłbyś wrzucić do geometry shadera i z każdego z nich wygenerować wierzchołki. Zamiast masy draw calli miałbyś jeden. Tu można by znów próbować optymalizować wychodząc z założenia, że na raz można widzieć co najwyżej 3 ścianki kostki. Jednak geometry shader jest znacznie wolniejszy od vertex shadera, więc może to działać trochę wolno.
Cytuj
Zamiast użycia geometry shadera można użyć vertex shadera i od razu do niego wepchnąć zwielokrotnione dane o wokselach przeplecione dodatkowym atrybutem mówiącym który to jest wierzchołek w kostce. Pytanie tylko czy możesz w unity wywołać własnego ręcznie zmontowanego draw calla własnego shadera dla samodzielnie zbindowanych danych.
Cytuj
Ogólne przesłanie jest takie, że najoptymalniej to można załatwić, gdy przerzuci się całą robotę związaną z przechowywaniem i zarządzaniem całą strukturą na kartę graficzną i ograniczyć wszystko do jedengo obiektu na CPU.
Ze wszystkimi trzema się całkowicie i absolutnie zgadzam. Nie wiem jak z fps, ale czas ładowania na pewno by drastycznie spadł bo większość obliczeń dałoby się zrównoleglić. Są tylko trzy problemy:
 - czy wierzchołki generowane po stronie GPU na pewno nie podlegają normalnemu limitowi 64k?
 - czy geometry shader nie podwyższył by ostro wymagań aplikacji?
 - czy z vertex shaderem, czy geometry shaderem, zwyczajnie nie potrafię realizować takich algorytmów na gpu :D
W sumie musiałbym jeszcze wybadać, czy by to podejście się nie dupsło dla obiektów półprzezroczystych. Bardzo możliwe, że tak.

Dotąd widziałem równo jeden artykuł o proceduralnym generowaniu mesha do Unity po stronie GPU i była to dla mnie czarna magia xD (chociaż to zamiast prostych voxeli były marching cubes, które robione gdziekolwiek są czarną magią). Jak masz jakieś przykłady to chętnie ogarnę. Na razie nie jestem pewien czy jest sens to robić - to zadanie na uczelnię na grafikę komputerową (tak czy inaczej wykraczające poza to co robią inni). Na pewno przydałoby się to gdyby aplikacja miała znaleźć konkretne zastosowania komercyjne.
Proceduralne generowanie siatki na GPU nie musi się zaraz sprowadzać do marching cubes / marching tethradrons. Można to zdecydowanie prościej, poza tym w twoim przypadku raczej by to nie zdało egzaminu. Ale skoro to zagadnienie na uczelnię... Z jednej strony nie warto się bawić w jakieś killer-appy a z drugiej strony projekt z programowania grafiki robiony na Unity to jest kpina. OpenGL / Direct3D, ewentualnie XNA albo coś do tego podobne. Żadnych shaderów nie piszesz, żadnych buforów nie masz, żadnych macierzy nie mnożysz... Panie! Co to jest? :D

Nie jestem pewien jak miałoby wyglądać ręczne wołanie draw calli. Najbardziej niskopoziomowe api do którego Unity daje dostęp i które kojarzę to klasa GL ("Low-level graphics library") (http://docs.unity3d.com/ScriptReference/GL.html).
Ciekawe. Rzucę okiem, może się nam przydać.
Edit:
Rzuciłem okiem, gówno warte. Tak się pisało w OpenGLu 2.0 zanim się ludzie zaczęli bawić shaderami. Z gówna bata nie ukręcisz.
Edit2:
To wygląda trochę sensowniej:
http://docs.unity3d.com/ScriptReference/Graphics.DrawMesh.html
http://docs.unity3d.com/ScriptReference/Graphics.DrawMeshNow.html
http://docs.unity3d.com/ScriptReference/Mesh.html
 
Popisuje się ciągle menda jedna...

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator

inż. Avallach
Administrator

Optymalizacja
#8 2014-12-07, 23:08(Ostatnia zmiana: 2014-12-07, 23:22)
...a jeśli zrobiłbyś to w trybie unsafe i usuwał obiekt manualnie to mógłbyś zauważyć efekty.
Taa, z tym że zysk pamięci pewnie byłby znikomy i tylko chwilowy (zużycie i tak jest małe). Niech sobie GC popracuje kiedy tam uzna za stosowne :D

Cytuj
Jak masz pizdyliard wokseli, które zżerają ci za dużo pamięci
To równo 8mb (512*512*32*byte). Wcale nie tak dużo.
Zgubiłeś jeden wymiar. 512x512x512 * 4 bajty (na alfę) = 512MB.
Nie, napisałem dobrze. Jedna warstwa ma wymiary 512x512. Warstw jest tylko 32, to zdjęcia z tomografu czy czegośtam. Bajt jest jeden, bo zapisuję TYLKO alfę (zdjęcia były monochromatyczne nieprzezroczyste, konwertuję ją z jasności). Także wychodzi 8mb, nie 512. Można próbować to dalej niskopoziomowo kompresować zapisując alphę na 4 bitach (i tak nie widać różnicy) i upychając 2 voxele na komórkę czy coś, ale to nie ma sensu.

No to fakt, że jakieś dzielenie będzie potrzebne w takim przypadku. Chyba, że uda ci się to ograniczenie w jakiś sposób zdjąć. Może w unity się gdzieś da to wyłączyć, bo to raczej nie jest ograniczenie po stronie możliwości shaderów.
Ograniczenie musiałoby zostać zdjęte przez programistów Unity i wiązałoby się ze zmianą typu danych używanego do przechowywania indeksów wierzchołków - a co za tym idzie zdublowania zużycia pamięci na meshe. Raczej tego nie zrobią, wiele popularnych u casualowych użytkowników kart w ogóle by tego nie obsłużyło.
Cytuj
The limitation is because the indexes that uploaded to the GPU are stored as 16 bit unsigned integers which means a limit like you say of 65536 vertices per mesh. Unity splitting the mesh into batches is itself Unity getting around the limitation. This is the default limitation built into the underlying API (OpenGL or DirectX). I believe both OpenGL and DirectX have options for using 32 bit indexes (could be wrong about this bit), but as far as I'm aware there's no way in Unity3D to access that.
Cytuj
pretty surely it won't because I doubt that UT intends to write on the page: You need a Geforce 7300 / Radeon 3400 or higher with at very least 128mb, optimally 256mb of VRAM as min. Intel is not supported. and that would be the consequence of that step.
Jedna z wielu ognistych dyskusji na ten temat: http://forum.unity3d.com/threads/meshes-may-not-have-more-than-65000-triangles-at-the-moment.43826/

projekt z programowania grafiki robiony na Unity to jest kpina. OpenGL / Direct3D, ewentualnie XNA albo coś do tego podobne. Żadnych shaderów nie piszesz, żadnych buforów nie masz, żadnych macierzy nie mnożysz... Panie! Co to jest? :D
Nie rozumiem, wyjaśnij.
Unity nadaje się tutaj idealnie, przy każdym innym rozwiązaniu ilość mojego koniecznego wkładu drastycznie by wzrosła.
Shadera nie musiałem pisać, ale zrobiłem to żeby mieć więcej fps. Jest bardzo krótki, do tego opierałem się na gotowym i jedyne co robiłem to powyłączałem w nim jeszcze więcej ficzerów (jak cieniowanie).
Nie korzystam z żadnych buforów (rozważałem skorzystanie z .NETowego Buffer.BlockCopy do szybkiego przenoszenia danych między tablicą 1d a 3d, ale zysk okazał się niewielki więc zrezygnoawłem).
Macierz (tablicę 3x3x3) mam tylko jedną, do przechowania danych o voxelach, jest ona nie do uniknięcia chyba żeby tworzyć mesh w miarę czytania obrazków z dysku (niegłupie, właściwie dzięki zrównolegleniu mogłoby teoretycznie być szybsze - problem w tym że zysk byłby żaden, bo wczytywanie obrazków trwa 0.1 s a generowanie mesha 10s, do tego strasznie namieszałoby to w kodzie).

W nowym Unity jest nowy system GUI. Wcześniej żeby zrobić i poustawiać suwak regulujący clip plane aktywnej kamery musiałbym napisać kilkanaście liniejk kodu. Teraz wszystko... wyklikałem. Jedno kliknięcie dodaje suwak jako obiekt na scenę i konfiguruje go jako część gui, prosty interface pozwala ustawić jego wygląd i pozycję, nie trzeba nawet pisać metody która będzie reagowała na zmiany jego pozycji / zewnętrzne zmiany parametru kamery. Zamiast tego można wybrać jeden z obiektów na scenie (kamera) i jedną z jej propercji (nearClipPlane) a Unity załatwi binding/synchronizację automagicznie. Który inny silnik robi aż tyle za ciebie? :D

Edit:
Klasa mesh którą zalinkowałeś jest do proceduralnego generowania mesha jako obiektu (używam jej). Nie ma nic wspólnego z renderowaniem. Nie wiem czy byłby tu jakiś użytek z Graphics.DrawMesh. Ciekawsze wydaje się być Graphics.DrawProcedural, ale ma haczyk: "This is only useful on DirectX 11 level hardware where shaders can read arbitrary data from ComputeBuffer buffers". Wydaje się dawać duże możliwości:
Cytuj
Draws a fully procedural geometry on the GPU.
DrawProcedural does a draw call on the GPU, without any vertex or index buffers.
Note that this call executes immediately, similar to Graphics.DrawMeshNow. It uses currently set render target, transformation matrices and currently set shader pass.

Sawik

Sawik

Użytkownicy
Rebel
posty4772
Propsy3197
ProfesjaNierób
  • Użytkownicy
  • Rebel
No, on właśnie o tym mówi. 
Co to za projekt skoro tyle jest zrobione za ciebie? 
 
Życzę wam seksu analnego po stronie biernej.
Dropbox +500 mb na start
LowPoly
Wykonanie modelu niskopoligonowego to sztuka kompromisu. Nie jest to jedynie uproszczenie modelu wysokopoligonowego, ale głęboka modyfikacja oraz podejmowanie decyzji często zmieniających wygląd pierwotny obiektu, tak by przy najmniejszej ilości trójkątów uzyskać jak najwierniej odwzorowany kształt oryginału. Nie można też zapomnieć o tym iż musi nadal wyglądać przekonywająco i tak balansować by uzyskać efekt optymalny.

Podstawowym założeniem jest, że model nie powinien mieć zbędnych, niewidocznych dla gracza detali włączonych w geometrie. Większość obiektów jakie znajdują się w grze powinna prezentować się najlepiej z odległości około 3-5 metrów. Wszelkie detale, które zanikają, wydają się płaskie lub zlewają się z bryłą modelu należy uznać za zbędne i pozostawić je na normal mapie.

Fakt, iż gracz będzie w stanie podejść bliżej do obiektu i zobaczyć go z mniejszej niż 3m odległości nie powinno stanowić większego problemu, gdyż większą rolę odgrywają wtedy tekstury oraz dodatkowy detal zależny od materiału obiektu. To właśnie kompromis między wydajnością, a szczegółowością otoczenia.

Detal, którego nie widać z 3-5 metrów nie powinnien istnieć w geometrii modelu.
Krawędzie znajdujące się blisko siebie, które zlewają się z większej odległości należy uprościć do wspólnej płaszczyzny

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
Aaaa, teraz rozumiem, w sensie że mam za łatwo xD

Zadanie polega tylko na generowaniu meshów (część z przejrzystych voxeli sześciennych, jak widać powyżej, a część w formie gładkiej jednolitej powierzchni opartej o marchnig cubes). To robię. Resztę (renderowanie, interface, abstrakcja na system) może (i powinien) zapewnić gotowy silnik. To nie jest zadanie czysto edukacyjne - ten program jest naprawdę potrzebny i ma jakieśtam realne szanse na wejście do użytku.

Nigdy nie paliłem się do programowania niskopoziomowego. Shader jeden napisałem, ale tak jak pisałem super prosty. Nie chcę żadnego więcej operowania na pamięci, buforach GPU, liczenia macierzy xD
Na pierwsze laborki robiliśmy edytor grafiki wektorowej, na drugie coś podobnego, ale z przekształceniami geometrycznymi (wykonywanymi przez mnożenie macierzy transformacji i macierzy współrzędnych jednorodnych), a na trzecie pisanie własnego soft-renderera. Jestem już ponad to, chcę mieć eleganckie wysokopoziomowe api :D

mgr Fartuess

mgr Fartuess

Użytkownicy
Kiedyś to były czasy!
posty1485
Propsy890
ProfesjaProgramista
  • Użytkownicy
  • Kiedyś to były czasy!
Cytuj
Jak masz pizdyliard wokseli, które zżerają ci za dużo pamięci
To równo 8mb (512*512*32*byte). Wcale nie tak dużo.
Zgubiłeś jeden wymiar. 512x512x512 * 4 bajty (na alfę) = 512MB.
Nie, napisałem dobrze. Jedna warstwa ma wymiary 512x512. Warstw jest tylko 32, to zdjęcia z tomografu czy czegośtam. Bajt jest jeden, bo zapisuję TYLKO alfę (zdjęcia były monochromatyczne nieprzezroczyste, konwertuję ją z jasności). Także wychodzi 8mb, nie 512. Można próbować to dalej niskopoziomowo kompresować zapisując alphę na 4 bitach (i tak nie widać różnicy) i upychając 2 voxele na komórkę czy coś, ale to nie ma sensu.
Sorry liczyłem alphę jako 32bitowego floata, bo tak to będzie reprezentowane po stronie GPU podczas obliczeń.

Ograniczenie musiałoby zostać zdjęte przez programistów Unity i wiązałoby się ze zmianą typu danych używanego do przechowywania indeksów wierzchołków - a co za tym idzie zdublowania zużycia pamięci na meshe. Raczej tego nie zrobią, wiele popularnych u casualowych użytkowników kart w ogóle by tego nie obsłużyło.
Faktycznie trochę słabo. Zawsze mogli dać klasę generyczną, albo podklasę z intem zamiast shorta. Poza tym to wcale nie zwiększyłoby wagi mesha dwukrotnie. Wzrost byłby tak naprawdę nieznaczny. Pozycja - 12 bajtów (vec3), normalna 12 bajtów, tangent i bitangent 12 bajtów, texcoordy 8 bajtów. A oszczędzają 2 bajty na indeksowaniu i wszystkim robią problemy. Geniusze kurwa, geniusze :P

projekt z programowania grafiki robiony na Unity to jest kpina. OpenGL / Direct3D, ewentualnie XNA albo coś do tego podobne. Żadnych shaderów nie piszesz, żadnych buforów nie masz, żadnych macierzy nie mnożysz... Panie! Co to jest? :D
Nie rozumiem, wyjaśnij.
Unity nadaje się tutaj idealnie, przy każdym innym rozwiązaniu ilość mojego koniecznego wkładu drastycznie by wzrosła.
Shadera nie musiałem pisać  [...] Który inny silnik robi aż tyle za ciebie? :D
Jak napisał Sawik. To jest przedmiot na którym teoretycznie masz się czegoś nauczyć. W domyśle tego jak wygląda renderowanie na niskim poziomie abstrakcji. Manualne montowanie VBO/VAO pisanie shaderów takich jakie się chce z atrybutami i uniformami takimi jaki samemu się dostarczy, a nie takimi jakie dostarczy silnik... etc. Oczywiście przy tak prowadzonych zajęciach ponad 90% ludzi nie rozumie co się w ogóle dzieje na zajęciach :P.

Klasa mesh którą zalinkowałeś jest do proceduralnego generowania mesha jako obiektu (używam jej). Nie ma nic wspólnego z renderowaniem.
Dunno, definiowanie siatki wyglądało dużo sensowniej niż w klasie GL.
 
Popisuje się ciągle menda jedna...

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
W GL "skrobiesz kursorem po karcie graficznej". W Mesh po prostu tworzysz sobie strukturę danych (w pamięci ram korzystając z procesora).

Na tych zajeciach najambitniejsze co mamy to samodzielne wyznaczanie i przekształcanie macierzy perspektywicznej i pisanie oświetlenia i cieniowania (Phong i Gouraud) (całość do złożenia we własny soft renderer). Reszta nie ma pojęcia co to materiał czy shader :D (i piszą w Javie).

mgr Fartuess

mgr Fartuess

Użytkownicy
Kiedyś to były czasy!
posty1485
Propsy890
ProfesjaProgramista
  • Użytkownicy
  • Kiedyś to były czasy!
w GL robisz to tak jakbyś używał OpenGLa 15 lat temu. W Mesh, to przynajmniej wygląda to na przygotowanie VBO i VAO, które automatycznie zostanie przerzucone przez Unity na GPU.

Ale tym, że piszą w Javie to mnie przekonałeś, że do bani te zajęcia. Same używane modele oświetlenia to jeszcze mało istotne, jeśli się uczy jak tworzyć bufory, shadery i jak przepychać do shaderów bufory i zmienne pomocnicze.
 
Popisuje się ciągle menda jedna...

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
Nie ma ani słowa o buforach ani shaderach. Kurs jest w praktyce o softrenderingu, właściwie bez udziału gpu (kolor każdego pixela jest liczony przez algorytm działający na procesorze). Chcą nas w taki praktyczny sposób nauczyć jak to działa na gpu (gdzie ingerowanie w algorytmy renderujące nie jest już takie proste jak pisanie w Javie) - przekształcenia perspektywiczne, cieniowanie, teksturowanie, z-buffer, back-face culling, occlision culling, frustum culling...

Klasa mesh to z o ile wiem zwyczajna struktura danych, nie mająca żadnego bezpośredniego związku z GPU. Żeby go wyświetlić, musisz podać go komponentowi MeshFilter, a on z kolei musi być czytany przez MeshRenderera. Nie mam pojęcia jaka dokładnie magia dzieje się w Mesh Filterze, nie jest to opisane.

mgr Fartuess

mgr Fartuess

Użytkownicy
Kiedyś to były czasy!
posty1485
Propsy890
ProfesjaProgramista
  • Użytkownicy
  • Kiedyś to były czasy!
Przekazuję Ci wyrazy współczucia.
 
Popisuje się ciągle menda jedna...

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
Na początek to jest ok. Za semestr czy dwa mieliśmy mieć "zaawansowaną grafikę komputerową" i tam pewnie byłyby właśnie takie zabawy na gpu. Wygląda na to że ktoś z dziekanatu postanowił sobie że jej nie będzie. Nie jestem pewien czy nie można próbować się o to wykłócać, ale raczej nie.

mgr Fartuess

mgr Fartuess

Użytkownicy
Kiedyś to były czasy!
posty1485
Propsy890
ProfesjaProgramista
  • Użytkownicy
  • Kiedyś to były czasy!
Ja na Zaawansowanych metodach renderingu na projekcie robiłem anizotropy, tessalacje, parralaxy, cook-torrance'y, straussy i inne takie. i to było proste, bo na wykładzie były poczwórne całki opisujące transport energii między dwoma płatami, dla algorytmów płatowych global illumination :P
 
Popisuje się ciągle menda jedna...


0 użytkowników i 1 Gość przegląda ten wątek.
0 użytkowników
Do góry