Udostępnij za pośrednictwem


Zapytania (Direct3D 9)

Istnieje kilka typów zapytań przeznaczonych do wykonywania zapytań dotyczących stanu zasobów. Stan danego zasobu obejmuje stan jednostki przetwarzania grafiki (GPU), stan sterownika lub stan środowiska uruchomieniowego. Aby zrozumieć różnicę między różnymi typami zapytań, musisz zrozumieć stany zapytania. Na poniższym diagramie przejścia stanu opisano poszczególne stany zapytania.

diagram przedstawiający przejścia między stanami zapytań

Na diagramie przedstawiono trzy stany, z których każdy jest definiowany przez okręgi. Każde z wierszy stałych to zdarzenia oparte na aplikacji, które powodują przejście stanu. Linia przerywana to zdarzenie sterowane zasobami, które przełącza zapytanie ze stanu wystawionego na stan sygnalizowany. Każdy z tych stanów ma inny cel:

  • Stan sygnalizowany jest jak stan bezczynności. Obiekt zapytania został wygenerowany i oczekuje na wysłanie zapytania przez aplikację. Po zakończeniu i przeniesieniu zapytania z powrotem do stanu zasygnalizowanego można pobrać odpowiedź na zapytanie.
  • Stan budynku jest jak obszar przejściowy dla zapytania. Ze stanu budynku została wydana kwerenda (wywołując D3DISSUE_BEGIN), ale nie została jeszcze przeniesiona do stanu wystawionego. Gdy aplikacja wystawia koniec zapytania (wywołując D3DISSUE_END), zapytanie przechodzi do stanu wystawionego.
  • Stan wystawiony oznacza, że badany zasób ma kontrolę nad zapytaniem. Po zakończeniu pracy zasobu zasób przenosi maszynę stanu do stanu zasygnalizowanego. Podczas wystawiania stanu aplikacja musi sondować, aby wykryć przejście do stanu zasygnalizowanego. Po przejściu do stanu zasygnalizowanego GetData zwraca wynik zapytania (za pośrednictwem argumentu) do aplikacji.

W poniższej tabeli wymieniono dostępne typy zapytań.

Typ zapytania Zdarzenie problemu Bufor GetData Środowiska wykonawczego Niejawny początek zapytania
PRZEPUSTOWOŚĆ D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9BANDWIDTHTIMINGS Sprzedaż detaliczna/debugowanie N/A
PAMIĘĆ PODRĘCZNA D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9CACHEUTILIZATION Sprzedaż detaliczna/debugowanie N/A
ZDARZENIE D3DISSUE_END BOOL Sprzedaż detaliczna/debugowanie CreateDevice
INTERFEJSY D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9INTERFACETIMINGS Sprzedaż detaliczna/debugowanie N/A
OKLUZJI D3DISSUE_BEGIN, D3DISSUE_END DWORD Sprzedaż detaliczna/debugowanie N/A
POTOKI D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9PIPELINETIMINGS Sprzedaż detaliczna/debugowanie N/A
RESOURCEMANAGER D3DISSUE_END D3DDEVINFO_ResourceManager Tylko debugowanie present
SYGNATURY CZASOWEJ D3DISSUE_END UINT64 Sprzedaż detaliczna/debugowanie N/A
TIMESTAMPDISJOINT D3DISSUE_BEGIN, D3DISSUE_END BOOL Sprzedaż detaliczna/debugowanie N/A
TIMESTAMPFREQ D3DISSUE_END UINT64 Sprzedaż detaliczna/debugowanie N/A
VCACHE D3DISSUE_END D3DDEVINFO_VCACHE Sprzedaż detaliczna/debugowanie CreateDevice
WIERZCHOŁKI D3DISSUE_END D3DDEVINFO_D3DVERTEXSTATS Tylko debugowanie present
WIERZCHOŁKI D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9STAGETIMINGS Sprzedaż detaliczna/debugowanie N/A

 

Niektóre zapytania wymagają zdarzenia rozpoczęcia i zakończenia, a inne wymagają tylko zdarzenia końcowego. Zapytania, które wymagają zdarzenia końcowego, zaczynają się tylko po wystąpieniu innego niejawnego zdarzenia (które znajduje się w tabeli). Wszystkie zapytania zwracają odpowiedź, z wyjątkiem zapytania zdarzenia, którego odpowiedź jest zawsze true. Aplikacja używa stanu zapytania lub zwracanego kodu GetData.

Tworzenie zapytania

Przed utworzeniem zapytania możesz sprawdzić, czy środowisko uruchomieniowe obsługuje zapytania, wywołując CreateQuery z wskaźnikiem NULL w następujący sposób:

IDirect3DQuery9* pEventQuery;

// Create a device pointer m_pd3dDevice

// Create a query object
HRESULT hr = m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, NULL);

Ta metoda zwraca kod powodzenia, jeśli można utworzyć zapytanie; w przeciwnym razie zwraca kod błędu. Po pomyślnym zakończeniu CreateQuery można utworzyć obiekt zapytania w następujący sposób:

IDirect3DQuery9* pEventQuery;
m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);

Jeśli to wywołanie powiedzie się, zostanie utworzony obiekt zapytania. Zapytanie jest zasadniczo bezczynne w stanie sygnału (z niezainicjowaną odpowiedzią) oczekując na wydanie. Po zakończeniu pracy z zapytaniem zwolnij go jak każdy inny interfejs.

Problem z zapytaniem

Aplikacja zmienia stan zapytania, wydając zapytanie. Oto przykład wystawiania zapytania:

IDirect3DQuery9* pEventQuery;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);

// Issue a Begin event
pEventQuery->Issue(D3DISSUE_BEGIN);

or

// Issue an End event
pEventQuery->Issue(D3DISSUE_END);

Zapytanie w stanie sygnału będzie przechodzić w następujący sposób po wystawieniu:

Typ problemu Zapytanie przechodzi do . . .
D3DISSUE_BEGIN Stan budynku.
D3DISSUE_END Stan wystawiony.

 

Zapytanie w stanie budynku będzie przechodzić w następujący sposób po wystawieniu:

Typ problemu Zapytanie przechodzi do . . .
D3DISSUE_BEGIN (Brak przejścia, pozostaje w stanie budynku. Uruchamia ponownie nawias zapytania).
D3DISSUE_END Stan wystawiony.

 

Zapytanie w stanie wystawionym będzie przechodzić w następujący sposób po wystawieniu:

Typ problemu Zapytanie przechodzi do . . .
D3DISSUE_BEGIN Stan kompilacji i ponownie uruchamia nawias zapytania.
D3DISSUE_END Stan wystawiony po porzuceniu istniejącego zapytania.

 

Sprawdź stan zapytania i uzyskaj odpowiedź na zapytanie

GetData wykonuje dwie czynności:

  1. Zwraca stan zapytania w kodzie zwrotnym.
  2. Zwraca odpowiedź na zapytanie w pData.

Z każdego z trzech stanów zapytania poniżej przedstawiono kody powrotne GetData:

Stan zapytania Kod powrotny GetData
Sygnalizowane S_OK
Budowla Kod błędu
Wydane S_FALSE

 

Jeśli na przykład zapytanie znajduje się w stanie wystawionym, a odpowiedź na zapytanie jest niedostępna, GetData zwraca S_FALSE. Gdy zasób zakończy pracę i aplikacja wystawi koniec zapytania, zasób przenosi zapytanie do stanu zasygnalizowanego. Ze stanu zasygnalizowanego GetData zwraca S_OK, co oznacza, że odpowiedź na zapytanie jest również zwracana w pData. Na przykład poniżej przedstawiono sekwencję zdarzeń zwracających liczbę pikseli (lub próbki, gdy włączono wieloprzykładowe próbkowanie) w sekwencji renderowania:

  • Utwórz zapytanie.
  • Wystawianie zdarzenia rozpoczęcia.
  • Rysuj coś.
  • Wystawianie zdarzenia końcowego.

Poniżej znajduje się odpowiednia sekwencja kodu:

IDirect3DQuery9* pOcclusionQuery;
DWORD numberOfSamplesDrawn;

m_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);

// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue(D3DISSUE_BEGIN);

// API render loop
...
Draw(...)
...

// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue(D3DISSUE_END);

// Force the driver to execute the commands from the command buffer.
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pOcclusionQuery->GetData( &numberOfSamplesDrawn, 
                                  sizeof(DWORD), D3DGETDATA_FLUSH ))
    ;

// To get the number of pixels drawn when multisampling is enabled,
// divide numberOfSamplesDrawn by the sample count of the render target.

Te wiersze kodu wykonują kilka czynności:

  • Wywołaj GetData, aby zwrócić liczbę pikseli/próbek narysowanych.
  • Określ D3DGETDATA_FLUSH, aby umożliwić zasobowi przejście zapytania do stanu sygnału.
  • Sonduj zasób zapytania, wywołując GetData z pętli. O ile GetData zwraca S_FALSE, oznacza to, że zasób nie zwrócił jeszcze odpowiedzi.

Wartość zwracana GetData zasadniczo informuje o stanie zapytania. Możliwe wartości to S_OK, S_FALSE i błąd. Nie należy wywoływać GetData w zapytaniu, które znajduje się w stanie budynku.

  • S_OK oznacza, że zasób (procesor GPU lub sterownik lub środowisko uruchomieniowe) jest gotowy. Zapytanie powraca do stanu zasygnalizowanego. Odpowiedź (jeśli istnieje) jest zwracana przez GetData.
  • S_FALSE oznacza, że zasób (procesor GPU lub sterownik lub środowisko uruchomieniowe) nie może jeszcze zwrócić odpowiedzi. Może to być spowodowane tym, że procesor GPU nie został ukończony lub jeszcze nie widział pracy.
  • Błąd oznacza, że zapytanie wygenerowało błąd, z którego nie można go odzyskać. Może to być przypadek, gdy urządzenie zostanie utracone podczas wykonywania zapytania. Po wygenerowaniu błędu (innego niż S_FALSE) zapytanie musi zostać ponownie stworzone, co spowoduje ponowne uruchomienie sekwencji zapytań ze stanu sygnalizowanego.

Zamiast określać D3DGETDATA_FLUSH, która udostępnia więcej up-to-date informacji, można podać zero, co jest bardziej lekkim sprawdzaniem, czy zapytanie znajduje się w stanie wystawionym. Podanie zera spowoduje, że getData nie opróżnić buforu poleceń. Z tego powodu należy zachować ostrożność, aby uniknąć nieskończonych pętli (zobacz GetData, aby uzyskać szczegółowe informacje). Ponieważ środowisko uruchomieniowe kolejkuje pracę w buforze poleceń, D3DGETDATA_FLUSH jest mechanizmem opróżniania buforu poleceń do sterownika (a tym samym procesora GPU; zobacz Dokładne profilowanie wywołań interfejsu API Direct3D (Direct3D 9)). Podczas opróżniania buforu poleceń zapytanie może przejść do stanu sygnalizowanego.

Przykład: zapytanie o zdarzenie

Zapytanie o zdarzenie nie obsługuje zdarzenia rozpoczęcia.

  • Utwórz zapytanie.
  • Wystawianie zdarzenia końcowego.
  • Sonduj, dopóki procesor GPU nie będzie w stanie bezczynności.
  • Wystawianie zdarzenia końcowego.
IDirect3DQuery9* pEventQuery = NULL;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);

// Add an end marker to the command buffer queue.
pEventQuery->Issue(D3DISSUE_END);

// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ))
    ;

... // API calls

// Add an end marker to the command buffer queue.
pEventQuery->Issue(D3DISSUE_END);

// Force the driver to execute the commands from the command buffer.
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ))
    ;

Jest to sekwencja poleceń używanych przez zapytanie zdarzeń do profilowania wywołań interfejsu programowania aplikacji (API) (zobacz Dokładne profilowanie wywołań interfejsu API Direct3D (Direct3D 9)). Ta sekwencja używa znaczników, aby kontrolować ilość pracy w buforze poleceń.

Należy pamiętać, że aplikacje powinny zwrócić szczególną uwagę na duży koszt związany z opróżnieniem buforu poleceń, ponieważ powoduje to przełączenie systemu operacyjnego do trybu jądra, co powoduje naliczanie znacznej kary za wydajność. Aplikacje powinny również pamiętać o marnowaniu cykli procesora CPU przez oczekiwanie na ukończenie zapytań.

Zapytania to optymalizacja, która ma być używana podczas renderowania w celu zwiększenia wydajności. W związku z tym nie jest korzystne, aby poświęcić czas na zakończenie zapytania. Jeśli zapytanie jest wystawiane i gdy wyniki nie są jeszcze gotowe do czasu sprawdzenia ich przez aplikację, próba optymalizacji nie powiodła się i renderowanie powinno być kontynuowane w normalny sposób.

Klasycznym przykładem jest to podczas Occlusion Culling. Zamiast podczas pętli powyżej, aplikacja korzystająca z zapytań może zaimplementować wyłuskanie okluzji, aby sprawdzić, czy zapytanie zostało zakończone w czasie, gdy potrzebuje wyniku. Jeśli zapytanie nie zostało zakończone, kontynuuj (w najgorszym scenariuszu), tak jakby testowany obiekt nie był occluded (tj. widoczny) i renderował go. Kod będzie wyglądać podobnie do poniższego.

IDirect3DQuery9* pOcclusionQuery = NULL;
m_pD3DDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery );

// Add a begin marker to the command buffer queue.
pOcclusionQuery->Issue( D3DISSUE_BEGIN );

... // API calls

// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue( D3DISSUE_END );

// Avoid flushing and letting the CPU go idle by not using a while loop.
// Check if queries are finished:
DWORD dwOccluded = 0;
if( S_FALSE == pOcclusionQuery->GetData( &dwOccluded, sizeof(DWORD), 0 ) )
{
    // Query is not done yet or object not occluded; avoid flushing/wait by continuing with worst-case scenario
    pSomeComplexMesh->Render();
}
else if( dwOccluded != 0 )
{
    // Query is done and object is not occluded.
    pSomeComplexMesh->Render();
}

tematy zaawansowane