Delen via


Direct2D gebruiken voor Server-Side Rendering

Direct2D is geschikt voor grafische toepassingen waarvoor rendering aan serverzijde op Windows Server is vereist. In dit overzicht worden de basisbeginselen beschreven van het gebruik van Direct2D voor rendering aan de serverzijde. Deze bevat de volgende secties:

Vereisten voor Server-Side Rendering

Hier volgt een typisch scenario voor een grafiekserver: grafieken en afbeeldingen worden weergegeven op een server en geleverd als bitmaps als reactie op webaanvragen. De server kan zijn uitgerust met een goedkope grafische kaart of helemaal geen grafische kaart.

In dit scenario worden drie toepassingsvereisten beschreven. Ten eerste moet de toepassing efficiënt meerdere gelijktijdige aanvragen verwerken, met name op multicore-servers. Ten tweede moet de toepassing softwarerendering gebruiken wanneer deze wordt uitgevoerd op servers met een goedkope grafische kaart of geen grafische kaart. Ten slotte moet de toepassing worden uitgevoerd als een service in Sessie 0, zodat er geen gebruiker hoeft te worden aangemeld. Raadpleeg Compatibiliteit van applicaties - Isolatie van Sessies 0 en Richtlijnen voor Sessies Zero voor UMDF-stuurprogramma'svoor meer informatie over Session 0.

Opties voor beschikbare API's

Er zijn drie opties voor rendering aan de serverzijde: GDI, GDI+ en Direct2D. Net als GDI en GDI+ is Direct2D een systeemeigen 2D-rendering-API die toepassingen meer controle geeft over het gebruik van grafische apparaten. Daarnaast biedt Direct2D unieke ondersteuning voor één thread en een multithreaded factory. In de volgende secties worden alle API's vergeleken met betrekking tot tekenkwaliteiten en multithreaded rendering aan de serverzijde.

GDI

In tegenstelling tot Direct2D en GDI+ biedt GDI geen ondersteuning voor tekenfuncties van hoge kwaliteit. GDI biedt bijvoorbeeld geen ondersteuning voor antialiasing voor het maken van vloeiende lijnen en heeft slechts beperkte ondersteuning voor transparantie. Op basis van de resultaten van de grafische prestatietest op Windows 7 en Windows Server 2008 R2 schaalt Direct2D efficiënter dan GDI, ondanks het herontwerp van vergrendelingen in GDI. Zie Engineering Windows 7 Graphics Performancevoor meer informatie over deze testresultaten.

Bovendien zijn toepassingen die gebruikmaken van GDI beperkt tot 10240 GDI-ingangen per proces en 65536 GDI-ingangen per sessie. De reden hiervoor is dat windows intern een 16-bits WORD gebruikt om de index van ingangen voor elke sessie op te slaan.

GDI+

Hoewel GDI+ ondersteuning biedt voor antialiasing en alfamixing voor hoogwaardige tekening, is het belangrijkste probleem met GDI+ voor serverscenario's dat het geen ondersteuning biedt voor uitvoering in Sessie 0. Omdat Session 0 alleen niet-interactieve functionaliteit ondersteunt, ontvangen functies die direct of indirect communiceren met weergaveapparaten fouten. Specifieke voorbeelden van functies zijn niet alleen de functies die te maken hebben met beeldschermapparaten, maar ook die indirect te maken hebben met apparaatstuurprogramma's.

Net als bij GDI wordt GDI+ beperkt door het vergrendelingsmechanisme. De vergrendelingsmechanismen in GDI+ zijn hetzelfde in Windows 7 en Windows Server 2008 R2 als in eerdere versies.

Direct2D

Direct2D is een hardwareversneld, immediate-mode, 2D-grafische API die hoge prestaties en hoogwaardige rendering biedt. Het biedt een fabriek met enkelvoudige thread en een met meerdere threads, en de lineaire schaalverdeling van grofkorrelige software-rendering.

Hiervoor definieert Direct2D een root factory-interface. In de regel kan een object dat op een fabriek is gemaakt, alleen worden gebruikt met andere objecten die zijn gemaakt op basis van dezelfde fabriek. De aanroeper kan een enkele thread of een multithreaded factory aanvragen wanneer deze wordt gemaakt. Als er een factory met één thread wordt aangevraagd, wordt er geen vergrendeling van threads uitgevoerd. Als de aanroeper een multithreaded factory aanvraagt, wordt er een fabrieksbrede threadvergrendeling verkregen wanneer er een aanroep wordt gedaan in Direct2D.

Bovendien is het vergrendelen van threads in Direct2D gedetailleerder dan in GDI en GDI+, zodat de toename van het aantal threads minimale invloed heeft op de prestaties.

Direct2D gebruiken voor Server-Side Rendering

In de volgende secties wordt beschreven hoe u softwarerendering gebruikt, hoe u optimaal gebruikmaakt van één thread en een multithreaded factory en hoe u een complexe tekening in een bestand tekent en opslaat.

Software weergave

Server-side applicaties maken gebruik van software-rendering door een IWICBitmap renderdoel te creëren, waarbij het renderdoeltype is ingesteld op D2D1_RENDER_TARGET_TYPE_SOFTWARE of D2D1_RENDER_TARGET_TYPE_DEFAULT. Zie de methode ID2D1Factory::CreateWicBitmapRenderTarget voor meer informatie over IWICBitmap renderdoelen; Zie D2D1_RENDER_TARGET_TYPEvoor meer informatie over de renderdoeltypen.

Multithreading

Als u weet hoe u factory's maakt en deelt en doelen over threads weergeeft, kan dit van invloed zijn op de prestaties van een toepassing. In de volgende drie cijfers ziet u drie verschillende benaderingen. De optimale benadering wordt weergegeven in afbeelding 3.

direct2d multithreading-diagram met één renderdoel.

In afbeelding 1 delen verschillende threads dezelfde factory en hetzelfde renderdoel. Deze benadering kan leiden tot onvoorspelbare resultaten in gevallen waarin meerdere threads tegelijkertijd de status van het gedeelde renderdoel wijzigen, zoals het tegelijkertijd instellen van de transformatiematrix. Omdat de interne vergrendeling in Direct2D geen gedeelde resource synchroniseert, zoals renderdoelen, kan deze methode ertoe leiden dat de aanroep BeginDraw mislukt in thread 1, omdat in thread 2 de BeginDraw-aanroep al gebruikmaakt van het gedeelde renderdoel.

direct2d multithreading-diagram met meerdere renderdoelen.

Om de onvoorspelbare resultaten in afbeelding 1 te voorkomen, toont afbeelding 2 een multithreaded factory waarbij elke thread een eigen renderdoel heeft. Deze benadering werkt, maar functioneert effectief als een toepassing met één thread. De reden hiervoor is dat de fabrieksbrede vergrendeling alleen van toepassing is op het tekenbewerkingsniveau en dat alle tekenoproepen in dezelfde fabriek daarom worden geserialiseerd. Als gevolg hiervan wordt thread 1 geblokkeerd bij het invoeren van een tekenoproep, terwijl thread 2 zich midden in het uitvoeren van een andere tekenoproep bevindt.

direct2d multithreading-diagram met meerdere factory's en meerdere renderdoelen.

In afbeelding 3 ziet u de optimale benadering, waarbij een factory met één thread en een renderdoel met één thread worden gebruikt. Omdat er geen vergrendeling wordt uitgevoerd wanneer u een factory met één thread gebruikt, kunnen tekenbewerkingen in elke thread gelijktijdig worden uitgevoerd om optimale prestaties te bereiken.

Een bitmapbestand genereren

Als u een bitmapbestand wilt genereren met softwarerendering, gebruikt u een IWICBitmap rendertarget. Gebruik een IWICStream- om de bitmap naar een bestand te schrijven. Gebruik IWICBitmapFrameEncode om de bitmap te coderen in een opgegeven afbeeldingsindeling. In het volgende codevoorbeeld ziet u hoe u de volgende afbeelding tekent en opslaat in een bestand.

uitvoer voorbeeldafbeelding.

In dit codevoorbeeld wordt eerst een IWICBitmap- en een IWICBitmap- renderdoel gemaakt. Vervolgens wordt een tekening weergegeven met tekst, een padgeometrie die een zandglas vertegenwoordigt, en een getransformeerd zandglas dat onderdeel wordt van een WIC-bitmap. Vervolgens wordt IWICStream::InitializeFromFilename gebruikt om de bitmap op te slaan in een bestand. Als uw toepassing de bitmap in het geheugen moet opslaan, gebruikt u in plaats daarvan IWICStream::InitializeFromMemory. Ten slotte wordt IWICBitmapFrameEncode gebruikt om de bitmap te coderen.

// 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();
}

Conclusie

Zoals u eerder hebt gezien, is het gebruik van Direct2D voor rendering aan de serverzijde eenvoudig en eenvoudig. Daarnaast biedt het hoogwaardige en zeer parallelle rendering die kan worden uitgevoerd in omgevingen met lage bevoegdheden van de server.

Direct2D-verwijzing