Dela via


Grafikbindning

För att kunna använda Azure Remote Rendering i ett anpassat program måste det integreras i programmets återgivningspipeline. Den här integreringen är grafikbindningens ansvar.

När den har konfigurerats ger grafikbindningen åtkomst till olika funktioner som påverkar den renderade bilden. Dessa funktioner kan delas in i två kategorier: allmänna funktioner som alltid är tillgängliga och specifika funktioner som endast är relevanta för den valda Microsoft.Azure.RemoteRendering.GraphicsApiType.

Grafikbindning i Unity

I Unity hanteras hela bindningen av den RemoteUnityClientInit struct som skickas till RemoteManagerUnity.InitializeManager. Om du vill ange grafikläget GraphicsApiType måste fältet anges till den valda bindningen. Fältet fylls i automatiskt beroende på om en XRDevice finns. Beteendet kan åsidosättas manuellt med följande beteenden:

Den enda andra relevanta delen för Unity är att komma åt den grundläggande bindningen. Alla andra avsnitt nedan kan hoppas över.

Konfiguration av grafikbindning i anpassade program

Välj en grafikbindning genom att utföra följande två steg: Först måste grafikbindningen initieras statiskt när programmet initieras:

RemoteRenderingInitialization managerInit = new RemoteRenderingInitialization();
managerInit.GraphicsApi = GraphicsApiType.OpenXrD3D11;
managerInit.ConnectionType = ConnectionType.General;
managerInit.Right = ///...
RemoteManagerStatic.StartupRemoteRendering(managerInit);
RemoteRenderingInitialization managerInit;
managerInit.GraphicsApi = GraphicsApiType::OpenXrD3D11;
managerInit.ConnectionType = ConnectionType::General;
managerInit.Right = ///...
StartupRemoteRendering(managerInit); // static function in namespace Microsoft::Azure::RemoteRendering

Anropet ovan måste anropas innan andra API:er för fjärrrendering används. På samma sätt bör motsvarande de-init-funktion RemoteManagerStatic.ShutdownRemoteRendering(); anropas när alla andra fjärrrenderingsobjekt redan har förstörts. För WMR StartupRemoteRendering måste också anropas innan något holografiskt API anropas. För OpenXR gäller samma sak för alla OpenXR-relaterade API:er.

Åtkomst till grafikbindning

När en klient har konfigurerats kan du komma åt den grundläggande grafikbindningen med RenderingSession.GraphicsBinding gettern. Till exempel kan den senaste bildrutestatistiken hämtas så här:

RenderingSession currentSession = ...;
if (currentSession.GraphicsBinding != null)
{
    FrameStatistics frameStatistics;
    if (currentSession.GraphicsBinding.GetLastFrameStatistics(out frameStatistics) == Result.Success)
    {
        ...
    }
}
ApiHandle<RenderingSession> currentSession = ...;
if (ApiHandle<GraphicsBinding> binding = currentSession->GetGraphicsBinding())
{
    FrameStatistics frameStatistics;
    if (binding->GetLastFrameStatistics(&frameStatistics) == Result::Success)
    {
        ...
    }
}

Grafiska API:er

Det finns för närvarande tre grafik-API:er som kan väljas, OpenXrD3D11, WmrD3D11 och SimD3D11. En fjärde Headless finns men stöds ännu inte på klientsidan.

OpenXR

GraphicsApiType.OpenXrD3D11 är standardbindningen som ska köras på HoloLens 2. Bindningen GraphicsBindingOpenXrD3d11 skapas. I det här läget skapar Azure Remote Rendering ett OpenXR API-lager för att integrera sig i OpenXR-körningen.

För att få åtkomst till de härledda grafikbindningarna måste basen GraphicsBinding gjutas. Det finns tre saker som måste göras för att använda OpenXR-bindningen:

Paketera anpassad OpenXR-lager-json

Om du vill använda fjärrrendering med OpenXR måste det anpassade OpenXR API-lagret aktiveras. Detta görs genom att anropa StartupRemoteRendering som nämns i föregående avsnitt. Som en förutsättning XrApiLayer_msft_holographic_remoting.json måste dock paketeras med programmet så att det kan läsas in. Detta görs automatiskt om NuGet-paketet "Microsoft.Azure.RemoteRendering.Cpp" läggs till i ett projekt.

Informera fjärrrendering av det använda XR-utrymmet

Detta krävs för att justera fjärr- och lokalt renderat innehåll.

RenderingSession currentSession = ...;
ulong space = ...; // XrSpace cast to ulong
GraphicsBindingOpenXrD3d11 openXrBinding = (currentSession.GraphicsBinding as GraphicsBindingOpenXrD3d11);
if (openXrBinding.UpdateAppSpace(space) == Result.Success)
{
    ...
}
ApiHandle<RenderingSession> currentSession = ...;
XrSpace space = ...;
ApiHandle<GraphicsBindingOpenXrD3d11> openXrBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingOpenXrD3d11>();
#ifdef _M_ARM64
    if (openXrBinding->UpdateAppSpace(reinterpret_cast<uint64_t>(space)) == Result::Success)
#else
    if (openXrBinding->UpdateAppSpace(space) == Result::Success)
#endif
{
    ...
}

Där ovanstående XrSpace är det som används av programmet som definierar det system för världsrymdkoordinat som koordinaterna i API:et uttrycks i.

Rendera fjärrbild (OpenXR)

I början av varje bildruta måste fjärrramen återges i den bakre bufferten. Detta görs genom att anropa BlitRemoteFrame, som fyller både färg- och djupinformation för båda ögonen i det för närvarande bundna återgivningsmålet. Därför är det viktigt att göra det när du har bindt den fullständiga backbufferten som ett återgivningsmål.

Varning

När fjärrbilden har blivit blit i backbuffer ska det lokala innehållet återges med hjälp av en stereorenderingsteknik med ett enda pass, t.ex. med hjälp av SV_RenderTargetArrayIndex. Att använda andra tekniker för stereorendering, till exempel att återge varje öga i ett separat pass, kan leda till större prestandaförsämring eller grafiska artefakter och bör undvikas.

RenderingSession currentSession = ...;
GraphicsBindingOpenXrD3d11 openXrBinding = (currentSession.GraphicsBinding as GraphicsBindingOpenXrD3d11);
openXrBinding.BlitRemoteFrame();
ApiHandle<RenderingSession> currentSession = ...;
ApiHandle<GraphicsBindingOpenXrD3d11> openXrBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingOpenXrD3d11>();
openXrBinding->BlitRemoteFrame();

Windows Mixed Reality

GraphicsApiType.WmrD3D11 är den tidigare använda grafikbindningen som ska köras på HoloLens 2. Bindningen GraphicsBindingWmrD3d11 skapas. I det här läget ansluter Azure Remote Rendering direkt till de holografiska API:erna.

För att få åtkomst till de härledda grafikbindningarna måste basen GraphicsBinding gjutas. Det finns två saker som måste göras för att använda WMR-bindningen:

Informera fjärrrendering av det använda koordinatsystemet

Detta krävs för att justera fjärr- och lokalt renderat innehåll.

RenderingSession currentSession = ...;
IntPtr ptr = ...; // native pointer to ISpatialCoordinateSystem
GraphicsBindingWmrD3d11 wmrBinding = (currentSession.GraphicsBinding as GraphicsBindingWmrD3d11);
if (wmrBinding.UpdateUserCoordinateSystem(ptr) == Result.Success)
{
    ...
}
ApiHandle<RenderingSession> currentSession = ...;
void* ptr = ...; // native pointer to ISpatialCoordinateSystem
ApiHandle<GraphicsBindingWmrD3d11> wmrBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingWmrD3d11>();
if (wmrBinding->UpdateUserCoordinateSystem(ptr) == Result::Success)
{
    ...
}

Där ovanstående ptr måste vara en pekare till ett inbyggt ABI::Windows::Perception::Spatial::ISpatialCoordinateSystem objekt som definierar det system för världsrymdkoordinat som koordinaterna i API:et uttrycks i.

Rendera fjärrbild (WMR)

Samma överväganden som i OpenXR-fallet ovan gäller här. API-anropen ser ut så här:

RenderingSession currentSession = ...;
GraphicsBindingWmrD3d11 wmrBinding = (currentSession.GraphicsBinding as GraphicsBindingWmrD3d11);
wmrBinding.BlitRemoteFrame();
ApiHandle<RenderingSession> currentSession = ...;
ApiHandle<GraphicsBindingWmrD3d11> wmrBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingWmrD3d11>();
wmrBinding->BlitRemoteFrame();

Simulering

GraphicsApiType.SimD3D11 är simuleringsbindningen och om den väljs skapas grafikbindningen GraphicsBindingSimD3d11 . Det här gränssnittet används för att simulera huvudrörelser, till exempel i ett skrivbordsprogram och rendera en monoskopisk bild.

För att implementera simuleringsbindningen är det viktigt att förstå skillnaden mellan den lokala kameran och fjärrramen enligt beskrivningen på kamerasidan .

Två kameror behövs:

  • Lokal kamera: Den här kameran representerar den aktuella kamerapositionen som drivs av programlogik.
  • Proxykamera: Den här kameran matchar den aktuella fjärrramen som skickades av servern. Eftersom det finns en tidsfördröjning mellan klienten som begär en ram och dess ankomst ligger fjärrramen alltid lite bakom den lokala kamerans rörelse.

Den grundläggande metoden här är att både fjärrbilden och det lokala innehållet återges till ett mål utanför skärmen med hjälp av proxykameran. Proxyavbildningen återskapas sedan till det lokala kamerautrymmet, vilket förklaras ytterligare i ett senare skede.

GraphicsApiType.SimD3D11 stöder också stereoskopisk återgivning, som måste aktiveras under installationsanropet InitSimulation nedan. Installationen är lite mer involverad och fungerar på följande sätt:

Skapa proxyåtergivningsmål

Fjärr- och lokalt innehåll måste återges till ett mål för offscreen-färg/djupåtergivning som kallas "proxy" med hjälp av proxykameradata som tillhandahålls av GraphicsBindingSimD3d11.Update funktionen.

Proxyn måste matcha upplösningen för backbufferten och ska vara i formatet DXGI_FORMAT_R8G8B8A8_UNORM eller DXGI_FORMAT_B8G8R8A8_UNORM . När det gäller stereoskopisk återgivning måste både färgproxyns struktur och, om djup används, djupproxyns struktur ha två matrisskikt i stället för ett. När en session är klar GraphicsBindingSimD3d11.InitSimulation måste anropas innan du ansluter till den:

RenderingSession currentSession = ...;
IntPtr d3dDevice = ...; // native pointer to ID3D11Device
IntPtr color = ...; // native pointer to ID3D11Texture2D
IntPtr depth = ...; // native pointer to ID3D11Texture2D
float refreshRate = 60.0f; // Monitor refresh rate up to 60hz.
bool flipBlitRemoteFrameTextureVertically = false;
bool flipReprojectTextureVertically = false;
bool stereoscopicRendering = false;
GraphicsBindingSimD3d11 simBinding = (currentSession.GraphicsBinding as GraphicsBindingSimD3d11);
simBinding.InitSimulation(d3dDevice, depth, color, refreshRate, flipBlitRemoteFrameTextureVertically, flipReprojectTextureVertically, stereoscopicRendering);
ApiHandle<RenderingSession> currentSession = ...;
void* d3dDevice = ...; // native pointer to ID3D11Device
void* color = ...; // native pointer to ID3D11Texture2D
void* depth = ...; // native pointer to ID3D11Texture2D
float refreshRate = 60.0f; // Monitor refresh rate up to 60hz.
bool flipBlitRemoteFrameTextureVertically = false;
bool flipReprojectTextureVertically = false;
bool stereoscopicRendering = false;
ApiHandle<GraphicsBindingSimD3d11> simBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingSimD3d11>();
simBinding->InitSimulation(d3dDevice, depth, color, refreshRate, flipBlitRemoteFrameTextureVertically, flipReprojectTextureVertically, stereoscopicRendering);

Funktionen init måste förses med pekare till den interna d3d-enheten samt till färg och djupstruktur för proxyåtergivningsmålet. När den har initierats RenderingSession.ConnectAsync och Disconnect kan anropas flera gånger, men när du växlar till en annan session, GraphicsBindingSimD3d11.DeinitSimulation måste anropas först på den gamla sessionen innan GraphicsBindingSimD3d11.InitSimulation kan anropas på en annan session.

Återgivningsloopuppdatering

Återgivningsloopuppdateringen består av flera steg:

  1. Varje bildruta, innan någon rendering sker, GraphicsBindingSimD3d11.Update anropas med den aktuella kameratransformeringen som skickas över till servern som ska renderas. Samtidigt ska den returnerade proxytransformen tillämpas på proxykameran för att återges till proxyåtergivningsmålet. Om den returnerade proxyuppdateringen SimulationUpdate.frameId är null finns det inga fjärrdata ännu. I det här fallet, i stället för att återges till proxyåtergivningsmålet, ska allt lokalt innehåll återges till den bakre bufferten direkt med hjälp av aktuella kameradata och de kommande två stegen hoppas över.
  2. Programmet bör nu binda proxyåtergivningsmålet och anropa GraphicsBindingSimD3d11.BlitRemoteFrameToProxy. Detta fyller fjärrfärgen och djupinformationen i proxyåtergivningsmålet. Allt lokalt innehåll kan nu återges på proxyn med hjälp av proxykameratransformen.
  3. Därefter måste backbufferten bindas som ett återgivningsmål och GraphicsBindingSimD3d11.ReprojectProxy anropas vid vilken punkt backbufferten kan visas.
RenderingSession currentSession = ...;
GraphicsBindingSimD3d11 simBinding = (currentSession.GraphicsBinding as GraphicsBindingSimD3d11);
SimulationUpdateParameters updateParameters = new SimulationUpdateParameters();
// Fill out camera data with current camera data
// (see "Simulation Update structures" section below)
...
SimulationUpdateResult updateResult = new SimulationUpdateResult();
simBinding.Update(updateParameters, out updateResult);
// Is the frame data valid?
if (updateResult.FrameId != 0)
{
    // Bind proxy render target
    simBinding.BlitRemoteFrameToProxy();
    // Use proxy camera data to render local content
    ...
    // Bind back buffer
    simBinding.ReprojectProxy();
}
else
{
    // Bind back buffer
    // Use current camera data to render local content
    ...
}
ApiHandle<RenderingSession> currentSession;
ApiHandle<GraphicsBindingSimD3d11> simBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingSimD3d11>();

SimulationUpdateParameters updateParameters;
// Fill out camera data with current camera data
// (see "Simulation Update structures" section below)
...
SimulationUpdateResult updateResult;
simBinding->Update(updateParameters, &updateResult);
// Is the frame data valid?
if (updateResult.FrameId != 0)
{
    // Bind proxy render target
    simBinding->BlitRemoteFrameToProxy();
    // Use proxy camera data to render local content
    ...
    // Bind back buffer
    simBinding->ReprojectProxy();
}
else
{
    // Bind back buffer
    // Use current camera data to render local content
    ...
}

Strukturer för simuleringsuppdatering

Varje ram, återgivningsloopuppdateringen från föregående avsnitt kräver att du anger ett intervall med kameraparametrar som motsvarar den lokala kameran och returnerar en uppsättning kameraparametrar som motsvarar nästa tillgängliga bildrutas kamera. Dessa två uppsättningar samlas in i SimulationUpdateParameters respektive SimulationUpdateResult strukturer:

public struct SimulationUpdateParameters
{
    public int FrameId;
    public StereoMatrix4x4 ViewTransform;
    public StereoCameraFov FieldOfView;
};

public struct SimulationUpdateResult
{
    public int FrameId;
    public float NearPlaneDistance;
    public float FarPlaneDistance;
    public StereoMatrix4x4 ViewTransform;
    public StereoCameraFov FieldOfView;
};

Strukturens medlemmar har följande betydelse:

Medlem beskrivning
FrameId Kontinuerlig bildruteidentifierare. Krävs för SimulationUpdateParameters-indata och måste kontinuerligt ökas för varje ny bildruta. Kommer att vara 0 i SimulationUpdateResult om inga ramdata är tillgängliga ännu.
ViewTransform Vänster-höger-stereopar i ramens transformeringsmatriser för kameravyn. För monoskopisk återgivning är endast Left medlemmen giltig.
FieldOfView Vänster-höger-stereopar i bildrutekamerans synfält i OpenXR-synfältet. För monoskopisk återgivning är endast Left medlemmen giltig.
NearPlaneDistance avstånd nära planet som används för projektionsmatrisen för den aktuella fjärrramen.
FarPlaneDistance fjärranslutet avstånd som används för projektionsmatrisen för den aktuella fjärrramen.

Stereoparen ViewTransform och FieldOfView tillåt inställning av båda ögonkameravärdena om stereoskopisk återgivning är aktiverad. Annars Right ignoreras medlemmarna. Som du ser skickas endast kamerans transformering som vanliga 4x4-transformeringsmatriser medan inga projektionsmatriser anges. De faktiska matriserna beräknas av Azure Remote Rendering internt med hjälp av de angivna fälten och den aktuella nära-plan och fjärran-plan som angetts i Kamera Inställningar API.

Eftersom du kan ändra det nära planet och det fjärranslutna planet på Kamera Inställningar under körningen som önskat och tjänsten tillämpar dessa inställningar asynkront, bär varje SimulationUpdateResult också den specifika nära-plan och fjärran-plan som används under återgivningen av motsvarande ram. Du kan använda dessa planvärden för att anpassa dina projektionsmatriser för återgivning av lokala objekt så att de matchar fjärrramsåtergivningen.

Även om simuleringsuppdateringsanropet kräver visningsfältet i OpenXR-konventionen kan du av standardiserings- och algoritmiska säkerhetsskäl använda konverteringsfunktionerna som illustreras i följande exempel på strukturpopulation:

public SimulationUpdateParameters CreateSimulationUpdateParameters(int frameId, Matrix4x4 viewTransform, Matrix4x4 projectionMatrix)
{
    SimulationUpdateParameters parameters = default;
    parameters.FrameId = frameId;
    parameters.ViewTransform.Left = viewTransform;
    if (parameters.FieldOfView.Left.FromProjectionMatrix(projectionMatrix) != Result.Success)
    {
        // Invalid projection matrix
        throw new ArgumentException("Invalid projection settings");
    }
    return parameters;
}

public void GetCameraSettingsFromSimulationUpdateResult(SimulationUpdateResult result, out Matrix4x4 projectionMatrix, out Matrix4x4 viewTransform, out int frameId)
{
    projectionMatrix = default;
    viewTransform = default;
    frameId = 0;

    if (result.FrameId == 0)
    {
        // Invalid frame data
        return;
    }

    // Use the screenspace depth convention you expect for your projection matrix locally
    if (result.FieldOfView.Left.ToProjectionMatrix(result.NearPlaneDistance, result.FarPlaneDistance, DepthConvention.ZeroToOne, out projectionMatrix) != Result.Success)
    {
        // Invalid field-of-view
        return;
    }
    viewTransform = result.ViewTransform.Left;
    frameId = result.FrameId;
}
SimulationUpdateParameters CreateSimulationUpdateParameters(uint32_t frameId, Matrix4x4 viewTransform, Matrix4x4 projectionMatrix)
{
    SimulationUpdateParameters parameters;
    parameters.FrameId = frameId;
    parameters.ViewTransform.Left = viewTransform;
    if (FovFromProjectionMatrix(projectionMatrix, parameters.FieldOfView.Left) != Result::Success)
    {
        // Invalid projection matrix
        return {};
    }
    return parameters;
}

void GetCameraSettingsFromSimulationUpdateResult(const SimulationUpdateResult& result, Matrix4x4& projectionMatrix, Matrix4x4& viewTransform, uint32_t& frameId)
{
    if (result.FrameId == 0)
    {
        // Invalid frame data
        return;
    }

    // Use the screenspace depth convention you expect for your projection matrix locally
    if (FovToProjectionMatrix(result.FieldOfView.Left, result.NearPlaneDistance, result.FarPlaneDistance, DepthConvention::ZeroToOne, projectionMatrix) != Result::Success)
    {
        // Invalid field-of-view
        return;
    }
    viewTransform = result.ViewTransform.Left;
    frameId = result.FrameId;
}

Med de här konverteringsfunktionerna kan du snabbt växla mellan specifikationen för visningsfältet och en vanlig 4x4-perspektivprojektionsmatris, beroende på dina behov av lokal återgivning. Dessa konverteringsfunktioner innehåller verifieringslogik och returnerar fel, utan att ange ett giltigt resultat, om indataprojektionsmatriserna eller indatafälten är ogiltiga.

API-dokumentation

Nästa steg