2010-06-16 14 views
7

Zaczynam od C# i XNA. W "Update" metody "Game" klasy mam ten kod:C#/XNA - Załaduj obiekty do pamięci - jak to działa?

t = Texture2D.FromFile([...]); //t is a 'Texture2D t;' 

, który ładuje mały obraz. Metoda "Aktualizuj" działa jak pętla, więc ten kod nazywa się wiele razy w ciągu sekundy. Teraz, kiedy uruchamiam moją grę, zajmuje ona 95 MB pamięci RAM i powoli przechodzi do około 130 MB (z powodu kodu, który napisałem, bez tego kodu pozostaje na 95 MB), a następnie przechodzi natychmiast do około 100 MB (garletowe ujęcie?) i znowu idzie powoli do 130 MB, a następnie natychmiast do 100 MB i tak dalej. Moje pierwsze pytanie:

  1. Czy możesz wyjaśnić, dlaczego (jak) działa w ten sposób?

Znalazłem, że w przypadku zmiany kodu do:

t.Dispose() 
t = Texture2D.FromFile([...]); 

to działa tak: najpierw zajmuje 95MB, a następnie przechodzi powoli do około 101MB (ze względu na kod ) i pozostaje na tym poziomie.

  1. Nie rozumiem, dlaczego zajmuje to 6MB (101-95) ...?

  2. Chcę, aby to działa tak: load obrazu, zwolnienia z pamięci, obciążenia obrazu, zwolnienia z pamięci i tak dalej, więc program powinien zawsze zajmuje 95MB (zajmuje 95MB, gdy obraz jest ładowany tylko raz w poprzednich metoda). Jakich instrukcji powinienem użyć?

Jeśli jest to ważne, rozmiar obrazu wynosi około 10 KB.

Dziękujemy!

Odpowiedz

3

Metoda aktualizacji nie powinna być używana do ładowania tekstur, ponieważ jest załadowana dużo. W .net obiekty są zbierane śmieci, co oznacza, że ​​nie można jawnie zwolnić obiektu (nawet .Dispose tego nie robi).

Jeśli system znajduje się pod dużym ciśnieniem, GC może nie działać tak często, jak to możliwe.

W skrócie: Jesteś "przecieka" tysiące Texture2Ds.

Właściwy sposób ich załadowania znajduje się w jednym z Inicjalizacyjnych zdarzeń i zapisuje je w zmiennej klasy, słowniku, liście lub dowolnej strukturze. Zasadniczo załaduj go tylko raz, a następnie użyj go ponownie.

Jeśli musisz wczytać teksturę na żądanie, wczytaj ją tylko raz i zapisz w Liście/Słowniku/Zmiennej Klasy i ponownie użyj ponownie.

Edytuj: Twoje podejście z "Załaduj obraz, zwolnij, wczytaj, zwolnij" nie będzie działać w .NET, ponieważ nie możesz jawnie zwolnić pamięci. Mógłbyś zadzwonić do GC.Collect, ale to też nie gwarantuje tego ib) GC.Collect jest niesamowicie powolny.

Czy istnieje szczególny powód, dla którego trzeba ponownie załadować obraz 60 razy na sekundę? Jeśli musisz przeładować ją co sekundę, możesz użyć upłyniętego czasu gry, by zmierzyć czas, jaki upłynął od ostatniej aktualizacji. Jeśli przekroczysz próg, ponownie wczytaj obraz.(Potrzebna jest zmienna klasy podobna do "LastUpdated", którą porównujesz i którą aktualizujesz po ponownym wczytaniu obrazu)

13

Przede wszystkim musisz zrozumieć, że to co robisz jest dość dziwne!

„normalna” sposób, aby załadować tekstury jest użycie rurociągu treści i Content Manager.

Jeśli naprawdę trzeba załadować teksturę z pliku, a nie systemu treści - należy to zrobić tylko raz dla każdej fakturze. Należy użyć Texture2D.FromFile w Game.LoadContent, i nazywają Dispose w Game.UnloadContent (zwykle Content Manager będzie obsługiwać wywołanie Dispose dla ciebie - ale dlatego, że nie zamierzamy poprzez Content Manager trzeba zadzwonić utylizować siebie).


Ważną rzeczą jest, aby uświadomić sobie, że pracujesz z obu zarządzane i niezarządzani zasobów tutaj.

Pamięć używana przez zarządzane obiekty będzie obsługiwana przez garbage collector - w tym przypadku każde wystąpienie Texture2D używa małego fragmentu zarządzanej pamięci z niewielkim. 99% czasu, o którym nie musisz się martwić - śmieciarz jest naprawdę dobry w zarządzaniu pamięcią zarządzaną - to jest jego zadanie!

Musisz się martwić o niezarządzane zasoby - w tym przypadku pamięć tekstur używana przez wszystkie te tekstury, które ładujesz. Ostatecznie garbage collector będzie działać, zobaczyć te wszystkie obiekty bez odniesień Texture2D i je odebrać. Gdy je zbierze, sfinalizuje je, co, podobnie jak wywołanie Dispose, uwolni zasoby niezarządzane.

Ale garbage collector nie „widzi” wszystkie niezarządzanych zasoby te Texture2D przedmioty są używane. Nie wie kiedy te obiekty muszą zostać zwolnione - sam możesz zrobić o wiele lepszą pracę. Wszystko, co musisz zrobić, to zadzwonić pod numer Dispose w teksturach, gdy nie są już potrzebne.

To dlatego, że śmieciarz nie wie o tym 30 MB dodatkowej pamięci używanej przez tekstury, że jest on gromadzony, gdy nie dzwonisz do dyspozycji. Oprócz tego, czego nie widać, gdy patrzymy na wykorzystanie pamięci procesowej, to cała pamięć GPU, której używają te tekstury!

(Podstawową implementacją Texture2D jest to, że zawiera odniesienie do obiektu tekstury DirectX - który sam jest obiektem niezarządzanym przy użyciu niezarządzanej pamięci głównej - który z kolei obsługuje teksturę i związaną z nią pamięć na GPU. od obiektu Texture2D jest tylko około 100-200 bajtów. - która przechowuje wspomnianą odniesienia i cache szerokość, wysokość, formatu tekstury i tak dalej)


teraz - jak na to, co staramy się osiągnąć . Jeśli naprawdę mają potrzebę, aby załadować tekstury każdej ramki (to wysoce nietypowe), a następnie także nie trzeba już poprzednią tekstury ...

Cóż, przede wszystkim, nazywając Dispose na niewykorzystane tekstury każda ramka jest prawidłowy sposób. Będziesz zależał od śmieciarza, aby wyczyścić wszystkie śmieci, które tworzy (zarządzana strona obiektów Texture2D) - jest to dobre w Windowsie, ale może zabić twoją wydajność na Xboksie. Przynajmniej nie będziesz przeciekać niezarządzanych zasobów.

Lepszą metodą, szczególnie jeśli tekstura ma taki sam rozmiar za każdym razem, jest po prostu używanie tego samego obiektu tekstury. Następnie wywołaj na nim Texture2D.SetData z nowymi danymi tekstury każdej klatki.

(jeżeli tekstury są różne rozmiary, albo masz więcej niż jeden używany w danym czasie, może trzeba zaimplementować coś takiego basenu tekstur.)

Oczywiście loadFile bierze rzeczywisty plik SetData pobiera nieprzetworzone dane. Konieczne będzie samodzielne wdrożenie konwersji danych. Dobrym punktem wyjścia może być this thread on the XNA forum.

0

Jak twierdzi Andrew Russell, ładowanie obrazu 60 razy na sekundę nie jest tym, co chcesz robić. Dodam jednak: nie używaj w ogóle Texture2D.FromFile!

XNA zapewnia solidne źródło treści - używaj go!

  1. Dodaj swoje zdjęcie do swojego projektu gry. XNA domyślnie wie, co zrobić z typami obrazów PNG, GIF i JP (E) G. Podczas kompilacji projektu XNA przetworzy również twój obraz w formacie pliku BNA X (* .XNB).
  2. W MyGame.LoadContent załadować swój wizerunek za pomocą myTexture = Content.Load<Texture2D>(@"My/Image/Folder/MyImageAssetName")

Content.Load załaduje skompilowaną wersję XNB swojego wizerunku. Możesz także wykonać czynności we właściwościach obrazu w projekcie, takie jak ustawienie koloru maskowania (np. Wszystkie białe piksele w JPG będą przezroczyste w grze), skalowanie obrazu i zmiana nazwy zasobu. (Nazwa atutem domyślnie nazwy pliku obrazu, ale można go zmienić we właściwościach i nazwa atutem jest to, co będziesz używać do Content.Load.)

Content.Load buforuje także rzeczy ładuje tak, że jeśli dla z jakiegoś powodu musisz wielokrotnie wywoływać numer Load w tym samym zasobie, nie mnożysz używanej pamięci.

Pipeline treści XNA może być używany do wielu innych rzeczy, a także obrazów, w tym modeli 3D, dźwięku, a nawet własnych zajęć.