Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O Direct2D é adequado para aplicativos gráficos que exigem renderização do lado do servidor no Windows Server. Esta visão geral descreve os conceitos básicos do uso do Direct2D para renderização no servidor. Contém as seguintes secções:
- Requisitos para Server-Side renderização
- Opções de para APIs disponíveis
- Como usar o Direct2D para renderização Server-Side
- Conclusão
- Tópicos relacionados
Requisitos para renderização de Server-Side
O seguinte é um cenário típico para um servidor de gráficos: gráficos e ilustrações são renderizados em um servidor e fornecidos no formato bitmap em resposta a pedidos da Web. O servidor pode estar equipado com uma placa gráfica de baixa gama ou com nenhuma placa gráfica.
Este cenário revela três requisitos de aplicação. Primeiro, o aplicativo deve lidar com várias solicitações simultâneas de forma eficiente, especialmente em servidores multicore. Em segundo lugar, o aplicativo deve usar a renderização de software ao ser executado em servidores com uma placa gráfica low-end ou sem placa gráfica. Finalmente, o aplicativo deve ser executado como um serviço na Sessão 0 para que não exija que um usuário esteja conectado. Para obter mais informações sobre a Sessão Zero, consulte Application Compatibility - Session 0 Isolation e Session Zero Guidelines for UMDF Drivers.
Opções para APIs disponíveis
Há três opções para renderização do lado do servidor: GDI, GDI+ e Direct2D. Como GDI e GDI+, o Direct2D é uma API de renderização 2D nativa que dá aos aplicativos mais controle sobre o uso de dispositivos gráficos. Além disso, o Direct2D suporta exclusivamente uma fábrica de thread único e uma fábrica multithreaded. As seções a seguir comparam cada API em termos de qualidades de desenho e renderização multithreaded do lado do servidor.
GDI
Ao contrário do Direct2D e GDI+, o GDI não suporta recursos de desenho de alta qualidade. Por exemplo, o GDI não suporta antialiasing para a criação de linhas suaves e tem suporte limitado para transparência. Com base nos resultados do teste de desempenho gráfico no Windows 7 e no Windows Server 2008 R2, o Direct2D é dimensionado de forma mais eficiente do que o GDI, apesar do redesenho dos bloqueios no GDI. Para obter mais informações sobre esses resultados de teste, consulte Engineering Windows 7 Graphics Performance.
Além disso, os aplicativos que usam GDI são limitados a 10240 identificadores GDI por processo e 65536 identificadores GDI por sessão. O motivo é que internamente o Windows usa um WORD de 16 bits para armazenar o índice de identificadores para cada sessão.
GDI+
Embora o GDI+ suporte antialiasing e combinação alfa para criar gráficos de alta qualidade, o principal problema do GDI+ para caso de uso de servidor é que ele não suporta o funcionamento na Sessão 0. Como a Sessão 0 suporta apenas funcionalidades não interativas, as funções que interagem direta ou indiretamente com dispositivos de exibição receberão, portanto, erros. Exemplos específicos de funções incluem não apenas aqueles que lidam com dispositivos de exibição, mas também aqueles que lidam indiretamente com drivers de dispositivo.
Semelhante ao GDI, o GDI+ é limitado pelo seu mecanismo de bloqueio. Os mecanismos de bloqueio no GDI+ são os mesmos no Windows 7 e Windows Server 2008 R2 como nas versões anteriores.
Direct2D
O Direct2D é uma API gráfica 2D de modo imediato e acelerada por hardware que oferece alto desempenho e renderização de alta qualidade. Ele oferece uma fábrica monothread e multithread e o dimensionamento linear de renderização de software de granularidade grosseira.
Para fazer isso, o Direct2D define uma interface de fábrica raiz. Como regra, um objeto criado em uma fábrica só pode ser usado com outros objetos criados a partir da mesma fábrica. O chamador pode solicitar uma fábrica de único thread ou multithread quando esta é criada. Se uma fábrica com único thread for solicitada, nenhum bloqueio de threads será executado. Se o chamador solicitar uma fábrica multiencadeada, então, um bloqueio de encadeamento de toda a fábrica é estabelecido sempre que uma chamada é feita para o Direct2D.
Além disso, o bloqueio de threads no Direct2D é mais granular do que no GDI e GDI+, de modo que o aumento do número de threads tem impacto mínimo no desempenho.
Como usar o Direct2D para renderização de Server-Side
As secções a seguir descrevem como usar a renderização de software, como usar de forma ideal uma fábrica monothread e multithreaded e como criar e guardar um desenho complexo num ficheiro.
Renderização de software
As aplicações do lado do servidor utilizam a renderização de software ao criar IWICBitmap como alvo de renderização, com o tipo de alvo definido para D2D1_RENDER_TARGET_TYPE_SOFTWARE ou D2D1_RENDER_TARGET_TYPE_DEFAULT. Para obter mais informações sobre os alvos de renderização de IWICBitmap, consulte o método ID2D1Factory::CreateWicBitmapRenderTarget; para mais informações sobre os tipos de alvos de renderização, consulte D2D1_RENDER_TARGET_TYPE.
Multithreading
Saber como criar e compartilhar fábricas e renderizar destinos entre threads pode afetar significativamente o desempenho de um aplicativo. As três figuras seguintes mostram três abordagens variadas. A abordagem ótima é apresentada na figura 3.
Na figura 1, threads diferentes compartilham a mesma fábrica e o mesmo destino de renderização. Essa abordagem pode levar a resultados imprevisíveis nos casos em que vários threads alteram simultaneamente o estado do destino de renderização compartilhado, como a configuração simultânea da matriz de transformação. Como o bloqueio interno no Direct2D não sincroniza um recurso compartilhado, como destinos de renderização, essa abordagem pode fazer com que a chamada BeginDraw falhe no thread 1, porque no thread 2, a chamada BeginDraw já está usando o destino de renderização compartilhado.
Para evitar os resultados imprevisíveis encontrados na figura 1, a figura 2 mostra uma fábrica multiencadeada onde cada thread tem o seu próprio destino de renderização. Essa abordagem é eficaz, mas funciona como um aplicativo de thread único. O motivo é que o bloqueio de toda a fábrica se aplica apenas ao nível de operações de desenho, e, consequentemente, todas as chamadas de desenho na mesma fábrica são serializadas. Como resultado, o thread 1 fica bloqueado ao tentar inserir uma chamada de desenho, enquanto o thread 2 está no meio da execução de outra chamada de desenho.
A Figura 3 mostra a abordagem ideal, em que uma fábrica de thread único e um destino de renderização de thread único são usados. Uma vez que não é realizado nenhum bloqueio ao usar uma fábrica de um único thread, as operações de desenho em cada thread podem ser executadas concurrentemente para alcançar o desempenho ideal.
Gerando um arquivo bitmap
Para gerar um arquivo bitmap usando a renderização de software, use um destino de renderização IWICBitmap. Use um IWICStream para gravar o bitmap em um arquivo. Use IWICBitmapFrameEncode para codificar o bitmap em um formato de imagem especificado. O exemplo de código a seguir mostra como desenhar e salvar a imagem a seguir em um arquivo.
Este exemplo de código primeiro cria um IWICBitmap e um IWICBitmap destino de renderização. Em seguida, ele renderiza um desenho com algum texto, uma geometria de caminho representando uma ampulheta, e uma ampulheta transformada em um bitmap WIC. Em seguida, ele usa IWICStream::InitializeFromFilename para salvar o bitmap em um arquivo. Se seu aplicativo precisar salvar o bitmap na memória, use IWICStream::InitializeFromMemory em vez disso. Finalmente, ele usa IWICBitmapFrameEncode para codificar o bitmap.
// Create an IWICBitmap and RT
static const UINT sc_bitmapWidth = 640;
static const UINT sc_bitmapHeight = 480;
if (SUCCEEDED(hr))
{
hr = pWICFactory->CreateBitmap(
sc_bitmapWidth,
sc_bitmapHeight,
GUID_WICPixelFormat32bppBGR,
WICBitmapCacheOnLoad,
&pWICBitmap
);
}
// Set the render target type to D2D1_RENDER_TARGET_TYPE_DEFAULT to use software rendering.
if (SUCCEEDED(hr))
{
hr = pD2DFactory->CreateWicBitmapRenderTarget(
pWICBitmap,
D2D1::RenderTargetProperties(),
&pRT
);
}
// Create text format and a path geometry representing an hour glass.
if (SUCCEEDED(hr))
{
static const WCHAR sc_fontName[] = L"Calibri";
static const FLOAT sc_fontSize = 50;
hr = pDWriteFactory->CreateTextFormat(
sc_fontName,
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
sc_fontSize,
L"", //locale
&pTextFormat
);
}
if (SUCCEEDED(hr))
{
pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
hr = pD2DFactory->CreatePathGeometry(&pPathGeometry);
}
if (SUCCEEDED(hr))
{
hr = pPathGeometry->Open(&pSink);
}
if (SUCCEEDED(hr))
{
pSink->SetFillMode(D2D1_FILL_MODE_ALTERNATE);
pSink->BeginFigure(
D2D1::Point2F(0, 0),
D2D1_FIGURE_BEGIN_FILLED
);
pSink->AddLine(D2D1::Point2F(200, 0));
pSink->AddBezier(
D2D1::BezierSegment(
D2D1::Point2F(150, 50),
D2D1::Point2F(150, 150),
D2D1::Point2F(200, 200))
);
pSink->AddLine(D2D1::Point2F(0, 200));
pSink->AddBezier(
D2D1::BezierSegment(
D2D1::Point2F(50, 150),
D2D1::Point2F(50, 50),
D2D1::Point2F(0, 0))
);
pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
hr = pSink->Close();
}
if (SUCCEEDED(hr))
{
static const D2D1_GRADIENT_STOP stops[] =
{
{ 0.f, { 0.f, 1.f, 1.f, 1.f } },
{ 1.f, { 0.f, 0.f, 1.f, 1.f } },
};
hr = pRT->CreateGradientStopCollection(
stops,
ARRAYSIZE(stops),
&pGradientStops
);
}
if (SUCCEEDED(hr))
{
hr = pRT->CreateLinearGradientBrush(
D2D1::LinearGradientBrushProperties(
D2D1::Point2F(100, 0),
D2D1::Point2F(100, 200)),
D2D1::BrushProperties(),
pGradientStops,
&pLGBrush
);
}
if (SUCCEEDED(hr))
{
hr = pRT->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black),
&pBlackBrush
);
}
if (SUCCEEDED(hr))
{
// Render into the bitmap.
pRT->BeginDraw();
pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
D2D1_SIZE_F rtSize = pRT->GetSize();
// Set the world transform to a 45 degree rotation at the center of the render target
// and write "Hello, World".
pRT->SetTransform(
D2D1::Matrix3x2F::Rotation(
45,
D2D1::Point2F(
rtSize.width / 2,
rtSize.height / 2))
);
static const WCHAR sc_helloWorld[] = L"Hello, World!";
pRT->DrawText(
sc_helloWorld,
ARRAYSIZE(sc_helloWorld) - 1,
pTextFormat,
D2D1::RectF(0, 0, rtSize.width, rtSize.height),
pBlackBrush);
// Reset back to the identity transform.
pRT->SetTransform(D2D1::Matrix3x2F::Translation(0, rtSize.height - 200));
pRT->FillGeometry(pPathGeometry, pLGBrush);
pRT->SetTransform(D2D1::Matrix3x2F::Translation(rtSize.width - 200, 0));
pRT->FillGeometry(pPathGeometry, pLGBrush);
hr = pRT->EndDraw();
}
if (SUCCEEDED(hr))
{
// Save the image to a file.
hr = pWICFactory->CreateStream(&pStream);
}
WICPixelFormatGUID format = GUID_WICPixelFormatDontCare;
// Use InitializeFromFilename to write to a file. If there is need to write inside the memory, use InitializeFromMemory.
if (SUCCEEDED(hr))
{
static const WCHAR filename[] = L"output.png";
hr = pStream->InitializeFromFilename(filename, GENERIC_WRITE);
}
if (SUCCEEDED(hr))
{
hr = pWICFactory->CreateEncoder(GUID_ContainerFormatPng, NULL, &pEncoder);
}
if (SUCCEEDED(hr))
{
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr))
{
hr = pEncoder->CreateNewFrame(&pFrameEncode, NULL);
}
// Use IWICBitmapFrameEncode to encode the bitmap into the picture format you want.
if (SUCCEEDED(hr))
{
hr = pFrameEncode->Initialize(NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->SetSize(sc_bitmapWidth, sc_bitmapHeight);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->SetPixelFormat(&format);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->WriteSource(pWICBitmap, NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->Commit();
}
if (SUCCEEDED(hr))
{
hr = pEncoder->Commit();
}
Conclusão
Como visto acima, usar o Direct2D para renderização do lado do servidor é simples e direto. Além disso, ele fornece renderização de alta qualidade e altamente paralelizável que pode ser executada em ambientes de baixo privilégio do servidor.
Tópicos relacionados