Uso di Direct2D per il rendering lato server
Direct2D è ideale per le applicazioni grafiche che richiedono il rendering lato server in Windows Server. Questa panoramica descrive le nozioni di base sull'uso di Direct2D per il rendering lato server. Contiene le sezioni seguenti:
- Requisiti per il rendering lato server
- Opzioni per le API disponibili
- Come usare Direct2D per il rendering lato server
- Conclusione
- Argomenti correlati
Requisiti per il rendering lato server
Di seguito è riportato uno scenario tipico per un server grafico: viene eseguito il rendering di grafici e grafici in un server e recapitati come bitmap in risposta alle richieste Web. Il server potrebbe essere dotato di una scheda grafica di fascia bassa o di nessuna scheda grafica.
Questo scenario rivela tre requisiti dell'applicazione. In primo luogo, l'applicazione deve gestire più richieste simultanee in modo efficiente, soprattutto nei server multicore. In secondo luogo, l'applicazione deve usare il rendering software durante l'esecuzione su server con una scheda grafica di fascia bassa o nessuna scheda grafica. Infine, l'applicazione deve essere eseguita come servizio nella sessione 0 in modo che non richieda l'accesso di un utente. Per altre informazioni sulla sessione 0, vedi Compatibilità delle applicazioni - Isolamento sessione 0 e Linee guida per i driver UMDF.
Opzioni per le API disponibili
Sono disponibili tre opzioni per il rendering lato server: GDI, GDI+ e Direct2D. Come GDI e GDI+, Direct2D è un'API di rendering 2D nativa che offre alle applicazioni un maggiore controllo sull'uso dei dispositivi grafici. Inoltre, Direct2D supporta in modo univoco una factory a thread singolo e multithreading. Le sezioni seguenti confrontano ogni API in termini di qualità di disegno e rendering lato server multithreading.
GDI
A differenza di Direct2D e GDI+, GDI non supporta caratteristiche di disegno di alta qualità. Ad esempio, GDI non supporta l'antialiasing per la creazione di linee uniformi e ha solo un supporto limitato per la trasparenza. In base ai risultati dei test delle prestazioni grafiche in Windows 7 e Windows Server 2008 R2, Direct2D viene ridimensionato in modo più efficiente rispetto a GDI, nonostante la riprogettazione dei blocchi in GDI. Per altre informazioni su questi risultati dei test, vedere Engineering Windows 7 Graphics Performance.For more information about these test results, see Engineering Windows 7 Graphics Performance.
Inoltre, le applicazioni che usano GDI sono limitate a 10240 handle GDI per processo e 65536 handle GDI per sessione. Il motivo è che internamente Windows usa word a 16 bit per archiviare l'indice degli handle per ogni sessione.
GDI+
Sebbene GDI+ supporti la fusione antialiasing e alfa per il disegno di alta qualità, il problema principale con GDI+ per gli scenari server è che non supporta l'esecuzione nella sessione 0. Poiché la sessione 0 supporta solo funzionalità non interattive, le funzioni che interagiscono direttamente o indirettamente con i dispositivi di visualizzazione riceveranno quindi errori. Esempi specifici di funzioni includono non solo quelli che gestiscono i dispositivi di visualizzazione, ma anche quelli indirettamente che gestiscono i driver di dispositivo.
Analogamente a GDI, GDI+ è limitato dal meccanismo di blocco. I meccanismi di blocco in GDI+ sono gli stessi in Windows 7 e Windows Server 2008 R2 come nelle versioni precedenti.
Direct2D
Direct2D è un'API grafica 2D accelerata dall'hardware che offre prestazioni elevate e rendering di alta qualità. Offre una factory a thread singolo e una factory multithreading e la scalabilità lineare del rendering software con granularità grossolana.
A tale scopo, Direct2D definisce un'interfaccia della factory radice. Come regola, un oggetto creato in una factory può essere usato solo con altri oggetti creati dalla stessa factory. Il chiamante può richiedere una factory a thread singolo o multithreading al momento della creazione. Se viene richiesta una factory a thread singolo, non viene eseguito alcun blocco dei thread. Se il chiamante richiede una factory multithreading, viene acquisito un blocco thread a livello di factory ogni volta che viene effettuata una chiamata in Direct2D.
Inoltre, il blocco dei thread in Direct2D è più granulare rispetto a GDI e GDI+, in modo che l'aumento del numero di thread abbia un impatto minimo sulle prestazioni.
Come usare Direct2D per il rendering lato server
Le sezioni seguenti descrivono come usare il rendering software, come usare in modo ottimale una factory a thread singolo e una factory multithreading e come disegnare e salvare un disegno complesso in un file.
Software Rendering
Le applicazioni lato server usano il rendering software creando la destinazione di rendering IWICBitmap , con il tipo di destinazione di rendering impostato su D2D1_RENDER_TARGET_TYPE_SOFTWARE o D2D1_RENDER_TARGET_TYPE_DEFAULT. Per altre informazioni sulle destinazioni di rendering IWICBitmap, vedere il metodo ID2D1Factory::CreateWicBitmapRenderTarget. Per altre informazioni sui tipi di destinazione di rendering, vedere D2D1_RENDER_TARGET_TYPE.
Multithreading
Sapere come creare e condividere factory e destinazioni di rendering tra thread può influire significativamente sulle prestazioni di un'applicazione. Le tre figure seguenti mostrano tre approcci diversi. L'approccio ottimale è illustrato nella figura 3.
Nella figura 1, thread diversi condividono la stessa factory e la stessa destinazione di rendering. Questo approccio può causare risultati imprevedibili nei casi in cui più thread modificano simultaneamente lo stato della destinazione di rendering condivisa, ad esempio l'impostazione simultanea della matrice di trasformazione. Poiché il blocco interno in Direct2D non sincronizza una risorsa condivisa, ad esempio le destinazioni di rendering, questo approccio può causare l'esito negativo della chiamata BeginDraw nel thread 1, perché nel thread 2 la chiamata BeginDraw usa già la destinazione di rendering condivisa.
Per evitare i risultati imprevedibili rilevati nella figura 1, la figura 2 mostra una factory multithreading con ogni thread con una propria destinazione di rendering. Questo approccio funziona ma funziona in modo efficace come applicazione a thread singolo. Il motivo è che il blocco a livello di fabbrica si applica solo al livello di operazione di disegno e tutte le chiamate di disegno nella stessa factory vengono serializzate di conseguenza. Di conseguenza, il thread 1 viene bloccato quando si tenta di immettere una chiamata di disegno, mentre il thread 2 è al centro dell'esecuzione di un'altra chiamata di disegno.
La figura 3 mostra l'approccio ottimale, in cui vengono usate una factory a thread singolo e una destinazione di rendering a thread singolo. Poiché non viene eseguito alcun blocco quando si usa una factory a thread singolo, le operazioni di disegno in ogni thread possono essere eseguite simultaneamente per ottenere prestazioni ottimali.
Generazione di un file bitmap
Per generare un file bitmap usando il rendering software, usare una destinazione di rendering IWICBitmap . Usare un IWICStream per scrivere la bitmap in un file. Usare IWICBitmapFrameEncode per codificare la bitmap in un formato di immagine specificato. Nell'esempio di codice seguente viene illustrato come disegnare e salvare l'immagine seguente in un file.
Questo esempio di codice crea prima di tutto una destinazione di rendering IWICBitmap e IWICBitmap . Esegue quindi il rendering di un disegno con testo, una geometria di percorso che rappresenta un bicchiere di ora e un bicchiere di ora trasformato in una bitmap WIC. Usa quindi IWICStream::InitializeFromFilename per salvare la bitmap in un file. Se l'applicazione deve salvare la bitmap in memoria, usare invece IWICStream::InitializeFromMemory . Infine, usa IWICBitmapFrameEncode per codificare la 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();
}
Conclusione
Come illustrato in precedenza, l'uso di Direct2D per il rendering lato server è semplice e semplice. Offre inoltre un rendering altamente parallelizzabile e di alta qualità che può essere eseguito in ambienti con privilegi limitati del server.
Argomenti correlati