Rendering i DirectX

Anteckning

Den här artikeln handlar om äldre inbyggda WinRT-API:er. För nya interna appprojekt rekommenderar vi att du använder OpenXR-API:et.

Windows Mixed Reality bygger på DirectX för att skapa omfattande 3D-grafiska upplevelser för användare. Renderingsabstraktionen ligger precis ovanför DirectX, vilket gör att appar kan resonera kring positionen och orienteringen hos holografiska scenobservatörer som förutspås av systemet. Utvecklaren kan sedan hitta sina hologram baserat på varje kamera, så att appen kan rendera dessa hologram i olika rumsliga koordinatsystem när användaren rör sig.

Obs! Den här genomgången beskriver holografisk återgivning i Direct3D 11. En Direct3D 12-Windows Mixed Reality-appmall levereras också med tillägget Mixed Reality-appmallar.

Uppdatera för den aktuella ramen

Om du vill uppdatera programtillståndet för hologram kommer appen en gång per ram att:

  • Hämta en HolographicFrame från visningshanteringssystemet.
  • Uppdatera scenen med den aktuella förutsägelsen om var kameravyn ska vara när återgivningen är klar. Observera att det kan finnas fler än en kamera för den holografiska scenen.

Om du vill rendera till holografiska kameravyer kommer appen en gång per ram att:

  • För varje kamera renderar du scenen för den aktuella ramen med hjälp av kameravyn och projektionsmatriserna från systemet.

Skapa en ny holografisk ram och få dess förutsägelse

HolographicFrame har information som appen behöver för att uppdatera och återge den aktuella ramen. Appen börjar varje ny ram genom att anropa metoden CreateNextFrame . När den här metoden anropas görs förutsägelser med hjälp av de senaste tillgängliga sensordata och kapslas in i CurrentPrediction-objektet .

Ett nytt ramobjekt måste användas för varje renderad ram eftersom det endast är giltigt för en snabb tidpunkt. Egenskapen CurrentPrediction innehåller information som kamerapositionen. Informationen extrapoleras till den exakta tidpunkt då ramen förväntas vara synlig för användaren.

Följande kod hämtas från AppMain::Update:

// The HolographicFrame has information that the app needs in order
// to update and render the current frame. The app begins each new
// frame by calling CreateNextFrame.
HolographicFrame holographicFrame = m_holographicSpace.CreateNextFrame();

// Get a prediction of where holographic cameras will be when this frame
// is presented.
HolographicFramePrediction prediction = holographicFrame.CurrentPrediction();

Bearbeta kamerauppdateringar

Bakåtbuffertar kan ändras från ram till ram. Din app måste verifiera bufferten för varje kamera och släppa och återskapa resursvyer och djupbuffertar efter behov. Observera att uppsättningen poser i förutsägelsen är den auktoritativa listan över kameror som används i den aktuella ramen. Vanligtvis använder du den här listan för att iterera på uppsättningen kameror.

Från AppMain::Update:

m_deviceResources->EnsureCameraResources(holographicFrame, prediction);

Från DeviceResources::EnsureCameraResources:

for (HolographicCameraPose const& cameraPose : prediction.CameraPoses())
{
    HolographicCameraRenderingParameters renderingParameters = frame.GetRenderingParameters(cameraPose);
    CameraResources* pCameraResources = cameraResourceMap[cameraPose.HolographicCamera().Id()].get();
    pCameraResources->CreateResourcesForBackBuffer(this, renderingParameters);
}

Hämta koordinatsystemet som ska användas som grund för återgivning

Windows Mixed Reality låter din app skapa olika koordinatsystem, till exempel bifogade och stationära referensramar för att spåra platser i den fysiska världen. Din app kan sedan använda dessa koordinatsystem för att resonera om var hologram ska återges varje bildruta. När du begär koordinater från ett API skickar du alltid in SpatialCoordinateSystem där du vill att koordinaterna ska uttryckas.

Från AppMain::Update:

pose = SpatialPointerPose::TryGetAtTimestamp(
    m_stationaryReferenceFrame.CoordinateSystem(), prediction.Timestamp());

Dessa koordinatsystem kan sedan användas för att generera matriser för stereovyer när innehållet återges i din scen.

Från CameraResources::UpdateViewProjectionBuffer:

// Get a container object with the view and projection matrices for the given
// pose in the given coordinate system.
auto viewTransformContainer = cameraPose.TryGetViewTransform(coordinateSystem);

Bearbeta blick och gestinmatning

Blick och handindata är inte tidsbaserade och behöver inte uppdateras i funktionen StepTimer . Den här indatan är dock något som appen behöver titta på varje bildruta.

Bearbeta tidsbaserade uppdateringar

Alla realtidsrenderingsappar behöver ett sätt att bearbeta tidsbaserade uppdateringar – Windows Holographic-appmallen använder en StepTimer-implementering , ungefär som StepTimer som tillhandahålls i DirectX 11 UWP-appmallen. Den här StepTimer-exempelhjälpklassen kan tillhandahålla fasta tidsstegsuppdateringar, uppdateringar av variabelt tidssteg och standardläget är variabeltidssteg.

För holografisk återgivning har vi valt att inte lägga för mycket på timerfunktionen eftersom du kan konfigurera den som ett fast tidssteg. Det kan anropas mer än en gång per bildruta – eller inte alls för vissa bildrutor – och våra holografiska datauppdateringar bör ske en gång per bildruta.

Från AppMain::Update:

m_timer.Tick([this]()
{
    m_spinningCubeRenderer->Update(m_timer);
});

Placera och rotera hologram i koordinatsystemet

Om du arbetar i ett enda koordinatsystem, som mallen gör med SpatialStationaryReferenceFrame, skiljer sig den här processen inte från vad du annars är van vid i 3D-grafik. Här roterar vi kuben och anger modellmatrisen baserat på positionen i det stationära koordinatsystemet.

Från SpinningCubeRenderer::Update:

// Rotate the cube.
// Convert degrees to radians, then convert seconds to rotation angle.
const float    radiansPerSecond = XMConvertToRadians(m_degreesPerSecond);
const double   totalRotation = timer.GetTotalSeconds() * radiansPerSecond;
const float    radians = static_cast<float>(fmod(totalRotation, XM_2PI));
const XMMATRIX modelRotation = XMMatrixRotationY(-radians);

// Position the cube.
const XMMATRIX modelTranslation = XMMatrixTranslationFromVector(XMLoadFloat3(&m_position));

// Multiply to get the transform matrix.
// Note that this transform does not enforce a particular coordinate system. The calling
// class is responsible for rendering this content in a consistent manner.
const XMMATRIX modelTransform = XMMatrixMultiply(modelRotation, modelTranslation);

// The view and projection matrices are provided by the system; they are associated
// with holographic cameras, and updated on a per-camera basis.
// Here, we provide the model transform for the sample hologram. The model transform
// matrix is transposed to prepare it for the shader.
XMStoreFloat4x4(&m_modelConstantBufferData.model, XMMatrixTranspose(modelTransform));

Information om avancerade scenarier: Den snurrande kuben är ett enkelt exempel på hur du placerar ett hologram inom en enda referensram. Det är också möjligt att använda flera SpatialCoordinateSystems i samma renderade ram samtidigt.

Uppdatera konstanta buffertdata

Modelltransformeringar för innehåll uppdateras som vanligt. Vid det här laget har du beräknat giltiga transformeringar för koordinatsystemet som du ska återge i.

Från SpinningCubeRenderer::Update:

// Update the model transform buffer for the hologram.
context->UpdateSubresource(
    m_modelConstantBuffer.Get(),
    0,
    nullptr,
    &m_modelConstantBufferData,
    0,
    0
);

Hur blir det med omvandlingar av vyer och projektioner? För bästa resultat vill vi vänta tills vi är nästan redo för våra anrop innan vi får dessa.

Rendera den aktuella ramen

Rendering på Windows Mixed Reality skiljer sig inte mycket från rendering på en 2D-monoskärm, men det finns några skillnader:

  • Förutsägelser för holografisk ram är viktiga. Desto närmare förutsägelsen är när din ram visas, desto bättre ser dina hologram ut.
  • Windows Mixed Reality styr kameravyerna. Rendera till var och en eftersom den holografiska ramen kommer att presentera dem för dig senare.
  • Vi rekommenderar att du gör stereorendering med instansritning till en målmatris för återgivning. Den holografiska appmallen använder den rekommenderade metoden för instansritning till en målmatris för återgivning, som använder en återgivningsmålvy på en Texture2DArray.
  • Om du vill rendera utan att använda stereo-instancing måste du skapa två RenderTargetViews som inte är matriser, en för varje öga. Varje RenderTargetViews refererar till en av de två segmenten i Texture2DArray som tillhandahålls till appen från systemet. Detta rekommenderas inte eftersom det vanligtvis är långsammare än att använda instancing.

Hämta en uppdaterad HolographicFrame-förutsägelse

Om du uppdaterar bildramsförutsägelserna förbättras bildstabiliseringens effektivitet. Du får en mer exakt placering av hologram på grund av den kortare tiden mellan förutsägelsen och när ramen är synlig för användaren. Vi rekommenderar att du uppdaterar din bildruteförutsägelse precis innan återgivningen.

holographicFrame.UpdateCurrentPrediction();
HolographicFramePrediction prediction = holographicFrame.CurrentPrediction();

Rendera till varje kamera

Loopa på kamerauppsättningen i förutsägelsen och rendera till varje kamera i den här uppsättningen.

Konfigurera renderingspasset

Windows Mixed Reality använder stereoskopisk återgivning för att förbättra illusionen av djup och för att återge stereoskopiskt, så både vänster och höger display är aktiva. Med stereoskopisk återgivning finns det en förskjutning mellan de två skärmarna, som hjärnan kan stämma av som faktiskt djup. Det här avsnittet beskriver stereoskopisk rendering med hjälp av instancing, med hjälp av kod från Windows Holographic-appmallen.

Varje kamera har ett eget återgivningsmål (bakåtbuffert) och visnings- och projektionsmatriser i det holografiska utrymmet. Din app måste skapa andra kamerabaserade resurser , till exempel djupbufferten, per kamera. I Windows Holographic-appmallen tillhandahåller vi en hjälpklass för att paketera dessa resurser tillsammans i DX::CameraResources. Börja med att konfigurera återgivningsmålvyerna:

Från AppMain::Render:

// This represents the device-based resources for a HolographicCamera.
DX::CameraResources* pCameraResources = cameraResourceMap[cameraPose.HolographicCamera().Id()].get();

// Get the device context.
const auto context = m_deviceResources->GetD3DDeviceContext();
const auto depthStencilView = pCameraResources->GetDepthStencilView();

// Set render targets to the current holographic camera.
ID3D11RenderTargetView *const targets[1] =
    { pCameraResources->GetBackBufferRenderTargetView() };
context->OMSetRenderTargets(1, targets, depthStencilView);

// Clear the back buffer and depth stencil view.
if (m_canGetHolographicDisplayForCamera &&
    cameraPose.HolographicCamera().Display().IsOpaque())
{
    context->ClearRenderTargetView(targets[0], DirectX::Colors::CornflowerBlue);
}
else
{
    context->ClearRenderTargetView(targets[0], DirectX::Colors::Transparent);
}
context->ClearDepthStencilView(
    depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

Använd förutsägelsen för att hämta kamerans vy- och projektionsmatriser

Matriserna för vy och projektion för varje holografisk kamera ändras med varje bildruta. Uppdatera data i konstantbufferten för varje holografisk kamera. Gör detta när du har uppdaterat förutsägelsen och innan du gör några anrop till kameran.

Från AppMain::Render:

// The view and projection matrices for each holographic camera will change
// every frame. This function refreshes the data in the constant buffer for
// the holographic camera indicated by cameraPose.
if (m_stationaryReferenceFrame)
{
    pCameraResources->UpdateViewProjectionBuffer(
        m_deviceResources, cameraPose, m_stationaryReferenceFrame.CoordinateSystem());
}

// Attach the view/projection constant buffer for this camera to the graphics pipeline.
bool cameraActive = pCameraResources->AttachViewProjectionBuffer(m_deviceResources);

Här visar vi hur matriserna förvärvas från kameraställningen. Under den här processen får vi även den aktuella visningsporten för kameran. Observera att vi tillhandahåller ett koordinatsystem: det här är samma koordinatsystem som vi använde för att förstå blicken, och det är samma som vi använde för att placera den snurrande kuben.

Från CameraResources::UpdateViewProjectionBuffer:

// The system changes the viewport on a per-frame basis for system optimizations.
auto viewport = cameraPose.Viewport();
m_d3dViewport = CD3D11_VIEWPORT(
    viewport.X,
    viewport.Y,
    viewport.Width,
    viewport.Height
);

// The projection transform for each frame is provided by the HolographicCameraPose.
HolographicStereoTransform cameraProjectionTransform = cameraPose.ProjectionTransform();

// Get a container object with the view and projection matrices for the given
// pose in the given coordinate system.
auto viewTransformContainer = cameraPose.TryGetViewTransform(coordinateSystem);

// If TryGetViewTransform returns a null pointer, that means the pose and coordinate
// system cannot be understood relative to one another; content cannot be rendered
// in this coordinate system for the duration of the current frame.
// This usually means that positional tracking is not active for the current frame, in
// which case it is possible to use a SpatialLocatorAttachedFrameOfReference to render
// content that is not world-locked instead.
DX::ViewProjectionConstantBuffer viewProjectionConstantBufferData;
bool viewTransformAcquired = viewTransformContainer != nullptr;
if (viewTransformAcquired)
{
    // Otherwise, the set of view transforms can be retrieved.
    HolographicStereoTransform viewCoordinateSystemTransform = viewTransformContainer.Value();

    // Update the view matrices. Holographic cameras (such as Microsoft HoloLens) are
    // constantly moving relative to the world. The view matrices need to be updated
    // every frame.
    XMStoreFloat4x4(
        &viewProjectionConstantBufferData.viewProjection[0],
        XMMatrixTranspose(XMLoadFloat4x4(&viewCoordinateSystemTransform.Left) *
            XMLoadFloat4x4(&cameraProjectionTransform.Left))
    );
    XMStoreFloat4x4(
        &viewProjectionConstantBufferData.viewProjection[1],
        XMMatrixTranspose(XMLoadFloat4x4(&viewCoordinateSystemTransform.Right) *
            XMLoadFloat4x4(&cameraProjectionTransform.Right))
    );
}

Visningsområdet ska ställas in för varje bildruta. Hörnskuggaren (åtminstone) behöver vanligtvis åtkomst till visnings-/projektionsdata.

Från CameraResources::AttachViewProjectionBuffer:

// Set the viewport for this camera.
context->RSSetViewports(1, &m_d3dViewport);

// Send the constant buffer to the vertex shader.
context->VSSetConstantBuffers(
    1,
    1,
    m_viewProjectionConstantBuffer.GetAddressOf()
);

Rendera till kamerans bakåtbuffert och checka in djupbufferten:

Det är en bra idé att kontrollera att TryGetViewTransform lyckades innan du försökte använda visnings-/projektionsdata, för om koordinatsystemet inte går att begränsa (till exempel om spårningen avbröts) kan appen inte rendera med den för den ramen. Mallen anropar endast Render på den snurrande kuben om klassen CameraResources indikerar en lyckad uppdatering.

Windows Mixed Reality innehåller funktioner för bildstabilisering för att hålla hologram placerade där en utvecklare eller användare placerar dem i världen. Bildstabilisering hjälper till att dölja svarstiden i en återgivningspipeline för att säkerställa de bästa holografiska upplevelserna för användarna. En fokuspunkt kan anges för att förbättra bildstabiliseringen ytterligare, eller så kan en djupbuffert tillhandahållas för beräkningsoptimerad bildstabilisering i realtid.

För bästa resultat bör appen tillhandahålla en djupbuffert med hjälp av API:et CommitDirect3D11DepthBuffer . Windows Mixed Reality kan sedan använda geometriinformation från djupbufferten för att optimera bildstabilisering i realtid. Windows Holographic App-mallen checkar in appens djupbuffert som standard, vilket hjälper till att optimera hologramstabiliteten.

Från AppMain::Render:

// Only render world-locked content when positional tracking is active.
if (cameraActive)
{
    // Draw the sample hologram.
    m_spinningCubeRenderer->Render();
    if (m_canCommitDirect3D11DepthBuffer)
    {
        // On versions of the platform that support the CommitDirect3D11DepthBuffer API, we can 
        // provide the depth buffer to the system, and it will use depth information to stabilize 
        // the image at a per-pixel level.
        HolographicCameraRenderingParameters renderingParameters =
            holographicFrame.GetRenderingParameters(cameraPose);
        
        IDirect3DSurface interopSurface =
            DX::CreateDepthTextureInteropObject(pCameraResources->GetDepthStencilTexture2D());

        // Calling CommitDirect3D11DepthBuffer causes the system to queue Direct3D commands to 
        // read the depth buffer. It will then use that information to stabilize the image as
        // the HolographicFrame is presented.
        renderingParameters.CommitDirect3D11DepthBuffer(interopSurface);
    }
}

Anteckning

Windows bearbetar djupstrukturen på GPU:n, så det måste vara möjligt att använda djupbufferten som en skuggresurs. ID3D11Texture2D som du skapar bör vara i ett typlöst format och det bör vara bundet som en skuggningsresursvy. Här är ett exempel på hur du skapar en djupstruktur som kan checkas in för bildstabilisering.

Kod för att skapa djupbuffertresurser för CommitDirect3D11DepthBuffer:

// Create a depth stencil view for use with 3D rendering if needed.
CD3D11_TEXTURE2D_DESC depthStencilDesc(
    DXGI_FORMAT_R16_TYPELESS,
    static_cast<UINT>(m_d3dRenderTargetSize.Width),
    static_cast<UINT>(m_d3dRenderTargetSize.Height),
    m_isStereo ? 2 : 1, // Create two textures when rendering in stereo.
    1, // Use a single mipmap level.
    D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE
);

winrt::check_hresult(
    device->CreateTexture2D(
        &depthStencilDesc,
        nullptr,
        &m_d3dDepthStencil
    ));

CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(
    m_isStereo ? D3D11_DSV_DIMENSION_TEXTURE2DARRAY : D3D11_DSV_DIMENSION_TEXTURE2D,
    DXGI_FORMAT_D16_UNORM
);
winrt::check_hresult(
    device->CreateDepthStencilView(
        m_d3dDepthStencil.Get(),
        &depthStencilViewDesc,
        &m_d3dDepthStencilView
    ));

Rita holografiskt innehåll

Windows Holographic App-mallen återger innehåll i stereo med hjälp av den rekommenderade metoden för att rita instanserad geometri till en Texture2DArray av storlek 2. Nu ska vi titta på den instancing delen av detta och hur det fungerar på Windows Mixed Reality.

Från SpinningCubeRenderer::Render:

// Draw the objects.
context->DrawIndexedInstanced(
    m_indexCount,   // Index count per instance.
    2,              // Instance count.
    0,              // Start index location.
    0,              // Base vertex location.
    0               // Start instance location.
);

Varje instans kommer åt en annan vy/projektionsmatris från den konstanta bufferten. Här är den konstanta buffertstrukturen, som bara är en matris med två matriser.

Från VertexShaderShared.hlsl, som ingår i VPRTVertexShader.hlsl:

// A constant buffer that stores each set of view and projection matrices in column-major format.
cbuffer ViewProjectionConstantBuffer : register(b1)
{
    float4x4 viewProjection[2];
};

Målmatrisindexet för återgivning måste anges för varje pixel. I följande kodfragment mappas output.viewId till SV_RenderTargetArrayIndex semantik. Detta kräver stöd för en valfri Direct3D 11.3-funktion, som gör det möjligt att återge målmatrisens indexsemantik från valfri skuggningsfas.

Från VPRTVertexShader.hlsl:

// Per-vertex data passed to the geometry shader.
struct VertexShaderOutput
{
    min16float4 pos     : SV_POSITION;
    min16float3 color   : COLOR0;

    // The render target array index is set here in the vertex shader.
    uint        viewId  : SV_RenderTargetArrayIndex;
};

Från VertexShaderShared.hlsl, som ingår i VPRTVertexShader.hlsl:

// Per-vertex data used as input to the vertex shader.
struct VertexShaderInput
{
    min16float3 pos     : POSITION;
    min16float3 color   : COLOR0;
    uint        instId  : SV_InstanceID;
};

// Simple shader to do vertex processing on the GPU.
VertexShaderOutput main(VertexShaderInput input)
{
    VertexShaderOutput output;
    float4 pos = float4(input.pos, 1.0f);

    // Note which view this vertex has been sent to. Used for matrix lookup.
    // Taking the modulo of the instance ID allows geometry instancing to be used
    // along with stereo instanced drawing; in that case, two copies of each 
    // instance would be drawn, one for left and one for right.
    int idx = input.instId % 2;

    // Transform the vertex position into world space.
    pos = mul(pos, model);

    // Correct for perspective and project the vertex position onto the screen.
    pos = mul(pos, viewProjection[idx]);
    output.pos = (min16float4)pos;

    // Pass the color through without modification.
    output.color = input.color;

    // Set the render target array index.
    output.viewId = idx;

    return output;
}

Om du vill använda dina befintliga instanserade ritningstekniker med den här metoden för ritning till en målmatris för stereoåtergivning ritar du dubbelt så många instanser som du normalt har. I skuggningen delar du input.instId med 2 för att hämta det ursprungliga instans-ID:t, som kan indexeras till (till exempel) en buffert med data per objekt: int actualIdx = input.instId / 2;

Viktig information om att återge stereoinnehåll på HoloLens

Windows Mixed Reality har stöd för möjligheten att ange målmatrisindexet för återgivningen från valfri skuggningsfas. Normalt är detta en uppgift som bara kan utföras i geometriskuggningssteget på grund av hur semantiken definieras för Direct3D 11. Här visar vi ett fullständigt exempel på hur du konfigurerar en renderingspipeline med bara hörn- och pixelskuggningsstegen inställda. Skuggningskoden är enligt beskrivningen ovan.

Från SpinningCubeRenderer::Render:

const auto context = m_deviceResources->GetD3DDeviceContext();

// Each vertex is one instance of the VertexPositionColor struct.
const UINT stride = sizeof(VertexPositionColor);
const UINT offset = 0;
context->IASetVertexBuffers(
    0,
    1,
    m_vertexBuffer.GetAddressOf(),
    &stride,
    &offset
);
context->IASetIndexBuffer(
    m_indexBuffer.Get(),
    DXGI_FORMAT_R16_UINT, // Each index is one 16-bit unsigned integer (short).
    0
);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->IASetInputLayout(m_inputLayout.Get());

// Attach the vertex shader.
context->VSSetShader(
    m_vertexShader.Get(),
    nullptr,
    0
);
// Apply the model constant buffer to the vertex shader.
context->VSSetConstantBuffers(
    0,
    1,
    m_modelConstantBuffer.GetAddressOf()
);

// Attach the pixel shader.
context->PSSetShader(
    m_pixelShader.Get(),
    nullptr,
    0
);

// Draw the objects.
context->DrawIndexedInstanced(
    m_indexCount,   // Index count per instance.
    2,              // Instance count.
    0,              // Start index location.
    0,              // Base vertex location.
    0               // Start instance location.
);

Viktig information om återgivning på icke-HoloLens-enheter

Om du ställer in målmatrisindexet för återgivning i hörnskuggningen måste grafikdrivrutinen ha stöd för en valfri Direct3D 11.3-funktion, som HoloLens stöder. Din app kan på ett säkert sätt implementera just den tekniken för återgivning, och alla krav uppfylls för att köras på Microsoft HoloLens.

Det kan vara så att du också vill använda HoloLens-emulatorn, som kan vara ett kraftfullt utvecklingsverktyg för din holografiska app – och stödja Windows Mixed Reality uppslukande headsetenheter som är anslutna till Windows 10 datorer. Stöd för icke-HoloLens-återgivningssökvägen – för alla Windows Mixed Reality – är också inbyggt i Windows Holographic-appmallen. I mallkoden hittar du kod som gör att din holografiska app kan köras på GPU:n i utvecklingsdatorn. Så här söker klassen DeviceResources efter det här valfria funktionsstödet.

Från DeviceResources::CreateDeviceResources:

// Check for device support for the optional feature that allows setting the render target array index from the vertex shader stage.
D3D11_FEATURE_DATA_D3D11_OPTIONS3 options;
m_d3dDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS3, &options, sizeof(options));
if (options.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer)
{
    m_supportsVprt = true;
}

Om du vill ha stöd för återgivning utan den här valfria funktionen måste appen använda en geometrisk skuggning för att ange målmatrisindexet för återgivning. Det här kodfragmentet läggs till efterVSSetConstantBuffers och förePSSetShader i kodexemplet som visas i föregående avsnitt som förklarar hur du renderar stereo på HoloLens.

Från SpinningCubeRenderer::Render:

if (!m_usingVprtShaders)
{
    // On devices that do not support the D3D11_FEATURE_D3D11_OPTIONS3::
    // VPAndRTArrayIndexFromAnyShaderFeedingRasterizer optional feature,
    // a pass-through geometry shader is used to set the render target 
    // array index.
    context->GSSetShader(
        m_geometryShader.Get(),
        nullptr,
        0
    );
}

HLSL OBS! I det här fallet måste du också läsa in en något modifierad hörnskuggning som skickar målmatrisindexet till geometriskuggningen med hjälp av en alltid tillåten skuggningssemantik, till exempel TEXCOORD0. Geometriskuggaren behöver inte utföra något arbete. mallens geometriskuggning passerar genom alla data, med undantag för renderningsmålmatrisindexet, som används för att ange SV_RenderTargetArrayIndex semantik.

Appmallkod för GeometryShader.hlsl:

// Per-vertex data from the vertex shader.
struct GeometryShaderInput
{
    min16float4 pos     : SV_POSITION;
    min16float3 color   : COLOR0;
    uint instId         : TEXCOORD0;
};

// Per-vertex data passed to the rasterizer.
struct GeometryShaderOutput
{
    min16float4 pos     : SV_POSITION;
    min16float3 color   : COLOR0;
    uint rtvId          : SV_RenderTargetArrayIndex;
};

// This geometry shader is a pass-through that leaves the geometry unmodified 
// and sets the render target array index.
[maxvertexcount(3)]
void main(triangle GeometryShaderInput input[3], inout TriangleStream<GeometryShaderOutput> outStream)
{
    GeometryShaderOutput output;
    [unroll(3)]
    for (int i = 0; i < 3; ++i)
    {
        output.pos   = input[i].pos;
        output.color = input[i].color;
        output.rtvId = input[i].instId;
        outStream.Append(output);
    }
}

Närvarande

Aktivera den holografiska ramen för att presentera växlingskedjan

Med Windows Mixed Reality styr systemet växlingskedjan. Systemet hanterar sedan presentation av ramar till varje holografisk kamera för att säkerställa en högkvalitativ användarupplevelse. Det ger också en viewport-uppdatering av varje bildruta för varje kamera för att optimera aspekter av systemet, till exempel bildstabilisering eller Mixed Reality Capture. En holografisk app som använder DirectX anropar alltså inte Present på en DXGI-växlingskedja. I stället använder du klassen HolographicFrame för att presentera alla swapchains för en ram när du är klar med att rita den.

Från DeviceResources::P resent:

HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();

Som standard väntar det här API:et på att ramen ska slutföras innan den returneras. Holografiska appar bör vänta tills den föregående ramen har slutförts innan arbetet med en ny ram påbörjas, eftersom det minskar svarstiden och ger bättre resultat från förutsägelser om holografisk ram. Detta är inte en hård regel, och om du har ramar som tar längre tid än en skärmuppdatering att rendera kan du inaktivera den här väntetiden genom att skicka parametern HolographicFramePresentWaitBehavior till PresentUsingCurrentPrediction. I det här fallet skulle du förmodligen använda en asynkron återgivningstråd för att upprätthålla en kontinuerlig belastning på GPU:n. Uppdateringshastigheten för HoloLens-enheten är 60 hz, där en bildruta har en varaktighet på cirka 16 ms. Immersive headset enheter kan variera från 60 hz till 90 hz; när du uppdaterar skärmen vid 90 hz har varje bildruta en varaktighet på cirka 11 ms.

Hantera DeviceLost-scenarier i samarbete med HolographicFrame

DirectX 11-appar skulle vanligtvis vilja kontrollera HRESULT som returneras av DXGI-växlingskedjans Present-funktion för att ta reda på om det fanns ett DeviceLost-fel . Klassen HolographicFrame hanterar detta åt dig. Granska den returnerade HolographicFramePresentResult för att ta reda på om du behöver släppa och återskapa Direct3D-enheten och enhetsbaserade resurser.

// The PresentUsingCurrentPrediction API will detect when the graphics device
// changes or becomes invalid. When this happens, it is considered a Direct3D
// device lost scenario.
if (presentResult == HolographicFramePresentResult::DeviceRemoved)
{
    // The Direct3D device, context, and resources should be recreated.
    HandleDeviceLost();
}

Om Direct3D-enheten gick förlorad och du återskapade den måste du be HolographicSpace att börja använda den nya enheten. Växlingskedjan återskapas för den här enheten.

Från DeviceResources::InitializeUsingHolographicSpace:

m_holographicSpace.SetDirect3D11Device(m_d3dInteropDevice);

När ramen har presenterats kan du återgå till huvudprogramloopen och låta den fortsätta till nästa bildruta.

Hybridgrafikdatorer och mixed reality-program

Windows 10 Creators Update-datorer kan konfigureras med både diskreta och integrerade GPU:er. Med dessa typer av datorer väljer Windows det kort som headsetet är anslutet till. Program måste se till att Den DirectX-enhet som skapas använder samma adapter.

De flesta allmänna Direct3D-exempelkoder visar hur du skapar en DirectX-enhet med hjälp av standardmaskinvarans adapter, som i ett hybridsystem kanske inte är samma som den som används för headsetet.

Om du vill lösa eventuella problem använder du HolographicAdapterID från antingen HolographicSpace. PrimaryAdapterId() eller HolographicDisplay. AdapterId(). Detta adapterId kan sedan användas för att välja rätt DXGIAdapter med IDXGIFactory4.EnumAdapterByLuid.

Från DeviceResources::InitializeUsingHolographicSpace:

// The holographic space might need to determine which adapter supports
// holograms, in which case it will specify a non-zero PrimaryAdapterId.
LUID id =
{
    m_holographicSpace.PrimaryAdapterId().LowPart,
    m_holographicSpace.PrimaryAdapterId().HighPart
};

// When a primary adapter ID is given to the app, the app should find
// the corresponding DXGI adapter and use it to create Direct3D devices
// and device contexts. Otherwise, there is no restriction on the DXGI
// adapter the app can use.
if ((id.HighPart != 0) || (id.LowPart != 0))
{
    UINT createFlags = 0;

    // Create the DXGI factory.
    ComPtr<IDXGIFactory1> dxgiFactory;
    winrt::check_hresult(
        CreateDXGIFactory2(
            createFlags,
            IID_PPV_ARGS(&dxgiFactory)
        ));
    ComPtr<IDXGIFactory4> dxgiFactory4;
    winrt::check_hresult(dxgiFactory.As(&dxgiFactory4));

    // Retrieve the adapter specified by the holographic space.
    winrt::check_hresult(
        dxgiFactory4->EnumAdapterByLuid(
            id,
            IID_PPV_ARGS(&m_dxgiAdapter)
        ));
}
else
{
    m_dxgiAdapter.Reset();
}

Kod för att uppdatera DeviceResources::CreateDeviceResources för att använda IDXGIAdapter

// Create the Direct3D 11 API device object and a corresponding context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;

const D3D_DRIVER_TYPE driverType = m_dxgiAdapter == nullptr ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_UNKNOWN;
const HRESULT hr = D3D11CreateDevice(
    m_dxgiAdapter.Get(),        // Either nullptr, or the primary adapter determined by Windows Holographic.
    driverType,                 // Create a device using the hardware graphics driver.
    0,                          // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
    creationFlags,              // Set debug and Direct2D compatibility flags.
    featureLevels,              // List of feature levels this app can support.
    ARRAYSIZE(featureLevels),   // Size of the list above.
    D3D11_SDK_VERSION,          // Always set this to D3D11_SDK_VERSION for Windows Runtime apps.
    &device,                    // Returns the Direct3D device created.
    &m_d3dFeatureLevel,         // Returns feature level of device created.
    &context                    // Returns the device immediate context.
);

Hybridgrafik och Media Foundation

Användning av Media Foundation i hybridsystem kan orsaka problem där video inte renderas eller videostruktur är skadade eftersom Media Foundation är standard för ett systembeteende. I vissa scenarier krävs det att du skapar en separat ID3D11Enhet för att stödja flera trådar och rätt skapandeflaggor anges.

När du initierar ID3D11Enhet måste D3D11_CREATE_DEVICE_VIDEO_SUPPORT flagga definieras som en del av D3D11_CREATE_DEVICE_FLAG. När enheten och kontexten har skapats anropar du SetMultithreadProtected för att aktivera multitrådning. Om du vill associera enheten med IMFDXGIDeviceManager använder du funktionen IMFDXGIDeviceManager::ResetDevice .

Kod för att associera en ID3D11Enhet med IMFDXGIDeviceManager:

// create dx device for media pipeline
winrt::com_ptr<ID3D11Device> spMediaDevice;

// See above. Also make sure to enable the following flags on the D3D11 device:
//   * D3D11_CREATE_DEVICE_VIDEO_SUPPORT
//   * D3D11_CREATE_DEVICE_BGRA_SUPPORT
if (FAILED(CreateMediaDevice(spAdapter.get(), &spMediaDevice)))
    return;                                                     

// Turn multithreading on 
winrt::com_ptr<ID3D10Multithread> spMultithread;
if (spContext.try_as(spMultithread))
{
    spMultithread->SetMultithreadProtected(TRUE);
}

// lock the shared dxgi device manager
// call MFUnlockDXGIDeviceManager when no longer needed
UINT uiResetToken;
winrt::com_ptr<IMFDXGIDeviceManager> spDeviceManager;
hr = MFLockDXGIDeviceManager(&uiResetToken, spDeviceManager.put());
if (FAILED(hr))
    return hr;
    
// associate the device with the manager
hr = spDeviceManager->ResetDevice(spMediaDevice.get(), uiResetToken);
if (FAILED(hr))
    return hr;

Se även