Aprimoramentos do Direct3D 9Ex

Este tópico descreve o suporte adicionado do Windows 7 para o Modo Flip Presente e suas estatísticas presentes associadas no Direct3D 9Ex e no Gerenciador de Janelas da Área de Trabalho. Os aplicativos de destino incluem aplicativos de apresentação baseados em taxa de quadros ou vídeo. Os aplicativos que usam o Modo Flip Direct3D 9Ex Presentes reduzem a carga de recursos do sistema quando o DWM está habilitado. Apresentar aprimoramentos de estatísticas associados ao Modo Flip Presente permitem que aplicativos Direct3D 9Ex controlem melhor a taxa de apresentação fornecendo comentários em tempo real e mecanismos de correção. Explicações detalhadas e ponteiros para recursos de exemplo são incluídos.

Este tópico inclui as seções a seguir.

O que melhorou no Direct3D 9Ex para Windows 7

Apresentação do Modo Flip do Direct3D 9Ex é um modo aprimorado de apresentar imagens no Direct3D 9Ex que entrega com eficiência imagens renderizadas ao DWM (Gerenciador de Janelas da Área de Trabalho) do Windows 7 para composição. A partir do Windows Vista, o DWM compõe toda a Área de Trabalho. Quando o DWM está habilitado, os aplicativos de modo em janela apresentam seu conteúdo na Área de Trabalho usando um método chamado Modo Blt Presente para DWM (ou Modelo Blt). Com o Modelo Blt, o DWM mantém uma cópia da superfície renderizada do Direct3D 9Ex para composição da área de trabalho. Quando o aplicativo é atualizado, o novo conteúdo é copiado para a superfície do DWM por meio de um blt. Para aplicativos que contêm conteúdo Direct3D e GDI, os dados GDI também são copiados para a superfície do DWM.

Disponível no Windows 7, o Modo Inverter Apresentar ao DWM (ou Flip Model) é um novo método de apresentação que essencialmente permite passar identificadores de superfícies de aplicativo entre aplicativos de modo de janela e DWM. Além de salvar recursos, o Flip Model dá suporte a estatísticas atuais aprimoradas.

As estatísticas atuais são informações de tempo de quadro que os aplicativos podem usar para sincronizar fluxos de vídeo e áudio e se recuperar de falhas de reprodução de vídeo. As informações de tempo de quadro nas estatísticas atuais permitem que os aplicativos ajustem a taxa de apresentação de seus quadros de vídeo para uma apresentação mais suave. No Windows Vista, onde o DWM mantém uma cópia correspondente da superfície de quadro para composição da área de trabalho, os aplicativos podem usar estatísticas atuais fornecidas pelo DWM. Esse método de obtenção das estatísticas atuais ainda estará disponível no Windows 7 para aplicativos existentes.

No Windows 7, os aplicativos baseados em Direct3D 9Ex que adotam o Modelo Flip devem usar APIs D3D9Ex para obter as estatísticas atuais. Quando o DWM está habilitado, o modo em janelas e os aplicativos Direct3D 9Ex no modo exclusivo de tela inteira podem esperar as mesmas informações de estatísticas presentes ao usar o Flip Model. As estatísticas atuais do Modelo flip do Direct3D 9Ex permitem que os aplicativos consultem as estatísticas atuais em tempo real, em vez de depois que o quadro é mostrado na tela; as mesmas informações de estatísticas presentes estão disponíveis para o modo de janela Flip-Model aplicativos habilitados como aplicativos de tela inteira; um sinalizador adicionado nas APIs D3D9Ex permite que aplicativos Flip Model descartem efetivamente quadros atrasados no momento da apresentação.

O Direct3D 9Ex Flip Model deve ser usado por novos aplicativos de apresentação baseados em taxa de quadros ou vídeo que se destinam ao Windows 7. Devido à sincronização entre o DWM e o runtime do Direct3D 9Ex, os aplicativos que usam o Flip Model devem especificar entre 2 a 4 backbuffers para garantir uma apresentação suave. Os aplicativos que usam as informações de estatísticas atuais se beneficiarão do uso de aprimoramentos de estatísticas presentes habilitados para Flip Model.

Apresentação do modo flip do Direct3D 9EX

As melhorias de desempenho do Modo Flip Direct3D 9Ex Presente são significativas no sistema quando o DWM está ativado e quando o aplicativo está no modo de janela, em vez de no modo exclusivo de tela inteira. A tabela e a ilustração a seguir mostram uma comparação simplificada de usos de largura de banda de memória e leituras e gravações do sistema de aplicativos em janelas que escolhem Flip Model versus o Blt Model de uso padrão.

Modo Blt presente no DWM D3D9Ex Flip Mode Present to DWM
1. O aplicativo atualiza seu quadro (Gravação)
1. O aplicativo atualiza seu quadro (Gravação)
2. O runtime do Direct3D copia o conteúdo da superfície para uma superfície de redirecionamento de DWM (Leitura, Gravação)
2. O runtime do Direct3D passa a superfície do aplicativo para o DWM
3. Após a conclusão da cópia da superfície compartilhada, o DWM renderiza a superfície do aplicativo na tela (Leitura, Gravação)
3. O DWM renderiza a superfície do aplicativo na tela (Leitura, Gravação)

ilustração de uma comparação do modelo blt e do modelo de inversão

O Modo de Inversão Presente reduz o uso de memória do sistema reduzindo o número de leituras e gravações pelo runtime do Direct3D para a composição de quadro em janelas pelo DWM. Isso reduz o consumo de energia do sistema e o uso geral de memória.

Os aplicativos podem aproveitar o Modo Flip direct3D 9Ex apresentar aprimoramentos de estatísticas quando o DWM está ativado, independentemente de o aplicativo estiver no modo de janela ou no modo exclusivo de tela inteira.

Modelo de programação e APIs

Novos aplicativos de avaliação de taxa de quadros ou vídeo que usam APIs Direct3D 9Ex no Windows 7 podem aproveitar a economia de memória e energia e a apresentação aprimorada oferecida pelo Modo Flip Presente durante a execução no Windows 7. (Ao executar em versões anteriores do Windows, o runtime do Direct3D usa como padrão o aplicativo para o Modo Blt Presente.)

Flip Mode Present implica que o aplicativo pode aproveitar os mecanismos de correção e comentários de estatísticas presentes em tempo real quando o DWM está ativado. No entanto, os aplicativos que usam o Modo Flip Presente devem estar cientes das limitações ao usar a renderização simultânea da API GDI.

Você pode modificar aplicativos existentes para aproveitar o Flip Mode Present, com os mesmos benefícios e ressalvas que os aplicativos recém-desenvolvidos.

Como aceitar o modelo flip do Direct3D 9Ex

Os aplicativos Direct3D 9Ex direcionados ao Windows 7 podem optar pelo Modelo flip criando a cadeia de troca com o valor de enumeração D3DSWAPEFFECT_FLIPEX . Para aceitar o Modelo flip, os aplicativos especificam a estrutura D3DPRESENT_PARAMETERS e, em seguida, passam um ponteiro para essa estrutura quando chamam a API IDirect3D9Ex::CreateDeviceEx . Esta seção descreve como os aplicativos destinados ao Windows 7 usam IDirect3D9Ex::CreateDeviceEx para aceitar o Modelo flip. Para obter mais informações sobre a API IDirect3D9Ex::CreateDeviceEx , consulte IDirect3D9Ex::CreateDeviceEx no MSDN.

Para sua conveniência, a sintaxe de D3DPRESENT_PARAMETERS e IDirect3D9Ex::CreateDeviceEx é repetida aqui.

HRESULT CreateDeviceEx(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS* pPresentationParameters,
  D3DDISPLAYMODEEX *pFullscreenDisplayMode,
  IDirect3DDevice9Ex **ppReturnedDeviceInterface
);
typedef struct D3DPRESENT_PARAMETERS {
    UINT BackBufferWidth, BackBufferHeight;
    D3DFORMAT BackBufferFormat;
    UINT BackBufferCount;
    D3DMULTISAMPLE_TYPE MultiSampleType;
    DWORD MultiSampleQuality;
    D3DSWAPEFFECT SwapEffect;
    HWND hDeviceWindow;
    BOOL Windowed;
    BOOL EnableAutoDepthStencil;
    D3DFORMAT AutoDepthStencilFormat;
    DWORD Flags;
    UINT FullScreen_RefreshRateInHz;
    UINT PresentationInterval;
} D3DPRESENT_PARAMETERS, *LPD3DPRESENT_PARAMETERS;

Ao modificar aplicativos Direct3D 9Ex para Windows 7 para aceitar o Modelo flip, você deve considerar os seguintes itens sobre os membros especificados do D3DPRESENT_PARAMETERS:

BackBufferCount

(Somente Windows 7)

Quando SwapEffect é definido como o novo D3DSWAPEFFECT_FLIPEX tipo de efeito de cadeia de troca, a contagem de buffers de fundo deve ser igual ou maior que 2, para evitar que uma penalidade de desempenho do aplicativo como resultado da espera no buffer Presente anterior seja liberado pelo DWM.

Quando o aplicativo também usa estatísticas presentes associadas a D3DSWAPEFFECT_FLIPEX, recomendamos que você defina a contagem de buffers de fundo como de 2 a 4.

Usar D3DSWAPEFFECT_FLIPEX no Windows Vista ou versões anteriores do sistema operacional retornará falha de CreateDeviceEx.

Swapeffect

(Somente Windows 7)

O novo tipo de efeito de cadeia de troca de D3DSWAPEFFECT_FLIPEX designa quando um aplicativo está adotando o Modo Flip Presente no DWM. Ele permite ao aplicativo um uso mais eficiente de memória e energia e também permite que o aplicativo aproveite as estatísticas presentes em tela inteira no modo de janela. O comportamento do aplicativo de tela inteira não é afetado. Se Windowed for definido como TRUE e SwapEffect for definido como D3DSWAPEFFECT_FLIPEX, o runtime criará um buffer de fundo extra e girará qualquer identificador que pertença ao buffer que se torna o buffer frontal no momento da apresentação.

Sinalizadores

(Somente Windows 7)

O sinalizador D3DPRESENTFLAG_LOCKABLE_BACKBUFFER não poderá ser definido se SwapEffect estiver definido como o novo D3DSWAPEFFECT_FLIPEX tipo de efeito de cadeia de troca.

Diretrizes de design para aplicativos de modelo Flip direct3D 9Ex

Use as diretrizes nas seções a seguir para projetar seus aplicativos Direct3D 9Ex Flip Model.

Usar o modo flip presente em um HWND separado do modo Blt presente

Os aplicativos devem usar o modo flip direct3D 9Ex presente em um HWND que também não é direcionado por outras APIs, incluindo o Modo Blt Apresentar Direct3D 9Ex, outras versões do Direct3D ou GDI. O Modo Inverter Presente pode ser usado para apresentar a janelas filho; Ou seja, os aplicativos podem usar o Flip Model quando ele não é misturado com o Modelo Blt no mesmo HWND, conforme mostrado nas ilustrações a seguir.

ilustração da janela pai direct3d e uma janela filho gdi, cada uma com seu próprio hwnd

ilustração da janela pai gdi e uma janela filho direct3d, cada uma com seu próprio hwnd

Como o Modelo Blt mantém uma cópia adicional da superfície, a GDI e outros conteúdos direct3D podem ser adicionados ao mesmo HWND por meio de atualizações por etapas do Direct3D e do GDI. Usando o Modelo flip, somente o conteúdo direct3D 9Ex em cadeias de troca D3DSWAPEFFECT_FLIPEX que são passadas para DWM ficará visível. Todas as outras atualizações de conteúdo do Blt Model Direct3D ou GDI serão ignoradas, conforme mostrado nas ilustrações a seguir.

ilustração do texto gdi que pode não ser exibido se o modelo de inversão for usado e o conteúdo direct3d e gdi estiverem no mesmo hwnd

ilustração do conteúdo direct3d e gdi no qual o dwm está habilitado e o aplicativo está no modo de janela

Portanto, o Flip Model deve ser habilitado para superfícies de buffers de cadeia de troca em que o Direct3D 9Ex Flip Model sozinho é renderizado para todo o HWND.

Não usar o modelo de inversão com ScrollWindow ou ScrollWindowEx da GDI

Alguns aplicativos Direct3D 9Ex usam as funções ScrollWindow ou ScrollWindowEx da GDI para atualizar o conteúdo da janela quando um evento de rolagem do usuário é disparado. ScrollWindow e ScrollWindowEx executam blts de conteúdo da janela na tela à medida que uma janela é rolada. Essas funções também exigem atualizações do Modelo Blt para conteúdo GDI e Direct3D 9Ex. Os aplicativos que usam qualquer função não exibirão necessariamente o conteúdo da janela visível rolando na tela quando o aplicativo estiver no modo de janela e o DWM estiver habilitado. Recomendamos que você não use as APIs ScrollWindow e ScrollWindowEx da GDI em seus aplicativos e, em vez disso, redesenhe seu conteúdo na tela em resposta à rolagem.

Usar uma cadeia de troca de D3DSWAPEFFECT_FLIPEX por HWND

Os aplicativos que usam o Flip Model não devem usar várias cadeias de troca do Modelo de Inversão direcionadas ao mesmo HWND.

Sincronização de quadros de aplicativos de modelo Flip Direct3D 9Ex

As estatísticas presentes são informações de tempo de quadro que os aplicativos de mídia usam para sincronizar fluxos de vídeo e áudio e se recuperar de falhas de reprodução de vídeo. Para habilitar a disponibilidade das estatísticas atuais, o aplicativo Direct3D 9Ex deve garantir que o parâmetro BehaviorFlags que o aplicativo passa para IDirect3D9Ex::CreateDeviceEx contenha o sinalizador de comportamento do dispositivo D3DCREATE_ENABLE_PRESENTSTATS.

Para conveniência, a sintaxe de IDirect3D9Ex::CreateDeviceEx é repetida aqui.

HRESULT CreateDeviceEx(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS* pPresentationParameters,
  D3DDISPLAYMODEEX *pFullscreenDisplayMode,
  IDirect3DDevice9Ex **ppReturnedDeviceInterface
);

O Direct3D 9Ex Flip Model adiciona o sinalizador de apresentação D3DPRESENT_FORCEIMMEDIATE que impõe o comportamento de sinalizador de apresentação D3DPRESENT_INTERVAL_IMMEDIATE. O aplicativo Direct3D 9Ex especifica esses sinalizadores de apresentação no parâmetro dwFlags que o aplicativo passa para IDirect3DDevice9Ex::P resentEx, conforme mostrado aqui.

HRESULT PresentEx(
  CONST RECT *pSourceRect,
  CONST RECT *pDestRect,
  HWND hDestWindowOverride,
  CONST RGNDATA *pDirtyRegion,
  DWORD dwFlags
);

Ao modificar seu aplicativo Direct3D 9Ex para Windows 7, considere as seguintes informações sobre os sinalizadores de apresentação D3DPRESENT especificados:

D3DPRESENT_DONOTFLIP

Esse sinalizador só está disponível no modo de tela inteira ou

(Somente Windows 7)

quando o aplicativo define o membro SwapEffect de D3DPRESENT_PARAMETERS como D3DSWAPEFFECT_FLIPEX em uma chamada para CreateDeviceEx.

D3DPRESENT_FORCEIMMEDIATE

(Somente Windows 7)

Esse sinalizador só poderá ser especificado se o aplicativo definir o membro SwapEffect de D3DPRESENT_PARAMETERS como D3DSWAPEFFECT_FLIPEX em uma chamada para CreateDeviceEx. O aplicativo pode usar esse sinalizador para atualizar imediatamente uma superfície com vários quadros posteriormente na fila DWM Present, essencialmente ignorando quadros intermediários.

Aplicativos habilitados para FlipEx com janelas podem usar esse sinalizador para atualizar imediatamente uma superfície com um quadro que está posteriormente na fila DWM Present, ignorando quadros intermediários. Isso é especialmente útil para aplicativos de mídia que desejam descartar quadros que foram detectados como atrasados e apresentam quadros subsequentes em tempo de composição. IDirect3DDevice9Ex::P resentEx retornará um erro de parâmetro inválido se esse sinalizador for especificado incorretamente.

Para obter as informações de estatísticas atuais, o aplicativo obtém a estrutura D3DPRESENTSTATS chamando a API IDirect3DSwapChain9Ex::GetPresentStatistics .

A estrutura D3DPRESENTSTATS contém estatísticas sobre chamadas IDirect3DDevice9Ex::P resentEx . O dispositivo deve ser criado usando uma chamada IDirect3D9Ex::CreateDeviceEx com o sinalizador D3DCREATE_ENABLE_PRESENTSTATS . Caso contrário, os dados retornados por GetPresentStatistics são indefinidos. Uma cadeia de troca Direct3D 9Ex habilitada para Flip-Model fornece informações de estatísticas presentes nos modos de janela e tela inteira.

Para cadeias de troca Direct3D 9Ex habilitadas para Blt-Model no modo de janela, todos os valores de estrutura D3DPRESENTSTATS serão zeros.

Para estatísticas presentes do FlipEx, GetPresentStatistics retorna D3DERR_PRESENT_STATISTICS_DISJOINT nas seguintes situações:

  • Primeira chamada para GetPresentStatistics de todos os tempos, o que indica o início de uma sequência
  • Transição do DWM de ativado para desativado
  • Alteração de modo: o modo em janelas para ou de tela inteira ou de tela inteira para transições de tela inteira

Para conveniência, a sintaxe de GetPresentStatistics é repetida aqui.

HRESULT GetPresentStatistics(
  D3DPRESENTSTATS * pPresentationStatistics
);

O método IDirect3DSwapChain9Ex::GetLastPresentCount retorna o último PresentCount, ou seja, a ID Presente da última chamada Presente bem-sucedida que foi feita por um dispositivo de exibição associado à cadeia de troca. Esta ID Presente é o valor do membro PresentCount da estrutura D3DPRESENTSTATS . Para cadeias de troca Direct3D 9Ex habilitadas para Blt-Model, enquanto no modo em janela, todos os valores de estrutura D3DPRESENTSTATS serão zeros.

Para conveniência, a sintaxe de IDirect3DSwapChain9Ex::GetLastPresentCount é repetida aqui.

HRESULT GetLastPresentCount(
  UINT * pLastPresentCount
);

Ao modificar seu aplicativo Direct3D 9Ex para Windows 7, considere as seguintes informações sobre a estrutura D3DPRESENTSTATS :

  • O valor PresentCount que GetLastPresentCount retorna não é atualizado quando uma chamada PresentEx com D3DPRESENT_DONOTWAIT especificada no parâmetro dwFlags retorna falha.
  • Quando PresentEx é chamado com D3DPRESENT_DONOTFLIP, uma chamada GetPresentStatistics é bem-sucedida, mas não retorna uma estrutura D3DPRESENTSTATS atualizada quando o aplicativo está no modo de janela.
  • PresentRefreshCount versus SyncRefreshCount em D3DPRESENTSTATS:
    • PresentRefreshCount é igual a SyncRefreshCount quando o aplicativo é apresentado em cada vsync.
    • SyncRefreshCount é obtido no intervalo vsync quando o presente foi enviado, SyncQPCTime é aproximadamente o tempo associado ao intervalo vsync.
typedef struct _D3DPRESENTSTATS {
    UINT PresentCount;
    UINT PresentRefreshCount;
    UINT SyncRefreshCount;
    LARGE_INTEGER SyncQPCTime;
    LARGE_INTEGER SyncGPUTime;
} D3DPRESENTSTATS;

Sincronização de quadros para aplicativos em janelas quando o DWM está desativado

Quando o DWM está desativado, os aplicativos em janela são exibidos diretamente na tela do monitor sem passar por uma cadeia de inversão. No Windows Vista, não há suporte para obter informações de estatísticas de quadro para aplicativos em janelas quando o DWM está desativado. Para manter uma API em que os aplicativos não precisam ter reconhecimento de DWM, o Windows 7 retornará informações de estatísticas de quadro para aplicativos em janelas quando o DWM estiver desativado. As estatísticas de quadro retornadas quando o DWM está desativado são somente estimativas.

Walk-Through de um modelo flip direct3D 9Ex e exemplo de estatísticas presentes

Para aceitar a apresentação do FlipEx para o exemplo direct3D 9Ex

  1. Verifique se o aplicativo de exemplo está em execução na versão do sistema operacional Windows 7 ou posterior.
  2. Defina o membro SwapEffect de D3DPRESENT_PARAMETERS como D3DSWAPEFFECT_FLIPEX em uma chamada para CreateDeviceEx.
    OSVERSIONINFO version;
    ZeroMemory(&version, sizeof(version));
    version.dwOSVersionInfoSize = sizeof(version);
    GetVersionEx(&version);
    
    // Sample would run only on Win7 or higher
    // Flip Model present and its associated present statistics behavior are only available on Windows 7 or higher operating system
    bool bIsWin7 = (version.dwMajorVersion > 6) || 
        ((version.dwMajorVersion == 6) && (version.dwMinorVersion >= 1));

    if (!bIsWin7)
    {
        MessageBox(NULL, L"This sample requires Windows 7 or higher", NULL, MB_OK);
        return 0;
    }

Para também aceitar o exemplo estatísticas presentes associadas ao FlipEx para Direct3D 9Ex

    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_FLIPEX;        // Opts into Flip Model present for D3D9Ex swapchain
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = 256;                
    d3dpp.BackBufferHeight = 256;
    d3dpp.BackBufferCount = QUEUE_SIZE;
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    g_iWidth = d3dpp.BackBufferWidth;
    g_iHeight = d3dpp.BackBufferHeight;

    // Create the D3DDevice with present statistics enabled - set D3DCREATE_ENABLE_PRESENTSTATS for behaviorFlags parameter
    if(FAILED(g_pD3D->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_ENABLE_PRESENTSTATS,
                                      &d3dpp, NULL, &g_pd3dDevice)))
    {
        return E_FAIL;
    }

Para evitar, detecte e se recupere de falhas

  1. Fila Chamadas presentes: a contagem recomendada de backbuffer é de 2 a 4.

  2. O exemplo direct3D 9Ex adiciona um backbuffer implícito, o comprimento real da fila Presente é a contagem de backbuffer + 1.

  3. Criar auxiliar Apresentar estrutura de fila para armazenar todas as IDs Presentes (PresentCount) enviadas com êxito e PresentRefreshCount associadas e calculadas/esperadas.

  4. Para detectar a ocorrência de falha:

    • Chame GetPresentStatistics.
    • Obtenha a ID presente (PresentCount) e a contagem vsync em que o quadro é mostrado (PresentRefreshCount) do quadro cujas estatísticas atuais são obtidas.
    • Recupere o PresentRefreshCount esperado (TargetRefresh no código de exemplo) associado à ID Presente.
    • Se PresentRefreshCount real for posterior ao esperado, ocorrerá uma falha.
  5. Para se recuperar da falha:

    • Calcule quantos quadros ignorar (g_ variável iImmediates no código de exemplo).
    • Apresente os quadros ignorados com intervalo D3DPRESENT_FORCEIMMEDIATE.

Considerações sobre detecção e recuperação de falhas

  1. A recuperação de falha usa N (variável g_iQueueDelay no código de exemplo) de Chamadas presentes em que N (g_iQueueDelay) é igual a g_iImmediates mais o comprimento da fila Presente, ou seja:

    • Ignorando quadros com o intervalo Presente D3DPRESENT_FORCEIMMEDIATE, mais
    • Presentes enfileirados que precisam ser processados
  2. Defina um limite para o comprimento da falha (GLITCH_RECOVERY_LIMIT no exemplo). Se o aplicativo de exemplo não puder se recuperar de uma falha muito longa (ou seja, 1 segundo ou 60 vsyncs no monitor de 60Hz), pule sobre a animação intermitente e redefina a fila auxiliar Presente.

VOID Render()
{
    g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    g_pd3dDevice->BeginScene();

    // Compute new animation parameters for time and frame based animations

    // Time-based is a difference between base and current SyncRefreshCount
    g_aTimeBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_LastSyncRefreshCount - g_SyncRefreshCount;
    // Frame-based is incrementing frame value
    g_aFrameBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_iFrameNumber;

    RenderBlurredMesh(TRUE);    // Time-based
    RenderBlurredMesh(FALSE);   // Frame-based

    g_iBlurHistoryCounter = (g_iBlurHistoryCounter + 1) % BLUR_FRAMES;

    DrawText();

    g_pd3dDevice->EndScene();

    // Performs glitch recovery if glitch was detected
    if (g_bGlitchRecovery && (g_iImmediates > 0))
    {
        // If we have present immediates queued as a result of glitch detected, issue forceimmediate Presents for glitch recovery 
        g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_FORCEIMMEDIATE);
        g_iImmediates--;
        g_iShowingGlitchRecovery = MESSAGE_SHOW;
    }
    // Otherwise, Present normally
    else
    {
        g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, 0);
    }

    // Add to helper Present queue: PresentID + expected present refresh count of last submitted Present
    UINT PresentCount;
    g_pd3dSwapChain->GetLastPresentCount(&PresentCount);
    g_Queue.QueueFrame(PresentCount, g_TargetRefreshCount);
    
    // QueueDelay specifies # Present calls to be processed before another glitch recovery attempt
    if (g_iQueueDelay > 0)
    {
        g_iQueueDelay--;
    }

    if (g_bGlitchRecovery)
    {
        // Additional DONOTFLIP presents for frame conversions, which basically follows the same logic, but without rendering
        for (DWORD i = 0; i < g_iDoNotFlipNum; i++)
        {
            if (g_TargetRefreshCount != -1)
            {
                g_TargetRefreshCount++;
                g_iFrameNumber++;
                g_aTimeBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_LastSyncRefreshCount - g_SyncRefreshCount;
                g_aFrameBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_iFrameNumber;
                g_iBlurHistoryCounter = (g_iBlurHistoryCounter + 1) % BLUR_FRAMES;
            }
            
            if (g_iImmediates > 0)
            {
                g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_FORCEIMMEDIATE | D3DPRESENT_DONOTFLIP);
                g_iImmediates--;
            }
            else
            {
                g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_DONOTFLIP);
            }
            UINT PresentCount;
            g_pd3dSwapChain->GetLastPresentCount(&PresentCount);
            g_Queue.QueueFrame(PresentCount, g_TargetRefreshCount);

            if (g_iQueueDelay > 0)
            {
                g_iQueueDelay--;
            }
        }
    }

    // Check Present Stats info for glitch detection 
    D3DPRESENTSTATS PresentStats;

    // Obtain present statistics information for successfully displayed presents
    HRESULT hr = g_pd3dSwapChain->GetPresentStats(&PresentStats);

    if (SUCCEEDED(hr))
    {
        // Time-based update
        g_LastSyncRefreshCount = PresentStats.SyncRefreshCount;
        if ((g_SyncRefreshCount == -1) && (PresentStats.PresentCount != 0))
        {
            // First time SyncRefreshCount is reported, use it as base
            g_SyncRefreshCount = PresentStats.SyncRefreshCount;
        }

        // Fetch frame from the queue...
        UINT TargetRefresh = g_Queue.DequeueFrame(PresentStats.PresentCount);

        // If PresentStats returned a really old frame that we no longer have in the queue, just don't do any glitch detection
        if (TargetRefresh == FRAME_NOT_FOUND)
            return;

        if (g_TargetRefreshCount == -1)
        {
            // This is first time issued frame is confirmed by present stats, so fill target refresh count for all frames in the queue
            g_TargetRefreshCount = g_Queue.FillRefreshCounts(PresentStats.PresentCount, g_SyncRefreshCount);
        } 
        else
        {
            g_TargetRefreshCount++;
            g_iFrameNumber++;

            // To determine whether we're glitching, see if our estimated refresh count is confirmed
            // if the frame is displayed later than the expected vsync count
            if (TargetRefresh < PresentStats.PresentRefreshCount)
            {
                // then, glitch is detected!

                // If glitch is too big, don't bother recovering from it, just jump animation
                if ((PresentStats.PresentRefreshCount - TargetRefresh) > GLITCH_RECOVERY_LIMIT)
                {
                    g_iStartFrame += PresentStats.SyncRefreshCount - g_SyncRefreshCount;
                    ResetAnimation();
                    if (g_bGlitchRecovery)
                        g_iGlitchesInaRow++;    
                } 
                // Otherwise, compute number of immediate presents to recover from it -- if we?re not still trying to recover from another glitch
                else if (g_iQueueDelay == 0)
                {
                      // skip frames to catch up to expected refresh count
                    g_iImmediates = PresentStats.PresentRefreshCount - TargetRefresh;
                    // QueueDelay specifies # Present calls before another glitch recovery 
                    g_iQueueDelay = g_iImmediates + QUEUE_SIZE;
                    if (g_bGlitchRecovery)
                        g_iGlitchesInaRow++;
                }
            }
            else
            {
                // No glitch, reset glitch count
                g_iGlitchesInaRow = 0;
            }
        }
    }
    else if (hr == D3DERR_PRESENT_STATISTICS_DISJOINT)
    {
        // D3DERR_PRESENT_STATISTICS_DISJOINT means measurements should be started from the scratch (could be caused by mode change or DWM on/off transition)
        ResetAnimation();
    }

    // If we got too many glitches in a row, reduce framerate conversion factor (that is, render less frames)
    if (g_iGlitchesInaRow == FRAMECONVERSION_GLITCH_LIMIT)
    {
        if (g_iDoNotFlipNum < FRAMECONVERSION_LIMIT)
        {
            g_iDoNotFlipNum++;
        }
        g_iGlitchesInaRow = 0;
        g_iShowingDoNotFlipBump = MESSAGE_SHOW;
    }
}

Cenário de exemplo

  • A ilustração a seguir mostra um aplicativo com contagem de backbuffer igual a 4. O comprimento real da fila Presente é, portanto, 5.

    ilustração de um aplicativo renderizado quadros e fila presente

    O quadro A tem como destino entrar na tela na contagem de intervalos de sincronização de 1, mas foi detectado que ele foi mostrado na contagem de intervalos de sincronização de 4. Portanto, ocorreu uma falha. Os três quadros subsequentes são apresentados com D3DPRESENT_INTERVAL_FORCEIMMEDIATE. A falha deve receber um total de 8 chamadas presentes antes de ser recuperada – o próximo quadro será mostrado de acordo com sua contagem de intervalos de sincronização de destino.

Resumo das recomendações de programação para sincronização de quadros

  • Crie uma lista de backup de todas as IDs LastPresentCount (obtidas por meio de GetLastPresentCount) e presentRefreshCount estimado associado de todos os Presentes enviados.

    Observação

    Quando o aplicativo chama PresentEx com D3DPRESENT_DONOTFLIP, a chamada GetPresentStatistics é bem-sucedida, mas não retorna uma estrutura D3DPRESENTSTATS atualizada quando o aplicativo está no modo de janela.

  • Chame GetPresentStatistics para obter o PresentRefreshCount real associado a cada ID presente de quadros mostrados, para garantir que o aplicativo lide com falhas retornada da chamada.

  • Se PresentRefreshCount real for posterior à estimativa de PresentRefreshCount, uma falha será detectada. Compense enviando quadros de atraso presentes com D3DPRESENT_FORCEIMMEDIATE.

  • Quando um quadro for apresentado no final da fila Presente, todos os quadros enfileirados subsequentes serão apresentados com atraso. D3DPRESENT_FORCEIMMEDIATE corrigirá apenas o próximo quadro a ser apresentado após todos os quadros enfileirados. Portanto, a contagem de filas ou backbuffers presentes não deve ser muito longa, portanto, há menos falhas de quadro para acompanhar. A contagem ideal de backbuffer é de 2 a 4.

  • Se o PresentRefreshCount estimado for posterior ao PresentRefreshCount real, a limitação de DWM poderá ter ocorrido. As seguintes soluções são possíveis:

    • reduzindo o tamanho da fila Presente
    • reduzindo os requisitos de memória de GPU com qualquer outro meio além de reduzir o comprimento da Fila Presente (ou seja, diminuindo a qualidade, removendo efeitos e assim por diante)
    • especificando DwmEnableMMCSS para evitar a limitação de DWM em geral
  • Verifique a funcionalidade de exibição do aplicativo e o desempenho das estatísticas de quadro nos seguintes cenários:

    • com o DWM ativado e desativado
    • modos exclusivos e em janelas de tela inteira
    • hardware de funcionalidade inferior
  • Quando os aplicativos não podem se recuperar de um grande número de quadros com falhas com D3DPRESENT_FORCEIMMEDIATE Presente, eles podem potencialmente executar as seguintes operações:

    • reduza o uso de CPU e GPU renderizando com menos carga de trabalho.
    • no caso de decodificação de vídeo, decodificar mais rapidamente reduzindo a qualidade e, portanto, o uso de CPU e GPU.

Conclusão sobre melhorias do Direct3D 9Ex

No Windows 7, os aplicativos que exibem a taxa de quadros de vídeo ou medidor durante a apresentação podem optar pelo Flip Model. As melhorias de estatísticas atuais associadas ao Flip Model Direct3D 9Ex podem beneficiar aplicativos que sincronizam a apresentação por taxa de quadros, com comentários em tempo real para detecção e recuperação de falhas. Os desenvolvedores que adotam o Direct3D 9Ex Flip Model devem levar em conta o direcionamento de um HWND separado do conteúdo GDI e da sincronização de taxa de quadros. Consulte os detalhes neste tópico e a documentação do MSDN. Para obter documentação adicional, consulte Central de Desenvolvedores do DirectX no MSDN.

Plano de ação

Incentivamos você a usar o Direct3D 9Ex Flip Model e suas estatísticas atuais no Windows 7 ao criar aplicativos que tentam sincronizar a taxa de quadros de apresentação ou se recuperar de falhas de exibição.

Central de Desenvolvedores do DirectX no MSDN