Sdílet prostřednictvím


Grafická vazba

Aby bylo možné používat Azure Remote Rendering ve vlastní aplikaci, je potřeba ji integrovat do kanálu vykreslování aplikace. Tato integrace je zodpovědností grafické vazby.

Po nastavení poskytuje grafická vazba přístup k různým funkcím, které ovlivňují vykreslený obrázek. Tyto funkce lze rozdělit do dvou kategorií: obecné funkce, které jsou vždy dostupné a specifické funkce, které jsou relevantní pouze pro vybraný Microsoft.Azure.RemoteRendering.GraphicsApiType.

Grafická vazba v Unity

V Unity je celá vazba zpracována strukturou RemoteUnityClientInit předanou do RemoteManagerUnity.InitializeManager. Pokud chcete nastavit grafický režim, GraphicsApiType musí být pole nastaveno na zvolenou vazbu. Pole se automaticky vyplní v závislosti na tom, jestli je k dispozici XRDevice. Toto chování lze ručně přepsat následujícím chováním:

Jedinou další relevantní částí Unity je přístup k základní vazbě, všechny ostatní části níže je možné přeskočit.

Nastavení grafické vazby ve vlastních aplikacích

Chcete-li vybrat grafickou vazbu, proveďte následující dva kroky: Nejprve musí být grafická vazba staticky inicializována při inicializaci programu:

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

Výše uvedená volání musí být volána před přístupem k jiným rozhraním API vzdáleného vykreslování. Podobně by měla být odpovídající funkce RemoteManagerStatic.ShutdownRemoteRendering(); de-init volána poté, co jsou všechny ostatní objekty vzdáleného vykreslování již zničeny. Aby bylo možné WMR StartupRemoteRendering volat také před voláním libovolného holografického rozhraní API. Pro OpenXR platí totéž pro všechna rozhraní API související s OpenXR.

Přístup k grafické vazbě

Po nastavení klienta je možné získat přístup k základní grafické vazbě pomocí getteru RenderingSession.GraphicsBinding . Například poslední statistiku rámce je možné načíst takto:

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)
    {
        ...
    }
}

Grafická rozhraní API

Aktuálně existují tři grafická rozhraní API, která lze vybrat, OpenXrD3D11WmrD3D11 a SimD3D11. Čtvrtý z nich Headless existuje, ale zatím není na straně klienta podporovaný.

OpenXR

GraphicsApiType.OpenXrD3D11 je výchozí vazba pro spuštění na HoloLens 2. GraphicsBindingOpenXrD3d11 Vytvoří vazbu. V tomto režimu Azure Remote Rendering vytvoří vrstvu rozhraní API OpenXR, která se integruje do modulu runtime OpenXR.

Pro přístup k odvozené grafické vazbě musí být základ GraphicsBinding přetypován. Aby bylo možné použít vazbu OpenXR, musíte udělat tři věci:

Zabalení vlastního souboru JSON vrstvy OpenXR

Pokud chcete používat vzdálené vykreslování s OpenXR, musí být aktivována vlastní vrstva rozhraní API OpenXR. To se provádí voláním StartupRemoteRendering uvedeným v předchozí části. Jako předpoklad však XrApiLayer_msft_holographic_remoting.json musí být zabalena s aplikací, aby ji bylo možné načíst. To se provádí automaticky, pokud se do projektu přidá balíček NuGet Microsoft.Azure.RemoteRendering.Cpp .

Informování vzdáleného vykreslování využitého prostoru XR

To je potřeba k zarovnání vzdáleného a místně vykresleného obsahu.

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
{
    ...
}

Kde výše uvedený XrSpace je ten, který používá aplikace, která definuje světový souřadnicový systém prostorů, ve kterém jsou souřadnice v rozhraní API vyjádřeny.

Vykreslení vzdáleného obrázku (OpenXR)

Na začátku každého rámce je potřeba vzdálený rámec vykreslit do zadní vyrovnávací paměti. To se provádí voláním BlitRemoteFrame, která vyplní informace o barvě i hloubkách obou očí do aktuálně vázaného cíle vykreslení. Proto je důležité to udělat po vytvoření úplného vyrovnávací paměti zpět jako cíle vykreslení.

Upozorňující

Po rozsvícení vzdáleného obrázku do backbufferu by se místní obsah měl vykreslit pomocí techniky stereo vykreslování s jedním průchodem, například pomocí SV_RenderTargetArrayIndex. Použití jiných stereo vykreslovacích technik, jako je například vykreslení každého oka v samostatném průchodu, může vést ke snížení výkonu nebo grafickým artefaktům a mělo by se jim vyhnout.

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 je dříve použitá grafická vazba ke spuštění na HoloLens 2. GraphicsBindingWmrD3d11 Vytvoří vazbu. V tomto režimu se Azure Remote Rendering připojí přímo k holografickým rozhraním API.

Pro přístup k odvozené grafické vazbě musí být základ GraphicsBinding přetypován. Aby bylo možné použít vazbu WMR, je potřeba udělat dvě věci:

Informování vzdáleného vykreslování o použitém souřadnicovém systému

To je potřeba k zarovnání vzdáleného a místně vykresleného obsahu.

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)
{
    ...
}

Kde výše uvedené ptr musí být ukazatel na nativní ABI::Windows::Perception::Spatial::ISpatialCoordinateSystem objekt, který definuje světový souřadnicový systém prostorů, ve kterém jsou souřadnice v rozhraní API vyjádřeny.

Vykreslení vzdáleného obrázku (WMR)

Stejné aspekty jako v případě OpenXR výše platí tady. Volání rozhraní API vypadají takto:

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

Simulace

GraphicsApiType.SimD3D11 je simulační vazba a pokud je vybrána, vytvoří grafickou GraphicsBindingSimD3d11 vazbu. Toto rozhraní se používá k simulaci pohybu hlavy, například v desktopové aplikaci a vykreslení monoskopického obrázku.

Pokud chcete implementovat vazbu simulace, je důležité pochopit rozdíl mezi místním fotoaparátem a vzdáleným snímkem, jak je popsáno na stránce kamery .

Jsou potřeba dvě kamery:

  • Místní kamera: Tato kamera představuje aktuální pozici kamery řízenou logikou aplikace.
  • Proxy kamera: Tato kamera odpovídá aktuálnímu vzdálenému snímku odeslanému serverem. Vzhledem k tomu, že mezi klientem žádajícím o rámec a jeho příchodem dochází k časovému zpoždění, vzdálený rámec je vždy trochu za pohybem místní kamery.

Základním přístupem je, že vzdálený i místní obsah se vykreslují do cíle mimo obrazovku pomocí proxy kamery. Obrázek proxy serveru se pak přeprojektuje do prostoru místní kamery, který je podrobněji vysvětlen v pozdější fázi reprojektování.

GraphicsApiType.SimD3D11 také podporuje stereoskopické vykreslování, které je potřeba povolit během InitSimulation následujícího volání nastavení. Nastavení je trochu více zapojené a funguje takto:

Vytvoření cíle vykreslení proxy serveru

Vzdálený a místní obsah se musí vykreslit do cíle vykreslení mimo obrazovku nebo hloubky s názvem "proxy" pomocí dat z fotoaparátu proxy, která GraphicsBindingSimD3d11.Update funkce poskytuje.

Proxy server musí odpovídat rozlišení vyrovnávací paměti back a musí být ve formátu DXGI_FORMAT_R8G8B8A8_UNORM nebo DXGI_FORMAT_B8G8R8A8_UNORM . V případě stereoskopického vykreslování musí mít textura barevného proxy serveru a pokud se používá hloubka, musí mít textura proxy hloubky dvě vrstvy pole místo jedné. Jakmile je relace připravená, GraphicsBindingSimD3d11.InitSimulation je potřeba před připojením k ní volat:

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);

Funkce inicializační funkce musí být k dispozici s ukazateli na nativní zařízení d3d a také na barvu a hloubkovou texturu cíle vykreslování proxy serveru. Po inicializaci RenderingSession.ConnectAsync a Disconnect může být volána vícekrát, GraphicsBindingSimD3d11.DeinitSimulation ale při přepnutí na jinou relaci je potřeba nejprve volat ve staré relaci, aby GraphicsBindingSimD3d11.InitSimulation bylo možné volat v jiné relaci.

Aktualizace smyčky vykreslování

Aktualizace smyčky vykreslování se skládá z několika kroků:

  1. Každý snímek před provedením vykreslování GraphicsBindingSimD3d11.Update se volá s aktuální transformací fotoaparátu, která se odešle na server, který se má vykreslit. Ve stejnou dobu by se vrácená transformace proxy serveru měla použít na proxy kameru, aby se vykreslovala do cíle vykreslování proxy serveru. Pokud vrácená aktualizace SimulationUpdate.frameId proxy serveru má hodnotu null, ještě neexistují žádná vzdálená data. V takovém případě by se místo vykreslení do cíle vykreslování proxy serveru měl vykreslit veškerý místní obsah do vyrovnávací paměti zpět přímo pomocí aktuálních dat fotoaparátu a další dva kroky se přeskočí.
  2. Aplikace by teď měla svázat cíl vykreslení proxy serveru a volat GraphicsBindingSimD3d11.BlitRemoteFrameToProxy. Tím se vyplní vzdálená barva a podrobné informace do cíle vykreslení proxy serveru. Jakýkoli místní obsah se teď dá vykreslit na proxy server pomocí transformace proxy kamery.
  3. V dalším kroku musí být vyrovnávací paměť zpět svázána jako cíl vykreslení a GraphicsBindingSimD3d11.ReprojectProxy volána v jakém okamžiku může být vrácena zpět vyrovnávací paměť.
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
    ...
}

Struktury aktualizace simulace

Každý snímek, aktualizace smyčky vykreslení z předchozí části vyžaduje, abyste zadali rozsah parametrů kamery odpovídající místní kameře a vrátí sadu parametrů kamery, které odpovídají další dostupné kameře. Tyto dvě sady jsou zachyceny v objektech SimulationUpdateParameters a SimulationUpdateResult strukturách:

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;
};

Členy struktury mají následující význam:

Člen Popis
FrameId Identifikátor spojitého rámce. Nezbytné pro vstup SimulationUpdateParameters a pro každý nový rámec je potřeba průběžně inkrementovat. Pokud ještě nejsou k dispozici žádná data rámce, bude v Aplikaci SimulationUpdateResult hodnota 0.
ViewTransform Transformační matice zobrazení kamery zleva doprava. Pro monoskopické vykreslování je platný pouze Left člen.
Fieldofview Stereofonní pár zleva doprava v polích rámeček kamery v poli OpenXR zobrazení konvence zobrazení. Pro monoskopické vykreslování je platný pouze Left člen.
PoblížPlaneDistance vzdálenost téměř roviny použitá pro projekční matici aktuálního vzdáleného rámce.
FarPlaneDistance vzdálenost daleko roviny použitá pro projekční matici aktuálního vzdáleného rámce.

Stereo-páry ViewTransform a FieldOfView umožňují nastavit obě hodnoty oční kamery v případě, že je povoleno stereoskopické vykreslování. Right V opačném případě budou členové ignorováni. Jak vidíte, transformace kamery se předává jako jednoduché matice transformace 4x4, zatímco nejsou zadány žádné matice projekce. Skutečné matice se počítají interně službou Azure Remote Rendering pomocí zadaných polí zobrazení a aktuální téměř roviny a vzdálené roviny nastavené v rozhraní API Kamera Nastavení.

Vzhledem k tomu, že při požadovaném běhu můžete změnit téměř rovinu a daleko rovinu na Kamera Nastavení a služba tato nastavení použije asynchronně, každá simulaceUpdateResult také nese konkrétní blízko rovinu a daleko rovinu použitou při vykreslování odpovídajícího rámce. Tyto hodnoty roviny můžete použít k přizpůsobení matic projekce pro vykreslení místních objektů tak, aby odpovídaly vykreslování vzdáleného rámce.

A konečně, zatímco volání simulace update vyžaduje pole zobrazení v rámci konvence OpenXR z důvodu standardizace a zabezpečení algoritmů, můžete využít konverzní funkce znázorněné v následujících příkladech základního souboru struktury:

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;
}

Tyto převodní funkce umožňují rychlé přepínání mezi specifikací zobrazení pole a prostým 4x4 perspektivním zobrazením v závislosti na vašich potřebách pro místní vykreslování. Tyto funkce převodu obsahují logiku ověření a vrátí chyby bez nastavení platného výsledku v případě, že vstupní matice projekce nebo vstupní pole zobrazení nejsou platná.

Dokumentace k rozhraní API

Další kroky