Autor Wątek: Szybsze (własne) GUI  (Przeczytany 3286 razy)

Offline komorra

  • Użytkownik
    • Blog naszego teamu (o grze Voxelfield)

# Styczeń 04, 2011, 12:16:05
Cześć. Od pewnego czasu zajmuję się tworzeniem pluginów VST (dla niewtajemniczonych, technologia wtyczek do programów muzycznych umożliwiająca tworzenie własnych instrumentów i efektów dźwiękowych). Najprościej rzecz ujmując taki plugin składa się z panelu (typowe VST ma pokrętła, przyciski, suwaki itp. - udaje prawdziwe studyjne urządzenie) oraz z funkcji która wytwarza bądź przetwarza dźwięk. Program w którym uruchamia się plugin nazywamy Hostem - np. ja używam FL Studio 9.7 (http://flstudio.image-line.com/).
Do tworzenia pluginów przeznaczone jest VST SDK Steinberga, napisane w C++, aczkolwiek bardziej mi odpowiada C# więc korzystam z wrappera VST.NET.

Samo napisanie części odpowiedzialnej za audio nie stanowi tutaj problemu (trzeba znać się na DSP, trochę na teorii muzyki) - jest duże pole do eksperymentowania. Dla mnie problemem jest jednak GUI - bo Windows Forms jest dosyć wolne, gdy w grę wchodzi np. wstawienie do pluginu wizualizacji real-time. I tu dosyć istotna informacja - nie używam standardowych kontrolek .NET-u, tworzę sobie UserControl, który w metodzie Paint tylko i wyłącznie blituje swoje tło z bitmapy w której jest zapisana animacja np. pokrętła w 100 klatkach. To działa "dosyć" szybko ale nie wystarczająco - oto przykład pluginu, który napisałem ostatnio: http://www.youtube.com/watch?v=szhXwMIwrNU

Jak można maksymalnie przyspieszyć GUI w WindowsForms? A może użyć OpenGL (jaki wrapper?) lub DX? Direct2D odpada ponieważ zawęża grono użytkowników do posiadaczy Visty i wyżej. Generalnie potrzebuję czegoś na .NET o bardzo szybkim kopiowaniu bitmap - tylko ta jedna operacja wystarczy do napisania całego plugin-u. Z góry dzięki za pomoc.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Snejk47

  • Użytkownik

# Styczeń 04, 2011, 12:43:36
Może użyj VST Gui? Lub poszukaj innych takich GUIowych dodatków.

Offline Esidar

  • Użytkownik

# Styczeń 04, 2011, 12:44:38
Ile to jest "nie wystarczająco" ? Cały interface windows jest oparty o standardowy Paint. Może wina leży we wraperze. Wtedy GL też by nie pomógł.

Może wina też leżeć w sposobie rysowania. Włączasz double buffer i rysujesz bezpośrednio w kontrolce, czy masz bitmapę tymczasową do której rysujesz ? Możliwe też że prędkość spada nie przy DrawImage ale przy innych rzeczach (np. tekst). Testowałeś każdy z elementów ?

Offline komorra

  • Użytkownik
    • Blog naszego teamu (o grze Voxelfield)

# Styczeń 04, 2011, 13:04:26
Cytuj
Ile to jest "nie wystarczająco" ?
Nie wystarczająco - podam przykład - standardowe pluginy VST nie powodują blokowania całego interfejsu Hosta przy kręceniu pokrętłem plugin-u. To jest właśnie podejrzane.

Cytuj
Może wina leży we wraperze.
Sam wrapper nie zawiera w sobie "rzeczy" od obsługi GUI

Cytuj
Włączasz double buffer i rysujesz bezpośrednio w kontrolce, czy masz bitmapę tymczasową do której rysujesz ?
Włączam double buffer i rysuje bezpośrednio na kontrolce

Rysowanie tekstu raczej nie spowalnia. Ale niepokoi mnie efekt zawieszania się GUI hosta przy operowaniu na GUI pluginu...

Offline Karol

  • Użytkownik

# Styczeń 04, 2011, 13:36:37
Przy tak małych kontrolkach rysowanie GUI w ten sposób nie powinno być w ogóle kosztowne ani odczuwalne. Może problemem jest kod wywoływany jako reakcja na zmianę kontrolki, a nie jej odrysowanie?

Offline komorra

  • Użytkownik
    • Blog naszego teamu (o grze Voxelfield)

# Styczeń 04, 2011, 13:49:12
W sumie kontrolki animują się bardzo płynnie. Ale coś jednak musi być na rzeczy skoro Host się w momencie refreshu kontrolek pluginu zawiesza.

Offline Pomnico

  • Użytkownik
    • Magic-Ars

# Styczeń 04, 2011, 14:06:49
Takie zachowanie (pomijając efekt środowiska, czyli VST) na nosa śmierdzi mi niewłaściwą reakcją na manipulację pokrętłami, tzn. całkowitym zjadaniem czasu procka. Np. czekaniem w pętli na zwolnienie przycisku myszki, niepotrzebnymi odrysowaniami (jeśli np. pokrętło nie zmieniło swojej pozycji, a z jakiegoś powodu dostajesz niepotrzebną notyfikację o zmianie) lub czymś w tym stylu.

Jeśli możesz to przetestuj to bez rysowania bitmap, bez rysowania czegokolwiek, albo tylko jakichś kwadratów dla orientacji w układzie pokręteł (oczywiści wytnij samo rysowanie a nie kod powodujący odrysowanie). Wynik pozwoli Ci się mniej więcej zorientować czy przyczyną jest rysowanie czy reakcja na zmiany pokręteł. Sprawdź też jak często przychodzą polecenia odrysowania (czy nie za często).

Jeśli nie wiesz, to w większości przypadków jesteś w stanie debugować pluginy. Wystarczy że swojego zbudujesz w trybie debugowym i po uruchomieniu host'a w visualu wybierz z menu Debug -> Attach to Process i wybierz proces hosta. Powinienes byc w stanie widziec komunikaty debugowe, ustawiac breakpointy itd. (oczywiście to ostatnie i kod tylko w Twoim pluginie)
« Ostatnia zmiana: Styczeń 04, 2011, 14:10:18 wysłana przez Pomnico »

Offline głos

  • Użytkownik

# Styczeń 04, 2011, 14:42:37
Użyj do wyświetlania grafy OpenTK, działa szybko i ładnie integruje się w Windows Forms.

Offline komorra

  • Użytkownik
    • Blog naszego teamu (o grze Voxelfield)

# Styczeń 04, 2011, 14:47:17
@Pomnico: u mnie każde wywołanie zdarzenia MouseMove = Refresh i chyba tu jest pies pogrzebany, spróbuję przerzucić odświeżanie do pętli w innym wątku tak aby wykonywało się 25 razy na sekundę i tylko gdy wartość się zmieniła.

@głos: faktycznie możnaby spróbować coś z tą biblioteką, ale wiadomości w tym wątku sugerują, że da się optymalizować po prostu to co mam w windows forms

Offline komorra

  • Użytkownik
    • Blog naszego teamu (o grze Voxelfield)

# Styczeń 04, 2011, 15:51:49
Właśnie przerzuciłem wywoływanie refresha do innego wątku, wykonuje się oddzielnie dla każdego knoba, i tylko w przypadku kiedy jego wartość zmieniła się na tyle, aby istniała potrzeba zmiany bitmapy knoba. Ustawiłem Sleep pętli na 30ms i odświeża się teraz wszystko ładnie, bez przeskoków GUI plugina i hosta.

Offline Pomnico

  • Użytkownik
    • Magic-Ars

# Styczeń 04, 2011, 16:16:52
Cieszę się że mogłem pomóc ;)

Co do osobnych wątków - nie wiem jak w .Net, ale podejrzewam że podobnie jak w WinAPI - nie musisz ich używać. Gdy dostajesz mouse move to sprawdzasz, czy potrzebne odrysowanie. Jeśli tak, to invalidujesz, jeśli nie to nic nie robisz. A na invalidate przychodzi komunikat (u Ciebie pewnie wołana jest metoda) do odrysowania i tam wszystko robisz.
Jeśli chcesz mieć odrysowanie co jakiś czas, to możesz użyć timer'ów (były w WinAPI, pewnie są też w .Net) - hint: funkcja SetTimer. Wtedy co ileś ms zostaje wysłany do okna komunikat (a u Ciebie pewnie metoda) i w nim robisz co chcesz (np. invalidujesz okno z kontrolkami, co w konsekwencji doprowadzi do wywołania metody odrysowującej).
Zerknij na to, bo jeśli da się tak zrobić w .Net to unikniesz problemów z synchronizacją wątków.

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Styczeń 04, 2011, 16:34:19
Cytuj
A może użyć OpenGL (jaki wrapper?) lub DX?
W przypadku FL Studio definitywnie nie polecam ani jednego ani drugiego. FL Studio korzysta z półprzezroczystych okienek na menu (tzw. layered windows), które OpenGL/DX po prostu zamazuje, przez co w efekcie ciężko wiele rzeczy z pluginem zrobić (bo menu pluginu wyświetla się w większej części na pluginie).

Póki co do rysowania GUI w VST wybrałem prostą metodę: tablica pikseli w pamięci, rysowanie po niej (z użyciem MMX do blendingu), a na koniec wrzucenie całości w okno VST za pomocą GDI+ (wrzucić piksele bezpośrednio na okno z pamięci jest tam dość prosto).

Offline nembutal

  • Użytkownik

# Styczeń 04, 2011, 16:51:11
Póki co do rysowania GUI w VST wybrałem prostą metodę: tablica pikseli w pamięci, rysowanie po niej (z użyciem MMX do blendingu), a na koniec wrzucenie całości w okno VST za pomocą GDI+ (wrzucić piksele bezpośrednio na okno z pamięci jest tam dość prosto).
Tak, też uważam, że posługiwanie się przy robieniu GUI abstrakcjami wyższego poziomu niż piksel jest szczytem rzymskiej dekadencji.
Tak na poważnie to napisałeś własną bibliotekę kontrolek i kod do ich rasteryzacji?

Offline komorra

  • Użytkownik
    • Blog naszego teamu (o grze Voxelfield)

# Styczeń 04, 2011, 19:49:52
Cytuj
Tak na poważnie to napisałeś własną bibliotekę kontrolek i kod do ich rasteryzacji?
Tzn. u mnie jest to tylko klasa Knob rozszerzająca UserControl, posiadająca właściwość Value i jej głównym zadaniem jest zblitowanie odpowiedniej częśći bitmapy w zależności od value. Dla przykładu załączam bitmapę źródłową.

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Styczeń 04, 2011, 20:07:27
Cytuj
Tak na poważnie to napisałeś własną bibliotekę kontrolek i kod do ich rasteryzacji?
Przynajmniej ze trzy biblioteki reusowalne, nie licząc mniejszych GUI pisanych pod konkretne projekty (których mogło być dośc dużo). Poza tym nie używałbym wielkich słów typu "rasteryzacja", skoro narysowanie kontrolki VST sprowadza się do narysowania alpha-blendowanego obrazka. :)

Jeżeli chodzi o "duże" GUI, to roboty tu jest nieco więcej, ale musiałem też zrobić bo żadne inne GUI jakie znalazłem nie spełniało moich wymagań, w tym głównego: jak najmniej kodu, czyli jedna kontrolka = jedna linijka w C++ i nic więcej (żadnych create/destroy, żadnych callbacków, itp).


EDIT: Co do samego VST i wzualizacji, to też mnie kusiło, ale zrezygnowałem z tego pomysłu. Szkoda cykli na świecące bajery i odświeżanie GUI, podczas gdy użytkownik toczy nieustającą bitwę z rozmiarem bufora i latencją przy działającej pół tonie innych wtyczek. :)
« Ostatnia zmiana: Styczeń 04, 2011, 20:09:49 wysłana przez Krzysiek K. »