Windows Dev Center

Dando suporte à orientação de tela (DirectX e C++)

Muitos dispositivos do Windows 8 dão suporte a várias orientações de tela. Os aplicativos da Windows Store que usam DirectX com C++ podem dar suporte a este recurso também quando você manipula o evento CoreWindow::SizeChanged. Nós discutiremos neste documento as práticas recomendadas para controlar a rotação da tela no aplicativo da Windows Store usando DirectX para que o hardware gráfico do dispositivo do Windows 8 seja usado de forma eficiente.

Observação  Essas práticas são completamente demonstradas no modelo de projeto do Aplicativo Direct2D no Microsoft Visual Studio e no Exemplo de rotação da cadeia de permuta DXGI.

Antes de começar, lembre-se de que o hardware gráfico sempre emite dados em pixel da mesma maneira, independentemente da orientação do dispositivo. Os dispositivos do Windows 8 podem determinar sua orientação do vídeo atual (com algum tipo de sensor ou com uma alternância de software) e permite que os usuários mudem as configurações de vídeo. Devido a isso, o próprio Windows 8 controla a rotação das imagens para assegurar que elas fiquem "verticais" de acordo com a orientação do dispositivo. Por padrão, seu aplicativo recebe a notificação de que algo mudou na orientação, por exemplo, o tamanho de uma janela. Quando isso acontece, o Windows 8 gira imediatamente a imagem para a exibição final. Para três das quatro orientações de tela específica (discutido posteriormente), o Windows 8 usa recursos gráficos adicionais e computação para exibir a imagem final.

Para o aplicativo da Windows Store usando DirectX, o objeto DisplayProperties inclui os dados básicos de orientação de vídeo que seu aplicativo pode consultar. A orientação padrão é paisagem, onde a largura em pixels da exibição é maior que a altura; a orientação alternativa é retrato, onde a exibição é girada a 90 graus nas duas direções e a largura torna-se menor que a altura.

O Windows 8 define quatro modos de orientação de exibição específicas:

  • Paisagem —a orientação de exibição padrão para o Windows 8 e é considerada o ângulo base ou de identificação da rotação (0 graus).
  • Retrato —o vídeo tem que ser girado no sentido horário 270 graus (ou sentido anti-horário 90 graus).
  • Paisagem, virado —o vídeo foi girado 180 graus (virado de ponta cabeça).
  • Retrato, virado—o vídeo foi girado no sentido horário a 90 graus (ou no sentido anti-horário a 270 graus).

Quando o vídeo gira de uma orientação para outra, o Windows 8 executa internamente uma operação de rotação para alinhar a imagem desenhada com a nova orientação e o usuário vê uma imagem vertical na tela.

Além disso, o Windows 8 exibe animações de transição automática para criar uma experiência de usuário uniforme ao mudar de uma orientação para a outra. À medida que a orientação do vídeo muda, o usuário vê essas mudanças como uma animação de rotação e zoom fixo da imagem da tela exibida. O tempo é alocado pelo Windows 8 para o aplicativo para o layout na nova orientação. Mas para os aplicativos da Windows Store usando DirectX, o tempo necessário para o novo layout geralmente é inferior ao tempo permitido, porque outras estruturas podem precisar mais do tempo alocado. Como seu aplicativo provavelmente nunca usará esta alocação de tempo integral, você poderá notificar diretamente o Windows 8 que seu aplicativo concluiu todo o trabalho necessário para controlar a nova orientação de vídeo e que seu aplicativo chamou o IDXGISwapChain::Present para exibir a nova imagem. Especifique a conclusão de layout inicial do aplicativo usando o CoreWindowResizeManager::NotifyLayoutCompleted.

Geral, este é o processo geral para tratar das mudanças de orientação de tela:

  1. Use uma combinação dos valores de limites de janela e os dados de orientação de vídeo para manter a cadeia de permuta alinhada com a orientação de vídeo nativa do dispositivo.
  2. Notifique o Windows 8 sobre a orientação da cadeia de permuta usando o IDXGISwapChain1::SetRotation.
  3. Mude o código de renderização para gerar imagens alinhadas com a orientação do usuário do dispositivo.
  4. Notifique o Windows 8 que seu aplicativo está pronto para continuar na nova orientação usando CoreWindowResizeManager::NotifyLayoutCompleted.

Para ver o código completo para este processo conforme aplicado para a renderização do Direct2D e Direct3D, baixe o exemplo de rotação de cadeia de permuta DXGI .

Há dois métodos que você pode usar no aplicativo da Windows Store usando DirectX para controlar a mudança na orientação:

Vamos dar uma olhada nesses métodos.

Método 1: Redimensionando a cadeia de permuta

A abordagem mais fácil para controlar a rotação é permitir que o Windows 8 faça isso para você. Naturalmente, como seu vídeo não possui uma taxa de proporção 1:1, os resultados terão a aparência esticada ou esguichada. A solução para isso? Redimensiona a cadeia de permuta com os valores de altura e largura para os limites da janela permutados e apresente o conteúdo.

Para executar um redimensionamento de vídeo básico em seu aplicativo da Windows Store usando DirectX, siga estas etapas:

  1. Manipule o evento CoreWindow::SizeChanged.
  2. Redimensione a cadeia de permuta para as novas dimensões da janela.
  3. Recrie os recursos que dependem do tamanho da janela, como seus alvos de renderização e outros buffers de dados em pixel.

Agora vamos examinar essas etapas um pouco mais detalhadamente.

Sua primeira etapa é registrar um manipulador para o evento CoreWindow::SizeChanged. Esse evento é acionado no CoreWindowde seu aplicativo, sempre que o tamanho da tela muda, como quando o vídeo é girado. Se você indicou que seu aplicativo dá suporte à rotação da tela no arquivo package.appxmanifestdo aplicativo, você deverá manipular o evento SizeChanged. (Caso contrário, o vídeo do aplicativo será redimensionado automaticamente quando a orientação muda e o conteúdo da cadeia de permuta é esticado ou esguichado para se ajustar à nova área.)

Para manipular o evento SizeChanged, conecte o manipulador para CoreWindow::SizeChangedno método SetWindow exigido, que é um dos métodos da interface IFrameworkView que seu provedor de visualização deve implementar.

Neste exemplo de código, o manipulador de eventos para CoreWindow::SizeChangedé um método chamado OnWindowSizeChangede ele também é definido no objeto do provedor de visualização. Quando CoreWindow::SizeChanged é acionado, ele chama um método chamado UpdateForWindowSizeChange definido no objeto renderizado.


void MyDirectXApp::SetWindow(
    _In_ CoreWindow^ window
    )
{
  // ... Other UI event handlers assigned here ...

  window->SizeChanged += ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(
                                 this,
                                 &MyDirectXApp::OnWindowSizeChanged
                                 );
  // ...

}


void MyDirectXApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
{
  m_renderer->UpdateForWindowSizeChange(); // m_renderer is an object that inherits from DirectXBase
}

Agora, redimensione a cadeia de permuta de sua chamada de retorno. (No exemplo de código, isso é executado no DirectXBase::UpdateForWindowSizeChange.)



// This routine is called in the event handler for the view SizeChanged event.
void DirectXBase::UpdateForWindowSizeChange()
{
  if (m_window->Bounds.Width  != m_windowBounds.Width ||
      m_window->Bounds.Height != m_windowBounds.Height ||
      m_orientation != DisplayProperties::CurrentOrientation)
  {
    m_d2dContext->SetTarget(nullptr);
    m_d2dTargetBitmap = nullptr;
    m_d3dRenderTargetView = nullptr;
    m_d3dDepthStencilView = nullptr;
    m_windowSizeChangeInProgress = true;
    CreateWindowSizeDependentResources();
  }
}




// Allocate all memory resources that change on a window SizeChanged event.
void DirectXBase::CreateWindowSizeDependentResources()
{
    // Store the window bounds so the next time we get a SizeChanged event we can
    // avoid rebuilding everything if the size is identical.
    m_windowBounds = m_window->Bounds;

    if (m_swapChain != nullptr)
    {
        // If the swap chain already exists, resize it.
        HRESULT hr = m_swapChain->ResizeBuffers(
            2,
            static_cast<UINT>(m_renderTargetSize.Width),
            static_cast<UINT>(m_renderTargetSize.Height),
            DXGI_FORMAT_B8G8R8A8_UNORM,
            0
            );

        if (hr == DXGI_ERROR_DEVICE_REMOVED)
        {
            // If the device was removed for any reason, a new device and swapchain will need to be created.
            HandleDeviceLost();

            // Everything is set up now. Don't continue to execute this method.
            return;
        }
        else
        {
            DX::ThrowIfFailed(hr);
        }
    }
    else
    {
        // ...
        // Otherwise, create a new one using the same adapter as the existing Direct3D device.
        // ...       
    }

    // Create a Direct3D render target view of the swap chain back buffer.
    ComPtr<ID3D11Texture2D> backBuffer;
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
        );

    DX::ThrowIfFailed(
        m_d3dDevice->CreateRenderTargetView(
            backBuffer.Get(),
            nullptr,
            &m_d3dRenderTargetView
            )
        );

    // ...
    // Set up other window size dependent resources like Direct2D contexts, depth stencils, 
    // and 3D viewports here...
    // ...

}


Neste método, você também reconfigura ou redimensiona quaisquer outros recursos DirectX que dependem do tamanho da janela, como as profundidades de estênceis ou outros buffers de dados em pixel, e os visores do Direct3D se você tiver uma cena 3-D para exibir.

Agora, com a cadeia de permuta efetivamente redimensionada e apresentada, o conteúdo da tela do aplicativo é exibido corretamente exibido na nova orientação após o Windows 8 concluir a animação de rotação do sistema. Entretanto, o Windows 8 ainda executa sua própria cópia de dados em pixel de tela inteira para a animação de rotação, tirando um instantâneo do buffer da tela e girando a própria imagem antes de exibir a cadeia de permuta apresentada do aplicativo. Você pode eliminar esta redundância e salvar a GPU algum trabalho (especialmente se a resolução nativa for alta) informando o Windows 8 que você já girou o pipeline de renderização. Examinaremos essa otimização potencial na próxima seção. Também examinaremos os componentes Direct2D e Direct3D de manipulação da cadeia de permuta, que exigem diferentes técnicas ao compor a imagem girada.

Para obter o código completo que mostra como esse processo funciona, examine o modelo Aplicativo Direct2D no Visual Studio ou baixe o exemplo de rotação de cadeia de permuta DXGI.

Método 2: Girar previamente o conteúdo da cadeia de permuta

Direct3D 11 e DXGI apresentam uma nova API, IDXGISwapChain1::SetRotation, que você pode usar no aplicativo da Windows Store usando DirectX para notificar o Windows 8 da orientação dos dados da imagem da janela na cadeia de permuta. Se a orientação do dispositivo corresponder à orientação da cadeia de permuta, o Windows 8 poderá usar diretamente as imagens da janela sem executar nenhuma rotação, e você evitará uma operação de cópia de tela inteira adicional no nível do hardware, retornando o desempenho e uma economia de vida útil da bateria para o aplicativo. Entretanto, seu aplicativo deve renderizar as imagens na cadeia de permuta para corresponder a orientação, dessa forma, você pode selecionar essa eficiência adicional.

Conforme discutimos anteriormente, o aplicativo da Windows Store usando DirectX pode, no mínimo, simplesmente manipular um evento de rotação redimensionando os buffers da cadeia de permuta à nova altura e largura da tela e depois apresentando-a.

O processo é concluído, mas apresenta alguns fatores negativos:

  • Ele executa uma operação de cópia de tela inteira redundante.
  • Ele realmente não diferencia entre o tipo do evento de mudança de tamanho, que poderia ter sido acionado por uma janela de aplicativo movimento para um estado ajustado ou preenchido por uma mudança na resolução de vídeo.

Vamos atualizar o processo para tratar essas deficiências seguindo essas etapas atualizadas:

  1. Manipule o evento CoreWindow::SizeChanged, como antes.
  2. Use DisplayProperties::CurrentOrientation para confirmar que o evento de mudança do tamanho da janela é especificamente para uma rotação.
  3. Não redimensione a cadeia de permuta! Em vez disso, use os dados atuais de orientação para atualizar as transformações do Direct2D e Direct3D mantidas pelo aplicativo e gire o pipeline de renderização.
  4. Chame IDXGISwapChain1::SetRotation para definir a orientação da cadeia de permuta.
  5. Recrie quaisquer recursos que dependem de tamanho de janela, como antes.

Veja o trabalho de forma longa para redimensionar a cadeia de permuta para a nova orientação de tela e prepará-la para girar o conteúdo do pipeline gráfico quando a renderização é executada. Neste exemplo, o DirectXBase::CreateWindowSizeDependentResources é um método que você implementaria em seu objeto renderizador.

Dedique um tempo para analisar o código, depois vamos discutir sobre ele.



// Allocate all memory resources that change on a window SizeChanged event.
void DirectXBase::CreateWindowSizeDependentResources()
{
    // Store the window bounds so the next time we get a SizeChanged event we can
    // avoid rebuilding everything if the size is identical.
    m_windowBounds = m_window->Bounds;

    // Calculate the necessary swap chain and render target size in pixels.
    auto windowWidth = ConvertDipsToPixels(m_windowBounds.Width);
    auto windowHeight = ConvertDipsToPixels(m_windowBounds.Height);

    // Swap width and height based on orientation.
    m_orientation = DisplayProperties::CurrentOrientation;
    bool swapDimensions = (
        m_orientation == DisplayOrientations::Portrait ||
        m_orientation == DisplayOrientations::PortraitFlipped
        );
    m_renderTargetSize.Width = swapDimensions ? windowHeight : windowWidth;
    m_renderTargetSize.Height = swapDimensions ? windowWidth : windowHeight;

    if (m_swapChain != nullptr)
    {
        // If the swap chain already exists, resize it.
        HRESULT hr = m_swapChain->ResizeBuffers(
            2,
            static_cast<UINT>(m_renderTargetSize.Width),
            static_cast<UINT>(m_renderTargetSize.Height),
            DXGI_FORMAT_B8G8R8A8_UNORM,
            0
            );

        if (hr == DXGI_ERROR_DEVICE_REMOVED)
        {
            // If the device was removed for any reason, a new device and swapchain will need to be created.
            HandleDeviceLost();

            // Everything is set up now. Don't continue to execute this method.
            return;
        }
        else
        {
            DX::ThrowIfFailed(hr);
        }
    }
    else
    {
        // Otherwise, create a new one using the same adapter as the existing Direct3D device.
        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
        swapChainDesc.Width = static_cast<UINT>(m_renderTargetSize.Width); // Match the size of the window.
        swapChainDesc.Height = static_cast<UINT>(m_renderTargetSize.Height);
        swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;           // This is the most common swap chain format.
        swapChainDesc.Stereo = false;
        swapChainDesc.SampleDesc.Count = 1;                          // Don't use multi-sampling.
        swapChainDesc.SampleDesc.Quality = 0;
        swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapChainDesc.BufferCount = 2;                               // Use double-buffering to minimize latency.
        swapChainDesc.Scaling = DXGI_SCALING_NONE;
        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
        swapChainDesc.Flags = 0;

        ComPtr<IDXGIDevice1> dxgiDevice;
        DX::ThrowIfFailed(
            m_d3dDevice.As(&dxgiDevice)
            );

        ComPtr<IDXGIAdapter> dxgiAdapter;
        DX::ThrowIfFailed(
            dxgiDevice->GetAdapter(&dxgiAdapter)
            );

        ComPtr<IDXGIFactory2> dxgiFactory;
        DX::ThrowIfFailed(
            dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
            );

        CoreWindow^ window = m_window.Get();
        DX::ThrowIfFailed(
            dxgiFactory->CreateSwapChainForCoreWindow(
                m_d3dDevice.Get(),
                reinterpret_cast<IUnknown*>(window),
                &swapChainDesc,
                nullptr,
                &m_swapChain
                )
            );

        // Ensure that DXGI doesn't queue more than one frame at a time. This both reduces latency and
        // ensures that the application will only render after each VSync, minimizing power consumption.
        DX::ThrowIfFailed(
            dxgiDevice->SetMaximumFrameLatency(1)
            );
    }

    // Set the proper orientation for the swap chain, and generate 2-D and
    // 3-D matrix transformations for rendering to the rotated swap chain.
    // Note the rotation angle for the 2-D and 3-D transforms are different.
    // This is due to the difference in coordinate spaces.  Additionally,
    // the 3-D matrix is specified explicitly to avoid rounding errors.
    DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
    switch (m_orientation)
    {
        case DisplayOrientations::Landscape:
            rotation = DXGI_MODE_ROTATION_IDENTITY;
            m_rotationTransform2D = Matrix3x2F::Identity();
            m_rotationTransform3D = identity();
            break;
        case DisplayOrientations::Portrait:
            rotation = DXGI_MODE_ROTATION_ROTATE270;
            m_rotationTransform2D =
                Matrix3x2F::Rotation(270.0f) *
                Matrix3x2F::Translation(0.0f, m_windowBounds.Width);
            m_rotationTransform3D = float4x4( // 90-degree Z-rotation
                0.0f, -1.0f, 0.0f, 0.0f,
                1.0f, 0.0f, 0.0f, 0.0f,
                0.0f, 0.0f, 1.0f, 0.0f,
                0.0f, 0.0f, 0.0f, 1.0f
                );
            break;
        case DisplayOrientations::LandscapeFlipped:
            rotation = DXGI_MODE_ROTATION_ROTATE180;
            m_rotationTransform2D =
                Matrix3x2F::Rotation(180.0f) *
                Matrix3x2F::Translation(m_windowBounds.Width, m_windowBounds.Height);
            m_rotationTransform3D = float4x4( // 180-degree Z-rotation
                -1.0f, 0.0f, 0.0f, 0.0f,
                0.0f, -1.0f, 0.0f, 0.0f,
                0.0f, 0.0f, 1.0f, 0.0f,
                0.0f, 0.0f, 0.0f, 1.0f
                );
            break;
        case DisplayOrientations::PortraitFlipped:
            rotation = DXGI_MODE_ROTATION_ROTATE90;
            m_rotationTransform2D =
                Matrix3x2F::Rotation(90.0f) *
                Matrix3x2F::Translation(m_windowBounds.Height, 0.0f);
            m_rotationTransform3D = float4x4( // 270-degree Z-rotation
                0.0f, 1.0f, 0.0f, 0.0f,
                -1.0f, 0.0f, 0.0f, 0.0f,
                0.0f, 0.0f, 1.0f, 0.0f,
                0.0f, 0.0f, 0.0f, 1.0f
                );
            break;
        default:
            throw ref new Platform::FailureException();
            break;
    }

    DX::ThrowIfFailed(
        m_swapChain->SetRotation(rotation)
        );

    // Create a Direct3D render target view of the swap chain back buffer.
    ComPtr<ID3D11Texture2D> backBuffer;
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
        );

    DX::ThrowIfFailed(
        m_d3dDevice->CreateRenderTargetView(
            backBuffer.Get(),
            nullptr,
            &m_d3dRenderTargetView
            )
        );

    // Create a depth stencil view for use with 3-D rendering if needed.
    CD3D11_TEXTURE2D_DESC depthStencilDesc(
        DXGI_FORMAT_D24_UNORM_S8_UINT,
        static_cast<UINT>(m_renderTargetSize.Width),
        static_cast<UINT>(m_renderTargetSize.Height),
        1,
        1,
        D3D11_BIND_DEPTH_STENCIL
        );

    ComPtr<ID3D11Texture2D> depthStencil;
    DX::ThrowIfFailed(
        m_d3dDevice->CreateTexture2D(
            &depthStencilDesc,
            nullptr,
            &depthStencil
            )
        );

    auto viewDesc = CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D);
    DX::ThrowIfFailed(
        m_d3dDevice->CreateDepthStencilView(
            depthStencil.Get(),
            &viewDesc,
            &m_d3dDepthStencilView
            )
        );

    // Set the 3-D rendering viewport to target the entire window.
    CD3D11_VIEWPORT viewport(
        0.0f,
        0.0f,
        m_renderTargetSize.Width,
        m_renderTargetSize.Height
        );

    m_d3dContext->RSSetViewports(1, &viewport);

    // Create a Direct2D target bitmap associated with the
    // swap chain back buffer and set it as the current target.
    D2D1_BITMAP_PROPERTIES1 bitmapProperties =
        BitmapProperties1(
            D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
            PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
            m_dpi,
            m_dpi
            );

    ComPtr<IDXGISurface> dxgiBackBuffer;
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
        );

    DX::ThrowIfFailed(
        m_d2dContext->CreateBitmapFromDxgiSurface(
            dxgiBackBuffer.Get(),
            &bitmapProperties,
            &m_d2dTargetBitmap
            )
        );

    m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());

    // Grayscale text anti-aliasing is recommended for all Windows Store apps.
    m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);

}


Depois de salvar os valores de altura e largura atuais da janela para a próxima vez que esse método for chamado, converta os valores do DIP (pixel independente de dispositivo) para os limites de vídeo para pixels. No exemplo, chame ConvertDipsToPixels, que é uma função simples que executa esse código:

floor((dips * DisplayProperties::LogicalDpi / 96.0f) + 0.5f);

Adicione o 0.5f para assegurar o arredondamento para o valor inteiro mais próximo.

Fazendo um aparte, as coordenadas do CoreWindow são sempre definidas em DIPs. Para o Windows 8 e versões anteriores do Windows, um DIP é definido como 1/96a de uma polegada e alinhado à definição de SO de para cima. Quando a orientação de vídeo gira para o modo retrato, o aplicativo inverte a largura e a altura do CoreWindow, e o tamanho de destino de renderização (limites) deve mudar de acordo com isso. Como as coordenadas do Direct3D são sempre em pixels físicos, você deve converte os valores de DIP do CoreWindowpara valores inteiros de pixel antes de enviar esses valores para o Direct3D configurar a cadeia de permuta.

Nesse processo, você está trabalhando um pouco mais do que trabalharia se simplesmente redimensionasse a cadeia de permuta: você está efetivamente girando os componentes do Direct2D e Direct3D de sua imagem antes de compô-las para a apresentação e está informando a cadeia de permuta que renderizou os resultados em uma nova orientação. Veja um pouco mais de detalhes sobre este processo, conforme mostrado no exemplo de código para o DirectXBase::CreateWindowSizeDependentResources:

  • Determine a nova orientação do vídeo. Se o vídeo tiver sido invertido de paisagem para retrato ou vice-versa, troque os valores de altura e largura —mudados de valores DIP para pixels, naturalmente—para os limites de vídeo.

  • Depois, verifique se a cadeia de permuta foi criada. Se ele não tiver sido criado, o crie chamando IDXGIFactory2::CreateSwapChainForCoreWindow. Caso contrário, redimensione os buffers da cadeia de permuta existentes para as novas dimensões de vídeo chamando IDXGISwapchain:ResizeBuffers. Embora não seja necessário redimensionar a cadeia de permuta para o evento de rotação—afinal, você está emitindo o conteúdo já girado por seu pipeline de renderização, —existem outros eventos de mudança de dimensionamento, como eventos de ajuste e preenchimento, que exigem redimensionamento.

  • Depois disso, defina a transformação de matriz 2-D ou 3-D adequada para aplicar aos pixels ou aos vértices (respectivamente) no pipeline gráfico ao renderizá-los para a cadeia de permuta. Temos 4 métricas de rotação possíveis:

    • paisagem (DXGI_MODE_ROTATION_IDENTITY)
    • retrato (DXGI_MODE_ROTATION_ROTATE270)
    • paisagem, virado (DXGI_MODE_ROTATION_ROTATE180)
    • retrato, virado (DXGI_MODE_ROTATION_ROTATE90)

    A matriz correta é selecionada de acordo com os dados fornecidos pelo Windows 8 (como os resultados do DisplayProperties::OrientationChanged ou SimpleOrientationSensor::OrientationChanged) para determinar a orientação do vídeo e ela será multiplicada pelas coordenadas de cada pixel (Direct2D) ou vértice (Direct3D) na cena, girando-as efetivamente para alinhar à orientação da tela. (Note que no Direct2D, a origem da tela é definida como o canto superior esquerdo, enquanto em Direct3D a origem é definida como o centro lógico da janela.)

Observação  Para obter mais informações sobre as transformações 2-D usadas para rotação e como defini-las, veja Definindo as matrizes para a rotação da tela (2-D). Para obter mais informações sobre as transformações 3-D usadas para rotação, veja Definindo as matrizes para a rotação da tela (3-D).

Agora, veja um pequeno aviso: chame o IDXGISwapChain1::SetRotation e lhe forneça sua matriz de rotação atualização, dessa maneira:

m_swapChain->SetRotation(rotation);

Você também armazena a matriz de rotação selecionada onde seu método de renderização pode acessá-la quando calcula a nova projeção. Você usará essa matriz quando renderizar sua projeção final 3-D ou compor seu layout 2-D final. (Ele não o aplica automaticamente para você.)

Depois disso, crie um novo destino de renderização para o modo de exibição 3-D girado, bem como um novo buffer de estênceis de profundidade para o modo de exibição. Defina o visor de renderização 3-D para a cena girada chamando o ID3D11DeviceContext:RSSetViewports.

Por fim, se você tiver imagens 2-D para girar ou definir layout, crie um destino de renderização 2-D como um bitmap gravável para a cadeia de permuta redimensionada usando ID2D1DeviceContext::CreateBitmapFromDxgiSurface e componha seu novo layout para a orientação atualizada. Defina quaisquer propriedades necessárias no destino de renderização, como o modo de suavização (conforme visto no exemplo de código).

Agora, apresente a cadeia de permuta.

Reduza o atraso de rotação usando o CoreWindowResizeManager

Por padrão, o Windows 8 fornece um período breve, mas considerável para qualquer aplicativo, independentemente do modelo do aplicativo ou idioma, para concluir a rotação da imagem. Entretanto, há chances de que quando seu aplicativo executa o cálculo de rotação usando uma das técnicas descritas aqui, ele será feito bem antes desse período ser encerrado. Você gostaria de recuperar o tempo e concluir a animação, certo? É nesse ponto que o CoreWindowResizeManager entra.

Veja como usar o CoreWindowResizeManager: quando um evento CoreWindow::SizeChanged é acionado, chame o CoreWindowResizeManager::GetForCurrentView no manipulador para o evento obter uma instância do CoreWindowResizeManager e, quando o para a nova orientação for concluída e apresentada, chame o NotifyLayoutCompleted para permitir que o Windows saiba que ele pode concluir a animação de rotação e exiba a tela do aplicativo.

Veja como pode ser a aparência do código em seu manipulador de eventos para CoreWindow::SizeChanged:


CoreWindowResizeManager^ resizeManager = Windows::UI::Core::CoreWindowResizeManager::GetForCurrentView();

// ... build the layout for the new display orientation ...

resizeManager->NotifyLayoutCompleted();

Quando um usuário gira a orientação do vídeo, o Windows 8 mostra uma animação independente de seu aplicativo como feedback para o usuário. Existem três partes para aquela animação que ocorrem na seguinte ordem:

  • O Windows 8 reduz a imagem original.
  • O Windows 8 mantém a imagem pelo tempo que ele leva para recriar um novo layout. Esse é o período que você gostaria de reduzir, porque seu aplicativo provavelmente não precisará dele inteiro.
  • Quando a janela do layout expira, ou quando uma notificação da conclusão de layout e recebida, o Windows gira a imagem e efetua fading cruzado de zooms para nova orientação.

Conforme sugerido no terceiro marcador, quando uma aplicativo chama NotifyLayoutCompleted, o Windows 8 interrompe o período, concluir a animação de rotação e devolve o controle para o aplicativo, que agora está desenhando na nova orientação de vídeo. O efeito geral é que seu aplicativo agora está um pouco mais fluido, responsivo e eficiente!

Apêndice A: Aplicando métricas para rotação da tela (2-D)

No exemplo em Optimizando o processo de rotação (e no Exemplo de rotação de cadeia de permuta DXGI ), você pode ter percebido que tínhamos matrizes de rotação separadas para a saída de Direct2D e Direct3D. Vamos examinar as matrizes 2-D, primeiro.

Há dois motivos pelos quais não podemos aplicar as mesmas matrizes de rotação ao conteúdo do Direct2D e Direct3D:

  • Primeiro, elas usam diferentes modelos de coordenada cartesiana. O Direct2D usa a regra à direita, onde a coordenada y aumenta em valor positivo movimento para cima a partir da origem. Entretanto, o Direct3D isa a regra à esquerda, onde a coordenada y aumenta em valor positivo à direita a partir da origem. O resultado é a origem das coordenadas da tela no canto superior esquerdo para o Direct2D, enquanto a origem da tela (o plano de projeção) está no canto inferior esquerdo para o Direct3D. (Veja Sistemas de coordenadas 3D para saber mais.)

    Sistema de coordenadas Direct3D.Sistema de coordenadas Direct2D.
  • Segundo, as matrizes de rotação 3-D devem ser especificadas explicitamente para evitar os erros de arredondamento.

A cadeia de permuta assume que a origem esteja localizada no canto inferior esquerdo, portanto você tem que executar uma rotação para alinhar o sistema de coordenadas do Direct2D à direita com o sistema à esquerda usado pela cadeia de permuta. Especificamente, você reposiciona a imagem sob a nova orientação à esquerda multiplicando a matriz de rotação pela matriz de translação para a origem do sistema de coordenadas girada e transforma a imagem do espaço da coordenadas do CoreWindow no espaço de coordenadas da cadeia de permuta. Além disso, seu aplicativo deve aplicar consistentemente esta transformação quando o destino de renderização do Direct2D é conectado com a cadeia de permuta. Entretanto, se o seu aplicativo estiver desenhando para intermediar as superfícies que não são associadas diretamente com a cadeia de permuta, não aplique essa transformação de espaço de coordenada.

Seu código para selecionar a matriz correta a partir de quatro rotações possíveis pode se assemelhar a isso (lembre-se da conversão para a nova origem do sistema de coordenadas):


	// Set the proper orientation for the swap chain, and generate 2-D and
 // 3-D matrix transformations for rendering to the rotated swap chain.

    DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
    switch (m_orientation)
    {
        case DisplayOrientations::Landscape:
            rotation = DXGI_MODE_ROTATION_IDENTITY;
            m_rotationTransform2D = Matrix3x2F::Identity();
            break;
        case DisplayOrientations::Portrait:
            rotation = DXGI_MODE_ROTATION_ROTATE270;
            m_rotationTransform2D =
                Matrix3x2F::Rotation(270.0f) *
                Matrix3x2F::Translation(0.0f, m_windowBounds.Width);
            break;
        case DisplayOrientations::LandscapeFlipped:
            rotation = DXGI_MODE_ROTATION_ROTATE180;
            m_rotationTransform2D =
                Matrix3x2F::Rotation(180.0f) *
                Matrix3x2F::Translation(m_windowBounds.Width, m_windowBounds.Height);
            break;
        case DisplayOrientations::PortraitFlipped:
            rotation = DXGI_MODE_ROTATION_ROTATE90;
            m_rotationTransform2D =
                Matrix3x2F::Rotation(90.0f) *
                Matrix3x2F::Translation(m_windowBounds.Height, 0.0f);
             break;
        default:
            throw ref new Platform::FailureException();
            break;
    }

Depois que você tiver a matriz e a origem de rotação corretas para a imagem 2D, a defina com uma chamada para ID2D1DeviceContext::SetTransform entre suas chamadas para ID2D1DeviceContext::BeginDraw e ID2D1DeviceContext::EndDraw.

Aviso   O Direct2D não tem uma pilha de transformação. Se o seu aplicativo também estiver usando o ID2D1DeviceContext::SetTransform como parte de seu código de desenho, essa matriz precisa ser multiplicada posteriormente em qualquer outra transformação que você tenha aplicado.


m_d2dContext->BeginDraw();

// draw reference bitmap at the center of the screen
m_d2dContext->SetTransform(
        Matrix3x2F::Translation(
            m_windowBounds.Width / 2.0f - bitmapWidth / 2.0f,
            m_windowBounds.Height / 2.0f - bitmapHeight / 2.0f
        ) *
     m_rotationTransform2D // apply 2D prerotation transform
);
m_d2dContext->DrawBitmap(m_referenceBitmap.Get());

m_d2dcontext->EndDraw();

No exemplo anterior, você também centraliza a imagem multiplicando a nova matriz de rotação ainda por outra matriz de tradução que especifica o canto esquerdo superior da imagem, que sempre deve estar acima e à esquerda do centro esquerdo após a rotação. (Esta é uma etapa opcional.) Desenhe a imagem girada para o contexto de dispositivo usando uma chamada para ID2D1DeviceContext::DrawBitmap.

A próxima vez que você apresentar a cadeia de permuta, sua imagem 2D será girada para corresponder a nova orientação de vídeo.

Apêndice B: Aplicando métricas para rotação da tela (3D)

No exemplo em Otimizando o processo de rotação (e no Exemplo de rotação de cadeia de permuta DXGI ), definimos uma matriz de transformação específica para cada orientação de tela possível. Agora, vamos examinar as matrizes para girar cenas 3D. Como antes, crie um conjunto de matrizes para cada uma das 4 orientações possíveis. Para evitar erros de arredondamento e, portanto, artefatos visuais secundários, declare explicitamente as matrizes em seu código.

Configure essas matrizes de rotação 3D como a seguir. As matrizes exibidas no seguinte exemplo de código são matrizes de rotação padrão para rotações de 0, 90, 180 e 270 graus das vértices que definem pontos no espaço da cena 3D da câmera. Cada valor da coordenada [x, y, z] do vértice na cena é multiplicado por esta matriz de rotação quando a projeção 2D da cena é calculada.


	// Set the proper orientation for the swap chain, and generate the
	// 3-D matrix transformation for rendering to the rotated swap chain.
	DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
	switch (m_orientation)
	{
		case DisplayOrientations::Landscape:
			rotation = DXGI_MODE_ROTATION_IDENTITY;
			m_orientationTransform3D = XMFLOAT4X4( // 0-degree Z-rotation
				1.0f, 0.0f, 0.0f, 0.0f,
				0.0f, 1.0f, 0.0f, 0.0f,
				0.0f, 0.0f, 1.0f, 0.0f,
				0.0f, 0.0f, 0.0f, 1.0f
				);
			break;

		case DisplayOrientations::Portrait:
			rotation = DXGI_MODE_ROTATION_ROTATE270;
			m_orientationTransform3D = XMFLOAT4X4( // 90-degree Z-rotation
				0.0f, 1.0f, 0.0f, 0.0f,
				-1.0f, 0.0f, 0.0f, 0.0f,
				0.0f, 0.0f, 1.0f, 0.0f,
				0.0f, 0.0f, 0.0f, 1.0f
				);
			break;

		case DisplayOrientations::LandscapeFlipped:
			rotation = DXGI_MODE_ROTATION_ROTATE180;
			m_orientationTransform3D = XMFLOAT4X4( // 180-degree Z-rotation
				-1.0f, 0.0f, 0.0f, 0.0f,
				0.0f, -1.0f, 0.0f, 0.0f,
				0.0f, 0.0f, 1.0f, 0.0f,
				0.0f, 0.0f, 0.0f, 1.0f
				);
			break;

		case DisplayOrientations::PortraitFlipped:
			rotation = DXGI_MODE_ROTATION_ROTATE90;
			m_orientationTransform3D = XMFLOAT4X4( // 270-degree Z-rotation
				0.0f, -1.0f, 0.0f, 0.0f,
				1.0f, 0.0f, 0.0f, 0.0f,
				0.0f, 0.0f, 1.0f, 0.0f,
				0.0f, 0.0f, 0.0f, 1.0f
				);
			break;

		default:
			throw ref new Platform::FailureException();
	}

Defina o tipo de rotação na cadeia de permuta com uma chamada para IDXGISwapChain1::SetRotation, semelhante a isso:

m_swapChain->SetRotation(rotation);

Agora, em seu método de renderização, implemente algum código similar a isso:


struct ConstantBuffer // this struct provided for illustration
{
    // other constant buffer matrices and data defined here

    float4x4 projection; // current matrix for projection
} ;
ConstantBuffer  m_constantBufferData;          // constant buffer resource data

// ...

// rotate the projection matrix as it will be used to render to the rotated swap chain
m_constantBufferData.projection = mul(m_constantBufferData.projection, m_rotationTransform3D);

Agora, quando você chamar seu método de renderização, ela multiplica a matriz de rotação atual (conforme especificado pela variável de classem_orientationTransform3D) com a matriz de projeção atual e atribui os resultados daquela operação como a nova matriz de projeção para seu renderizador. Apresente a cadeia de permuta para ver a cena na orientação de vídeo atualizado!

Tópicos relacionados

Exemplo de rotação da cadeia de permuta DXGI
Diretrizes de rotação
Respondendo à interação do usuário (DirectX e C++)

 

 

Mostrar:
© 2015 Microsoft