Aprimoramentos do Direct3D 9Ex

Este tópico descreve o suporte adicionado do Windows 7 para a Apresentação de Modo Flip e suas estatísticas de apresentação associadas no Direct3D 9Ex e no Gerenciador de Janelas da Área de Trabalho. Os aplicativos de destino incluem aplicativos de apresentação baseados em vídeo ou taxa de quadros. Os aplicativos que usam a Apresentação de Modo Flip do Direct3D 9Ex reduzem a carga de recursos do sistema quando o DWM está habilitado. Os aprimoramentos de estatísticas de apresentação associados à Apresentação de Modo Flip permitem que os aplicativos Direct3D 9Ex controlem melhor a taxa de apresentação, fornecendo mecanismos de feedback e correção em tempo real. Explicações detalhadas e indicações de recursos de amostra estão incluídas.

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

O que melhorou no Direct3D 9Ex para Windows 7

A Apresentação de Modo Flip do Direct3D 9Ex é um modo aprimorado de apresentação de imagens no Direct3D 9Ex que entrega imagens renderizadas com eficiência 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 de janela apresentam seu conteúdo na área de trabalho usando um método chamado Apresentação de Modo Blt para DWM (ou Modelo Blt). Com o Modelo Blt, o DWM mantém uma cópia da superfície renderizada do Direct3D 9Ex para a composição da área de trabalho. Quando o aplicativo é atualizado, o novo conteúdo é copiado para a superfície 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 DWM.

Disponível no Windows 7, a Apresentação de Modo Flip para DWM (ou Modelo Flip) é um novo método de apresentação que essencialmente permite a passagem de identificadores de superfícies de aplicativo entre aplicativos de modo de janela e DWM. Além de economizar recursos, o Modelo Flip oferece suporte a estatísticas de apresentação aprimoradas.

As estatísticas de apresentação 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 na reprodução de vídeo. As informações de tempo de quadro nas estatísticas de apresentação 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 do quadro para composição da área de trabalho, os aplicativos podem usar estatísticas de apresentação fornecidas pelo DWM. Este método de obter estatísticas de apresentação 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 do D3D9Ex para obter estatísticas de apresentação. Quando o DWM está habilitado, o modo de janela e o modo exclusivo de tela cheia dos aplicativos Direct3D 9Ex podem esperar as mesmas informações estatísticas presentes ao usar o Modelo Flip. As estatísticas de apresentação do Modelo Flip do Direct3D 9Ex permitem que os aplicativos consultem estatísticas de apresentação em tempo real, em vez de depois que o quadro foi exibido na tela. As mesmas informações de estatísticas de apresentação estão disponíveis para aplicativos habilitados para Modelo Flip no modo de janela como aplicativos de tela cheia. Um sinalizador adicionado nas APIs do D3D9Ex permite que os aplicativos de Modelo Flip descartem efetivamente quadros atrasados no momento da apresentação.

O Modelo Flip do Direct3D 9Ex deve ser usado por novos aplicativos de apresentação baseados em vídeo ou taxa de quadros direcionados ao Windows 7. Devido à sincronização entre o DWM e o runtime do Direct3D 9Ex, os aplicativos que usam o Modelo Flip devem especificar entre 2 e 4 buffers de fundo para garantir uma apresentação sem problemas. Os aplicativos que usam informações de estatísticas de apresentação se beneficiarão ao usar as melhorias de estatísticas de apresentação habilitadas pelo Modelo Flip.

Apresentação de Modo Flip do Direct3D 9EX

As melhorias de desempenho da Apresentação de Modo Flip do Direct3D 9Ex 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 dos usos de largura de banda de memória e leituras e gravações do sistema de aplicativos em janela que escolhem o Modelo Flip em comparação ao uso padrão do Modelo Blt.

Apresentação de Modo BLT no DWM Apresentação de Modo Flip D3D9Ex no 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 DWM (Leitura, Gravação)
2. O runtime do Direct3D passa a superfície do aplicativo para o DWM
3. Depois que a cópia de superfície compartilhada for concluída, o DWM renderizará 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 Flip

A Apresentação de Modo Flip 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 da memória.

Os aplicativos podem aproveitar as melhorias de estatísticas de apresentação do Modo Flip do Direct3D 9Ex quando o DWM está ativado, independentemente de o aplicativo estar em modo janela ou em modo de tela cheia exclusivo.

Modelo de programação e APIs

Os novos aplicativos de medição de taxa de quadros ou de vídeo que usam APIs do Direct3D 9Ex no Windows 7 podem aproveitar a economia de memória e energia e a apresentação aprimorada oferecida pela Apresentação de Modo Flip quando executados no Windows 7. (Ao executar em versões anteriores do Windows, o runtime do Direct3D padroniza o aplicativo para Apresentação de Modo Blt.)

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

Você pode modificar os aplicativos existentes para aproveitar a Apresentação de Modo Flip, com os mesmos benefícios e ressalvas dos aplicativos desenvolvidos recentemente.

Como optar pelo 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 enumeration value. Para optar pelo Modelo Flip, os aplicativos especificam a estrutura D3DPRESENT_PARAMETERS e depois passam um ponteiro para essa estrutura quando chamam a API IDirect3D9Ex::CreateDeviceEx. Esta seção descreve como os aplicativos direcionados ao Windows 7 usam IDirect3D9Ex::CreateDeviceEx para optar pelo Modelo Flip. Para obter mais informações sobre a API IDirect3D9Ex::CreateDeviceEx, consulte IDirect3D9Ex::CreateDeviceEx no MSDN.

Por 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 os aplicativos Direct3D 9Ex para Windows 7 para optar pelo Modelo Flip, você deve considerar os seguintes itens sobre os membros especificados de D3DPRESENT_PARAMETERS:

BackBufferCount

(Apenas Windows 7)

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

Quando o aplicativo também usa estatísticas de apresentação associadas ao D3DSWAPEFFECT_FLIPEX, recomendamos que você defina a contagem do buffer de retorno de 2 a 4.

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

SwapEffect

(Apenas Windows 7)

O novo tipo de efeito de cadeia de troca D3DSWAPEFFECT_FLIPEX designa quando um aplicativo está adotando a Apresentação de Modo Flip para DWM. Ele permite que o aplicativo use memória e energia de forma mais eficiente, além de possibilitar que o aplicativo aproveite as estatísticas de apresentação em tela cheia no modo de janela. O comportamento do aplicativo em tela cheia não é afetado. Se Windowed for definido como TRUE e SwapEffect for definido como D3DSWAPEFFECT_FLIPEX, o tempo de execução criará um buffer posterior adicional e rotacionará qualquer identificador pertencente ao buffer que se tornará o buffer frontal no momento da apresentação.

Sinalizadores

(Apenas Windows 7)

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

Diretrizes de design para aplicativos de Modelo Flip do Direct3D 9Ex

Use as diretrizes nas seções a seguir para criar seus aplicativos de Modelo Flip do Direct3D 9Ex.

Use a Apresentação de Modo Flip em um HWND separado da Apresentação de Modo Blt

Os aplicativos devem usar a Apresentação de Modo Flip do Direct3D 9Ex em um HWND que também não é direcionado por outras APIs, incluindo a Apresentação de Modo Blt do Direct3D 9Ex, outras versões do Direct3D ou GDI. A Apresentação de Modo Flip pode ser usado para apresentar janelas filhas; ou seja, os aplicativos podem usar o Modelo Flip quando ele não estiver misturado ao Modelo Blt no mesmo HWND, conforme mostrado nas ilustrações a seguir.

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

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

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

Ilustração do texto gdi que pode não ser exibido se o modelo flip for usado e se o conteúdo direct3d e gdi estiver 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 Modelo Flip deve ser habilitado para superfícies de buffers de cadeia de troca em que o Modelo Flip do Direct3D 9Ex sozinho é renderizado para todo o HWND.

Não use o Modelo Flip com ScrollWindow ou ScrollWindowEx do GDI

Alguns aplicativos Direct3D 9Ex usam as funções ScrollWindow ou ScrollWindowEx do GDI para atualizar o conteúdo da janela quando um evento de rolagem do usuário é acionado. ScrollWindow e ScrollWindowEx executam blts do 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 uma das funções não necessariamente exibirão 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 do GDI em seus aplicativos e, em vez disso, redesenhe seu conteúdo na tela em resposta à rolagem.

Use uma cadeia de troca de D3DSWAPEFFECT_FLIPEX por HWND

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

Sincronização de quadros de aplicativos de Modelo Flip do Direct3D 9Ex

As estatísticas atuais 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 na reprodução de vídeo. Para habilitar a disponibilidade de 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.

Por 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 Modelo Flip do Direct3D 9Ex adiciona o sinalizador de apresentação D3DPRESENT_FORCEIMMEDIATE que impõe o comportamento do 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::PresentEx, 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, você deve considerar as seguintes informações sobre os sinalizadores de apresentação D3DPRESENT especificados:

D3DPRESENT_DONOTFLIP

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

(Apenas Windows 7)

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

D3DPRESENT_FORCEIMMEDIATE

(Apenas 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 Present DWM, essencialmente ignorando quadros intermediários.

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

Para obter informações 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::PresentEx. O dispositivo deve ser criado usando uma chamada IDirect3D9Ex::CreateDeviceEx com o sinalizador D3DCREATE_ENABLE_PRESENTSTATS . Caso contrário, os dados retornados por GetPresentStatistics serão indefinidos. Uma cadeia de troca Direct3D 9Ex habilitada para Modelo Flip fornece informações estatísticas presentes nos modos de janela e tela inteira.

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

Para estatísticas de apresentação do FlipEx, GetPresentStatistics retorna D3DERR_PRESENT_STATISTICS_DISJOINT nas seguintes situações:

  • Primeira chamada para GetPresentStatistics, que indica o início de uma sequência
  • Transição DWM de ligado para desligado
  • Mudança de modo: modo de janela de ou para tela inteira ou tela inteira para transições de tela inteira

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

HRESULT GetPresentStatistics(
  D3DPRESENTSTATS * pPresentationStatistics
);

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

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

HRESULT GetLastPresentCount(
  UINT * pLastPresentCount
);

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

  • O valor PresentCount que GetLastPresentCount retorna não é atualizado quando uma chamada PresentEx com D3DPRESENT_DONOTWAIT especificado 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 a apresentação foi enviada, 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 onde os aplicativos não precisam ter reconhecimento de DWM, o Windows 7 retornará informações de estatísticas de quadros para aplicativos em janela quando o DWM estiver desativado. As estatísticas de quadro retornadas quando o DWM está desativado são apenas estimativas.

Passo a passo de um Modelo Flip do Direct3D 9Ex e exemplo de estatísticas de apresentação

Para optar pela apresentação do FlipEx para o exemplo do Direct3D 9Ex

  1. Certifique-se de que o aplicativo de exemplo esteja sendo executado no 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 optar por estatísticas de apresentação associadas ao FlipEx para o exemplo do 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, detectar e se recuperar de falhas

  1. Chamadas Present de fila: a contagem de buffer de fundo recomendada é de 2 a 4.

  2. O exemplo do Direct3D 9Ex adiciona um buffer de fundo implícito, o comprimento real da fila Present é a contagem do buffer de fundo + 1.

  3. Crie uma estrutura de fila Present auxiliar para armazenar todos os IDs de Present (PresentCount) enviados com sucesso e o PresentRefreshCount associado, calculado/esperado.

  4. Para detectar a ocorrência de falha:

    • Chame GetPresentStatistics.
    • Obtenha o ID de apresentação (PresentCount) e a contagem de vsync onde o quadro é mostrado (PresentRefreshCount) do quadro cujas estatísticas de apresentação são obtidas.
    • Recupere o PresentRefreshCount esperado (TargetRefresh no código de exemplo) associado ao ID de apresentação.
    • Se o PresentRefreshCount real for posterior ao esperado, ocorreu uma falha.
  5. Para se recuperar de uma falha:

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

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

  1. A recuperação de falhas leva N (variável g_iQueueDelay no código de exemplo) número de chamadas Present, onde N (g_iQueueDelay) é igual a g_iImmediates mais o comprimento da fila Present, ou seja:

    • Ignorar quadros com o intervalo Present D3DPRESENT_FORCEIMMEDIATE, mais
    • Presents em fila 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 60 Hz), pule a animação intermitente e redefina a fila auxiliar Present.

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 buffer de fundo de 4. Portanto, o comprimento real da fila Present é 5.

    ilustração de quadros renderizados de um aplicativo e fila present

    O quadro A é direcionado para ir à tela na contagem de intervalo de sincronização de 1, mas foi detectado que ele foi mostrado na contagem de intervalo de sincronização de 4. Portanto, ocorreu uma falha. Os 3 quadros subsequentes são apresentados com D3DPRESENT_INTERVAL_FORCEIMMEDIATE. A falha deve levar um total de 8 chamadas Present antes de ser recuperada. O próximo quadro será mostrado de acordo com sua contagem de intervalos de sincronização alvo.

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

  • Crie uma lista de backup de todos os IDs LastPresentCount (obtidos por meio de GetLastPresentCount) e PresentRefreshCount estimado associado de todos os Presents 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.

  • Call GetPresentStatistics para obter o PresentRefreshCount real associado a cada ID Present dos quadros mostrados, para garantir que o aplicativo trate os retornos de falha da chamada.

  • Se o PresentRefreshCount real for posterior ao PresentRefreshCount estimado, uma falha será detectada. Compense enviando quadros atrasados' Present com D3DPRESENT_FORCEIMMEDIATE.

  • Quando um quadro é apresentado no final da fila Present, 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 fila Present ou a contagem do buffer de fundo não deve ser muito longa, para que haja menos falhas de quadro para corrigir. A contagem ideal de buffer de fundo é de 2 a 4.

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

    • reduzindo o comprimento da fila Present
    • reduzindo os requisitos de memória da GPU com quaisquer outros meios além de reduzir o comprimento da fila Present (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 DWM ligado e desligado
    • modos exclusivos de tela inteira e de janela
    • hardware de menor capacidade
  • Quando os aplicativos não podem se recuperar de um grande número de quadros com falha com D3DPRESENT_FORCEIMMEDIATE Present, eles podem executar as seguintes operações:

    • reduzir o uso de CPU e GPU renderizando com menos carga de trabalho.
    • no caso de decodificação de vídeo, decodifique mais rápido reduzindo a qualidade e, portanto, o uso de CPU e GPU.

Conclusão sobre melhorias no Direct3D 9Ex

No Windows 7, os aplicativos que exibem vídeo ou medem a taxa de quadros durante a apresentação podem optar pelo Modelo Flip. As melhorias estatísticas atuais associadas ao Modelo Flip 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 Modelo Flip do Direct3D 9Ex devem levar em conta o direcionamento de um HWND separado do conteúdo GDI e da sincronização da taxa de quadros. Consulte os detalhes neste tópico. Para obter documentação adicional, consulte Centro de desenvolvedores do DirectX no MSDN.

Plano de ação

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

Centro de desenvolvedores do DirectX no MSDN