programowanie gier komputerowych tomasz martyn
DESCRIPTION
Programowanie gier komputerowych Tomasz Martyn. Wykład 3. Zarządzanie zasobami. Zarządzanie zasobami oparte na zwykłych wskaźnikach na ogół nie jest bezpieczne. - PowerPoint PPT PresentationTRANSCRIPT
Wykład 3.
Zarządzanie zasobami
Programowanie gier komputerowychTomasz Martyn
Uchwyty (1)Po co?
1. Zarządzanie zasobami oparte na zwykłych wskaźnikach na ogół nie
jest bezpieczne. Na przykład jeden z podsystemów silnika gry może zażądać od zarządcy zasobów usunięcia zasobu wskazywanego przez wskaźnik; jeżeli żądanie takie zostanie spełnione, wówczas wszystkie pozostałe wskaźniki na ten zasób istniejące w systemie są nieważne (ale korzystające z nich podsystemy o tym nie wiedzą).
2. Zapisywanie (np. na dysk) aktualnego stanu systemu wykorzystujące wartości wskaźników w celu ponownego odtworzenia stanu systemu jest na ogół niepoprawne. Ma to istotne znaczenie w przypadku zapisów bieżących stanów gry, ponieważ przy ponownym wczytaniu gry układ danych w pamięci prawdopodobnie będzie inny.
3. Proces defragmentacji pamięci w oczywisty sposób powoduje unieważnienie wskaźników. Jednym ze sposobów rozwiązania powyższych problemów jest
wprowadzenie dodatkowej warstwy abstrakcji pomiędzy zwykłymi wskaźnikami a systemem w postaci uchwytów.
Uchwyty (2)Przykład klasy szablonowej ogólnego uchwytu
(patrz np.: S. Bilas: Ogólny system zarządzania zasobami oparty na uchwytach, w: Perełki programowania gier 1)
Uchwyty (3)Zarządca uchwytów (1)
Definicja klasy
Uchwyty (4)Zarządca uchwytów (2)
Generowanie i zwalnianie uchwytu
Uchwyty (5)Zarządca uchwytów (3)
Dereferencja uchwytu
Zarządca zasobów (1)
Gry komputerowe i wideo wymagają obsługi dużej ilości różnorodnych danych multimedialnych (geometria, tekstury, materiały, animacje, efekty dźwiękowe, ...), które na ogół zajmują dużo miejsca w rozumieniu kosztów pamięciowych.
Manipulowanie tak dużą ilością danych w czasie rzeczywistym przy ograniczonym budżecie pamięciowych wymaga odpowiedniego zarządzania tymi danymi.
Zadania ogólnego zarządcy zasobów:
• zapewnia, że w danej chwili w pamięci każdy zasób przechowywany jest tylko w jednej kopii (zasób może być współdzielony np. przez kilka modeli)
• zarządza wykorzystaniem pamięci przez załadowane zasoby, w szczególności może dbać o to, żeby zasób został umieszczony w odpowiedniej pamięci (np. tekstura w pamięci tekstur; szczególnie w przypadku konsol posiadających pamięci różnego rodzaju)
• wspomaga zarządzanie czasem życia każdego zasobu, ładuje zasoby potrzebne (najlepiej z odpowiednim wyprzedzeniem) i usuwa zasoby, które nie są już potrzebne
• wspomaga ładowanie zasobów złożonych (np. model 3D może składać się z: siatki trójkątów, materiałów, tekstur, szkieletu, animacji...)
• często umożliwia asynchroniczne ładowanie zasobów
Zarządca zasobów (2)
Często zarządca zasobów udostępnia jednolity interfejs umożliwiający zarządzanie zasobami różnych typów.
Na przykład w Ogre3D realizowane jest to poprzez zdefiniowanie abstrakcyjnej klasy Resource oraz ogólnej klasy ResourceManager, po której dziedziczą (singletonowe) klasy zarządców konkretnych typów zasobów.
Rolę zarządcy zasobów (w opisywanym wyżej sensie) w Ogre3D pełni singletonowa klasa ResourceGroupManager - odwołuje się do poszczególnych zarządców za pośrednictwem przechowywanych przez nią wskaźników.
Zarządca zasobów (3)Przykład prostego zarządcy tekstur (1)
(S. Bilas: Ogólny system zarządzania zasobami oparty na uchwytach, w: Perełki programowania gier 1)
Zarządca zasobów (4)Przykład prostego zarządcy tekstur (2)
Zarządca zasobów (5)Problem łańcuchów znaków
• Łańcuchy znaków są wszechobecne w silniku gry. W szczególności wykorzystywane są jako unikatowe identyfikatory poszczególnych zasobów gry, które są przyjazne dla projektanta (w odróżnieniu np. od unikalnych identyfikatorów liczbowych - GUID).
• Jednakże dokonywanie działań na łańcuchach znaków (w szczególności porównań i kopiowania) jest o wiele bardziej czasochłonnymi operacjami niż analogiczne operacje dokonywane na liczbach. W rezultacie, nieodpowiednie podejście do przetwarzania łańcuchów znaków w czasie wykonania może wpłynąć na (nawet znaczne) obniżenie wydajności działania silnika.
• Powszechnym rozwiązaniem tego problemu stosowanym w silnikach gier jest wykorzystanie podejścia polegającego na reprezentowaniu łańcuchów znaków w postaci liczb (zwykle 32-bitowych) otrzymanych za pomocą odpowiedniej funkcji mieszającej (hash function) – tzw. internowanie stringów (interning strings)
• Często w roli funkcji mieszającej stosowany jest tutaj algorytm bazujący na cyklicznym kodzie nadmiarowym CRC-16 lub CRC-32.
• Zamiast łańcuchów znaków w kodzie gry wykorzystywane są ich id liczbowe, zaś same łańcuchy przechowywane są w tablicy mieszającej (indeksem jest id łańcucha) w celu odzyskania właściwego łańcucha na podstawie id (np. aby wyświetlać stosowane informacje o zasobach w konsoli deweloperskiej).
Zarządca zasobów (6)Czas życia zasobów (1)
Czas życia zasobu definiowany jest jako okres czasu pomiędzy załadowaniem zasobu do pamięci, a zwolnieniem tej pamięci dla innych celów.
Jednym z zadań zarządcy zasobów jest zarządzanie czasem życia zasobów - albo automatyczne, albo poprzez dostarczanie odpowiednich funkcji, które umożliwią określanie życia zasobów z zewnątrz (np. z kodu rozgrywki).
Zasoby pod względem życia można podzielić na:
• zasoby pozostające w pamięci przez cały okres gry (load-and-stay-resident – LSR); np. zasoby związane z postacią sterowaną przez gracza, zasoby interfejsu ekranowego (HUD)
• zasoby pozostające w pamięci przez cały poziom gry; zasób musi być w pamięci gdy gracz widzi poziom po raz pierwszy oraz zasób jest usuwany gdy gracz definitywnie opuszcza poziom
• zasoby o czasie życia krótszym niż trwanie poziomu gry (np. krótkie przerywniki filmowe „wtrącone” do rozgrywki poziomu albo zasoby związane z poziomem, ale aktualnie niepotrzebne i usuwane ze względu ograniczonego budżetu pamięciowego
• zasoby streamowane (np. muzyka, przerywniki filmowe o stosunkowo dużych „gabarytach”)
Zarządca zasobów (7)Czas życia zasobów (2)
Zagadnienie dotyczące momentu ładowania zasobu do pamięci jest stosunkowo proste, bowiem moment ten musi poprzedzać czas, w którym gracz (na ogół po raz pierwszy) widzi lub słyszy zasób.
Często o wiele trudniejsze jest zagadnienie dotyczące zwalniania pamięci zajmowanej przez zasób.
• Po pierwsze, zasób może być współdzielony przez wiele poziomów gry. W takim przypadku rozwiązanie może polegać na zliczaniu odwołań do zasobu i usuwaniu tych zasobów, których licznik jest zerowy.
• Po drugie, wszystkie zasoby związane z poziomem gry mogą nie mieścić się w pamięci (np. w zw. z mocno ograniczonym budżetem pamięciowym konsoli lub otwartym światem gry). W takim przypadku jedno z rozwiązań automatycznego usuwania zasobów bazuje na sortowaniu zasobów przez zarządcę zasobów w trakcie czasu wykonania względem nadanego priorytetu ważności, czasu ostatniego dostępu do zasobu, rozmiaru zasobu; zasoby znajdujące się na końcu posortowanej listy usuwane są z pamięci i na ich miejsce ładowane są zasoby aktualnie wymagane (por. np. J. Boer: Zarządzanie zasobami i pamięcią, w: Perełki programowania gier 1).