Udostępnij przez


Kopiowanie i uzyskiwanie dostępu do danych zasobów (Direct3D 10)

Nie trzeba już myśleć o zasobach tworzonych w pamięci wideo lub pamięci systemowej. Czy też środowisko uruchomieniowe powinno zarządzać pamięcią. Dzięki architekturze nowego modelu WDDM (Windows Display Driver Model) aplikacje tworzą teraz zasoby Direct3D 10 z różnymi flagami użycia , aby wskazać, w jaki sposób aplikacja zamierza korzystać z danych zasobów. Nowy model sterownika wirtualizuje pamięć używaną przez zasoby; odpowiedzialnością systemu operacyjnego/sterownika/menedżera pamięci jest następnie umieszczenie zasobów w najbardziej wydajnym obszarze pamięci, biorąc pod uwagę oczekiwane użycie.

Domyślnym przypadkiem jest udostępnienie zasobów procesorowi GPU. Oczywiście, mimo to, są sytuacje, gdy dane zasobów muszą być dostępne dla procesora. Kopiowanie danych zasobów wokół, aby odpowiedni procesor mógł uzyskać do niego dostęp bez wpływu na wydajność, wymaga pewnej wiedzy na temat sposobu działania metod interfejsu API.

Kopiowanie danych zasobów

Zasoby są tworzone w pamięci, gdy funkcja Direct3D wykonuje wywołanie Create. Można je utworzyć w pamięci wideo, pamięci systemowej lub w jakimkolwiek innym rodzaju pamięci. Ponieważ model sterownika WDDM zwirtualizuje tę pamięć, aplikacje nie muszą już śledzić, w jakich zasobach pamięci są tworzone.

Najlepiej, aby wszystkie zasoby znajdowały się w pamięci wideo, dzięki czemu procesor GPU może mieć natychmiastowy dostęp do nich. Jednak czasami konieczne jest, aby procesor CPU odczytał dane zasobów lub aby procesor GPU miał dostęp do danych zasobów zapisanych przez procesor CPU. Direct3D 10 obsługuje te różne scenariusze, żądając od aplikacji określenia użycia, a następnie oferuje kilka metod kopiowania danych zasobów w razie potrzeby.

W zależności od sposobu utworzenia zasobu nie zawsze jest możliwe bezpośrednie uzyskiwanie dostępu do danych bazowych. Może to oznaczać, że dane zasobów muszą być kopiowane z zasobu źródłowego do innego zasobu, który jest dostępny dla odpowiedniego procesora. Jeśli chodzi o direct3D 10, zasoby domyślne mogą być dostępne bezpośrednio przez procesor GPU, zasoby dynamiczne i przejściowe mogą być bezpośrednio dostępne przez procesor CPU.

Po utworzeniu zasobu jego użycie nie może zostać zmienione. Zamiast tego skopiuj zawartość jednego zasobu do innego zasobu, który został utworzony przy użyciu innego użycia. Direct3D 10 zapewnia tę funkcję z trzema różnymi metodami. Pierwsze dwie metody( ID3D10Device::CopyResource i ID3D10Device::CopySubresourceRegion) są przeznaczone do kopiowania danych zasobów z jednego zasobu do innego. Trzecia metoda (ID3D10Device::UpdateSubresource) jest przeznaczona do kopiowania danych z pamięci do zasobu.

Istnieją dwa główne rodzaje zasobów: mapowane i niezmapowalne. Zasoby utworzone z użyciem dynamicznym lub przejściowym można mapować, a zasoby utworzone przy użyciu domyślnych lub niezmiennych użycia nie są mapowalne.

Kopiowanie danych między zasobami niezmapowalnymi jest bardzo szybkie, ponieważ jest to najbardziej typowy przypadek i zostało zoptymalizowane pod kątem dobrego działania. Ponieważ te zasoby nie są bezpośrednio dostępne dla procesora CPU, są zoptymalizowane tak, aby procesor GPU mógł nimi szybko manipulować.

Kopiowanie danych między zasobami z możliwością mapowania jest bardziej problematyczne, ponieważ wydajność będzie zależeć od użycia, za pomocą którego został utworzony zasób. Na przykład procesor GPU może dość szybko odczytać zasób dynamiczny, ale nie może zapisywać w nich danych, a procesor GPU nie może bezpośrednio odczytywać ani zapisywać w zasobach przejściowych.

Aplikacje, które chcą skopiować dane z zasobu z domyślnym użyciem do zasobu z użyciem przejściowym (aby umożliwić procesorowi CPU odczytywanie danych — tj. problem z odczytem zwrotnym procesora GPU) muszą to zrobić z ostrożnością. Aby uzyskać więcej informacji na temat tego ostatniego przypadku, zobacz Uzyskiwanie dostępu do danych zasobów.

Uzyskiwanie dostępu do danych zasobów

Uzyskiwanie dostępu do zasobu wymaga mapowania zasobu; mapowanie zasadniczo oznacza, że aplikacja próbuje zapewnić dostęp procesora CPU do pamięci. Mapowanie zasobu w sposób, aby procesor mógł uzyskać dostęp do pamięci podstawowej, może powodować wąskie gardła wydajności. Z tego powodu należy starannie zaplanować, jak i kiedy wykonać to zadanie.

Wydajność może być zatrzymywana, jeśli aplikacja próbuje mapować zasób w niewłaściwym czasie. Jeśli aplikacja spróbuje uzyskać dostęp do wyników operacji przed zakończeniem tej operacji, nastąpi zator w potoku.

Wykonanie operacji mapowania w niewłaściwym czasie może potencjalnie spowodować poważny spadek wydajności przez wymuszenie synchronizacji GPU i CPU. Ta synchronizacja zostanie wykonana, jeśli aplikacja chce uzyskać dostęp do zasobu, zanim procesor GPU zakończy kopiowanie go do zasobu, który może mapować procesor CPU.

CPU może odczytywać tylko z zasobów utworzonych za pomocą flagi D3D10_USAGE_STAGING. Ponieważ zasobów utworzonych za pomocą tej flagi nie można ustawić jako danych wyjściowych potoku, jeśli procesor CPU chce odczytać dane w zasobie wygenerowanym przez procesor GPU, dane muszą zostać skopiowane do zasobu utworzonego za pomocą flagi przejściowej. Aplikacja może to zrobić przy użyciu ID3D10Device::CopyResource lub ID3D10Device::CopySubresourceRegion metody kopiowania zawartości jednego zasobu do innego. Następnie aplikacja może uzyskać dostęp do tego zasobu, wywołując odpowiednią metodę mapowania. Gdy dostęp do zasobu nie jest już potrzebny, aplikacja powinna wywołać odpowiednią metodę Unmap. Na przykład ID3D10Texture2D::Map i ID3D10Texture2D::Unmap. Różne metody mapowania zwracają określone wartości w zależności od flag wejściowych. Aby uzyskać szczegółowe informacje, zobaczsekcjęUwagi dotyczące mapy.

Notatka

Gdy aplikacja wywołuje metodę Map, otrzymuje wskaźnik do danych zasobów w celu uzyskania dostępu. Środowisko uruchomieniowe gwarantuje, że wskaźnik ma określone wyrównanie w zależności od poziomu funkcji. W przypadku D3D_FEATURE_LEVEL_10_0 i wyższych wskaźnik jest wyrównany do 16 bajtów. Wskaźnik jest wyrównany do 4 bajtów, jeśli wartość jest niższa niż D3D_FEATURE_LEVEL_10_0. Wyrównanie do 16 bajtów umożliwia aplikacji wykonywanie operacji zoptymalizowanych pod kątem SSEna danych w sposób natywny, bez dostosowywania ani kopiowania.

 

Zagadnienia dotyczące wydajności

Najlepiej jest traktować komputer jako maszynę działającą jako architekturę równoległą z dwoma głównymi typami procesorów: co najmniej jednym procesorem CPU i co najmniej jednym procesorem GPU. Podobnie jak w każdej architekturze równoległej, najlepszą wydajność jest osiągana, gdy każdy procesor jest zaplanowany z wystarczającą ilością zadań, aby zapobiec bezczynności i gdy praca jednego procesora nie czeka na pracę innego.

Najgorszy scenariusz równoległości procesora GPU/procesora CPU jest koniecznością wymuszenia oczekiwania na wyniki pracy wykonywanej przez inny procesor. Direct3D 10 próbuje usunąć ten koszt, czyniąc metody ID3D10Device::CopyResource i ID3D10Device::CopySubresourceRegion asynchronicznymi; kopia nie musi być wykonana w momencie zwrócenia przez metodę. Zaletą tego rozwiązania jest to, że aplikacja nie ponosi kosztów wydajności związanych z faktycznym kopiowaniem danych, dopóki procesor CPU nie uzyska dostępu do danych, co następuje w momencie wywołania funkcji Map. Jeśli metoda Map jest wywoływana po tym, jak dane zostały skopiowane, nie wystąpi żadna utrata wydajności. Z drugiej strony, jeśli metoda Map zostanie wywołana przed skopiowaniem danych, nastąpi zatrzymanie potoku.

Wywołania asynchroniczne w Direct3D 10 (które stanowią zdecydowaną większość metod tego trybu, a w szczególności wywołań renderowania) są przechowywane w tzw. buforze poleceń. Ten bufor jest wewnętrzny dla sterownika graficznego i służy do wsadowych wywołań do bazowego sprzętu, dzięki czemu kosztowna zmiana trybu użytkownika na tryb jądra w systemie Microsoft Windows występuje tak rzadko, jak to możliwe.

Bufor poleceń jest opróżniany, co powoduje przełączenie trybu użytkownika/jądra w jednej z czterech sytuacji, które są następujące.

  1. wywoływana jest Present.
  2. ID3D10Device::Flush jest wywoływany.
  3. Bufor poleceń jest pełny; jego rozmiar jest dynamiczny i jest kontrolowany przez system operacyjny i sterownik graficzny.
  4. Procesor CPU wymaga dostępu do wyników polecenia oczekującego na wykonanie w buforze poleceń.

Spośród czterech powyższych sytuacji liczba czwarta jest najbardziej krytyczna dla wydajności. Jeśli aplikacja wykonuje wywołanie ID3D10Device::CopyResource lub ID3D10Device::CopySubresourceRegion, to jest ono kolejkowane w buforze poleceń. Jeśli aplikacja spróbuje zmapować zasób przejściowy, który był celem wywołania kopiowania przed opróżnieniem bufora poleceń, nastąpi zawieszenie potoku, ponieważ nie tylko wywołanie metody Copy musi zostać wykonane, ale również wszystkie inne buforowane polecenia muszą zostać wykonane. Spowoduje to zsynchronizowanie GPU i CPU, ponieważ CPU będzie czekać na dostęp do zasobu przejściowego, podczas gdy GPU będzie opróżniać bufor poleceń i na koniec wypełni zasób wymagany przez CPU. Po zakończeniu kopiowania procesor GPU rozpocznie dostęp do zasobu przejściowego, ale w tym czasie procesor GPU będzie w stanie bezczynności.

Wykonanie tego często w czasie wykonywania poważnie obniży wydajność. Z tego powodu mapowanie zasobów utworzonych z użyciem domyślnym powinno być wykonywane z ostrożnością. Aplikacja musi czekać wystarczająco długo, aby bufor poleceń został opróżniony i w związku z tym wszystkie te polecenia zakończą wykonywanie, zanim spróbuje zamapować odpowiedni zasób przejściowy. Jak długo aplikacja powinna czekać? Co najmniej dwie klatki, ponieważ umożliwi to równoległość między procesorami CPU i procesorami GPU, by maksymalnie wykorzystać ich możliwości. Sposób działania procesora GPU polega na tym, że podczas gdy aplikacja przetwarza ramkę N przez przesyłanie wywołań do buforu poleceń, procesor GPU jest zajęty wykonywanie wywołań z poprzedniej ramki N-1.

Dlatego jeśli aplikacja chce mapować zasób pochodzący z pamięci wideo i wywołuje ID3D10Device::CopyResource lub ID3D10Device::CopySubresourceRegion w ramce N, to wywołanie faktycznie rozpocznie się w ramce N+1, gdy aplikacja przesyła wywołania dla następnej ramki. Kopia powinna zostać zakończona, gdy aplikacja przetwarza ramkę N+2.

Ramka Stan procesora GPU/procesora CPU
N
  • Problemy z CPU powodują przetwarzanie wywołań dla bieżącej ramki.
N+1
  • GPU, które wykonuje wywołania wysyłane z CPU podczas ramki obrazu N.
  • Problemy z procesorem CPU powodują renderowanie wywołań dla bieżącej ramki.
N+2
  • Procesor GPU zakończył wykonywanie wywołań wysyłanych z procesora CPU dla klatki N. Wyniki gotowe.
  • GPU wykonujący wywołania wysyłane z CPU w czasie ramki N+1.
  • Problemy z CPU powodują renderowanie wywołań dla aktualnej klatki.
N+3
  • GPU zakończyło wykonywanie wywołań wysyłanych przez CPU w trakcie klatki N+1. Wyniki są gotowe.
  • GPU, który wykonuje wywołania wysyłane z CPU podczas klatki N+2.
  • Problemy z CPU powodują renderowanie wywołań dla bieżącej klatki.
N+4 ...

 

Zasoby (Direct3D 10)