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:
- HoloLens 2: OpenXR - eller Windows Mixed Reality-grafikbindningen används beroende på det aktiva Unity XR-plugin-programmet.
- Platt UWP-skrivbordsapp: Simulering används alltid.
- Unity-redigerare: Simulering används alltid såvida inte ett WMR VR-headset är anslutet i vilket fall ARR inaktiveras för att tillåta felsökning av icke-ARR-relaterade delar av programmet. Se även holografisk fjärrkommunikation.
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:
- 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 proxyuppdateringenSimulationUpdate.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. - 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. - 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
- C# RemoteManagerStatic.StartupRemoteRendering()
- C# GraphicsBinding-klass
- C# GraphicsBindingWmrD3d11-klass
- C# GraphicsBindingSimD3d11-klass
- C++ RemoteRenderingInitialization struct
- C++ GraphicsBinding-klass
- C++ GraphicsBindingWmrD3d11-klass
- C++ GraphicsBindingSimD3d11-klass