البرنامج التعليمي: دمج Remote Rendering في HoloLens Holographic App

ستتعلم في هذا البرنامج التعليمي ما يأتي:

  • استخدام Visual Studio لإنشاء تطبيق Holographic يمكن نشره على HoloLens
  • إضافة قصاصات التعليمات البرمجية الضرورية وإعدادات المشروع لدمج العرض المحلي مع المحتوى المعروض عن بُعد

يركز هذا البرنامج التعليمي على إضافة وحدات البت الضرورية إلى عينة Holographic App الأصلية لدمج العرض المحلي باستخدام Azure Remote Rendering. النوع الوحيد من ملحوظات الحالة في هذا التطبيق هو من خلال لوحة إخراج تصحيح الأخطاء داخل Visual Studio، لذا يوصى بتشغيل العينة من داخل Visual Studio. يكون إضافة ملحوظات مناسبة داخل التطبيق خارج نطاق هذه العينة، لأن بناء لوحة نصية ديناميكية من البداية ينطوي على الكثير من التعليمات البرمجية. نقطة البداية الجيدة هي فئة StatusDisplay، التي تُعد جزءاً من مشروع عينة Remoting Player على GitHub. تستخدم النسخة التي تم فحصها سابقاً من هذا البرنامج التعليمي في الواقع نسخة محلية من تلك الفئة.

تلميح

يحتوي مستودع عينات ARR على نتائج هذا البرنامج التعليمي كمشروع Visual Studio الجاهز للاستخدام. كما يتم إثراؤه بالإبلاغ الصحيح عن الخطأ والحالة من خلال StatusDisplay فئة واجهة المستخدم. ويحدد #ifdef USE_REMOTE_RENDERING / #endif نطاق جميع الإضافات المحددة في ARR داخل البرنامج التعليمي، لذلك فمن السهل تحديد إضافات Remote Rendering.

المتطلبات الأساسية

ستحتاج في هذا البرنامج التعليمي ما يأتي:

  • معلومات حسابك (معرّف الحساب، مفتاح الحساب، مجال الحساب، معرّف الاشتراك). إذا لم يكن لديك حساب، ⁦⁩فأنشئ حساباً⁦⁩.
  • Windows SDK 10.0.18362.0 (تحميل).
  • أحدث إصدار من Visual Studio 2022 (تنزيل).
  • أدوات Visual Studio الخاصة بـ Mixed Reality. على وجه التحديد، عمليات تثبيت حمل العمل التالية إلزامية:
    • تطوير سطح المكتب باستخدام C++‎
    • تطوير Universal Windows Platform (UWP)
  • قوالب تطبيق Windows Mixed Reality لبرنامج Visual Studio (تنزيل).

إنشاء عينة جديدة لتطبيق Holographic

ننشئ كخطوة أولى عينة المخزون التي هي أساس تكامل Remote Rendering. افتح Visual Studio وحدد "إنشاء مشروع جديد" والبحث عن "تطبيق Holographic DirectX 11 (Universal Windows) (C ++/WinRT)"

Create new project

اكتب اسم مشروع من اختيارك، واختر مساراً وحدد الزر "Create". في المشروع الجديد، قم بتبديل التكوين إلى "Debug / ARM64". يجب أن تكون قادراً الآن على تجميعه ونشره إلى جهاز HoloLens 2 متصل. إذا قمت بتشغيله على HoloLens، فيجب أن تشاهد مكعباً دواراً أمامك.

إضافة تبعيات Remote Rendering من خلال NuGet

الخطوة الأولى في إضافة قدرات Remote Rendering هي إضافة تبعيات من جانب العميل. وتكون التبعيات ذات الصلة متوفرة كحزمة NuGet. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق المشروع وحدد "Manage NuGet Packages..." من قائمة السياق.

في مربع حوار المطالبة استعرض للحصول على حزمة NuGet المسماة "Microsoft.Azure.RemoteRendering.Cpp":

Browse for NuGet package

وأضفها إلى المشروع من خلال تحديد الحزمة ثم الضغط على زر "Install".

تضيف حزمة NuGet تبعيات Remote Rendering إلى المشروع. على وجه التحديد:

  • الارتباط بمكتبة العميل (RemoteRenderingClient.lib).
  • إعداد تبعيات ‎.dll.
  • تعيين المسار الصحيح إلى دليل التضمين.

إعداد المشروع

نحتاج إلى إجراء تغييرات صغيرة على المشروع الحالي. هذه التغييرات دقيقة، ولكن لن تعمل من دونها Remote Rendering.

تمكين الحماية متعددة السلاسل على جهاز DirectX

DirectX11يجب تمكين الحماية متعددة السلاسل للجهاز. لتغيير ذلك، افتح ملف DeviceResources.cpp في المجلد "Common"، وأدخل التعليمة البرمجية الآتية في نهاية الدالة DeviceResources::CreateDeviceResources():

// Enable multi thread protection as now multiple threads use the immediate context.
Microsoft::WRL::ComPtr<ID3D11Multithread> contextMultithread;
if (context.As(&contextMultithread) == S_OK)
{
    contextMultithread->SetMultithreadProtected(true);
}

تمكين قدرات الشبكة في بيان التطبيق

يجب تمكين قدرات الشبكة صراحةً للتطبيق المنشور. من دون تكوين ذلك، ستؤدي استعلامات الاتصال إلى المهلات في نهاية المطاف. ولتمكينها انقر نقراً مزدوجاً فوق العنصر package.appxmanifest في مستكشف الحلول. في واجهة المستخدم الآتية، انتقل إلى علامة التبويب "Capabilities" ثم حدد:

  • "Internet (Client & Server)"
  • "Internet (Client)"

Network capabilities

دمج Remote Rendering

الآن بعد أن تم إعداد المشروع، يمكننا أن نبدأ مع التعليمة البرمجية. نقطة الإدخال الجيدة في التطبيق هي الفئة HolographicAppMain (ملف HolographicAppMain.h/cpp) لأنه يحتوي على جميع الروابط الضرورية للتهيئة وإزالة التهيئة والعرض.

يتضمن

نبدأ بإضافة التضمينات الضرورية. أضف التضمين الآتي إلى ملف HolographicAppMain.h:

#include <AzureRemoteRendering.h>

... وتوجيهات include الإضافية هذه إلى ملف HolographicAppMain.cpp:

#include <AzureRemoteRendering.inl>
#include <RemoteRenderingExtensions.h>
#include <windows.perception.spatial.h>

لتبسيط التعليمات البرمجية، نعرف اختصار مساحة الاسم الآتي في الجزء العلوي من الملف HolographicAppMain.h، بعد توجيهات include:

namespace RR = Microsoft::Azure::RemoteRendering;

هذا الاختصار مفيد لذلك ليس علينا كتابة مساحة الاسم الكاملة في كل مكان ولكن لا يزال بإمكاننا التعرف على بُنى البيانات الخاصة بـ ARR. ويمكننا أيضاً بالطبع استخدام التوجيه using namespace....

تهيئة Remote Rendering

الاحتفاظ ببعض العناصر للجلسة وما إلى ذلك خلال مدة بقاء التطبيق. تتزامن مدة البقاء مع مدة بقاء عنصر التطبيق HolographicAppMain، لذلك نضيف العناصر كأعضاء إلى الفئة HolographicAppMain الخطوة التالية هي إضافة أعضاء الفئة الآتية في ملف HolographicAppMain.h:

class HolographicAppMain
{
    ...
    // members:
    std::string m_sessionOverride;                // if we have a valid session ID, we specify it here. Otherwise a new one is created
    RR::ApiHandle<RR::RemoteRenderingClient> m_client;  // the client instance
    RR::ApiHandle<RR::RenderingSession> m_session;    // the current remote rendering session
    RR::ApiHandle<RR::RenderingConnection> m_api;       // the API instance, that is used to perform all the actions. This is just a shortcut to m_session->Connection()
    RR::ApiHandle<RR::GraphicsBindingWmrD3d11> m_graphicsBinding; // the graphics binding instance
}

تُعد الدالة الإنشائية للفئة HolographicAppMain مكاناً جيداً للتنفيذ الفعلي. علينا تنفيذ ثلاثة أنواع من التهيئة هناك:

  1. تهيئة نظام Remote Rendering لمرة واحدة
  2. إنشاء عميل (مصادقة)
  3. إنشاء جلسة

نفعل كل ذلك بشكل متسلسل في الدالة الإنشائية. لكن، في حالات الاستخدام الحقيقي قد يكون من المناسب القيام بهذه الخطوات بشكل منفصل.

أضف التعليمة البرمجية الآتية إلى بداية نص الدالة الإنشائية في ملف HolographicAppMain.cpp:

HolographicAppMain::HolographicAppMain(std::shared_ptr<DX::DeviceResources> const& deviceResources) :
    m_deviceResources(deviceResources)
{
    // 1. One time initialization
    {
        RR::RemoteRenderingInitialization clientInit;
        clientInit.ConnectionType = RR::ConnectionType::General;
        clientInit.GraphicsApi = RR::GraphicsApiType::WmrD3D11;
        clientInit.ToolId = "<sample name goes here>"; // <put your sample name here>
        clientInit.UnitsPerMeter = 1.0f;
        clientInit.Forward = RR::Axis::NegativeZ;
        clientInit.Right = RR::Axis::X;
        clientInit.Up = RR::Axis::Y;
        if (RR::StartupRemoteRendering(clientInit) != RR::Result::Success)
        {
            // something fundamental went wrong with the initialization
            throw std::exception("Failed to start remote rendering. Invalid client init data.");
        }
    }


    // 2. Create Client
    {
        // Users need to fill out the following with their account data and model
        RR::SessionConfiguration init;
        init.AccountId = "00000000-0000-0000-0000-000000000000";
        init.AccountKey = "<account key>";
        init.RemoteRenderingDomain = "westus2.mixedreality.azure.com"; // <change to the region that the rendering session should be created in>
        init.AccountDomain = "westus2.mixedreality.azure.com"; // <change to the region the account was created in>
        m_modelURI = "builtin://Engine";
        m_sessionOverride = ""; // If there is a valid session ID to re-use, put it here. Otherwise a new one is created
        m_client = RR::ApiHandle(RR::RemoteRenderingClient(init));
    }

    // 3. Open/create rendering session
    {
        auto SessionHandler = [&](RR::Status status, RR::ApiHandle<RR::CreateRenderingSessionResult> result)
        {
            if (status == RR::Status::OK)
            {
                auto ctx = result->GetContext();
                if (ctx.Result == RR::Result::Success)
                {
                    SetNewSession(result->GetSession());
                }
                else
                {
                    SetNewState(AppConnectionStatus::ConnectionFailed, ctx.ErrorMessage.c_str());
                }
            }
            else
            {
                SetNewState(AppConnectionStatus::ConnectionFailed, "failed");
            }
        };

        // If we had an old (valid) session that we can recycle, we call async function m_client->OpenRenderingSessionAsync
        if (!m_sessionOverride.empty())
        {
            m_client->OpenRenderingSessionAsync(m_sessionOverride, SessionHandler);
            SetNewState(AppConnectionStatus::CreatingSession, nullptr);
        }
        else
        {
            // create a new session
            RR::RenderingSessionCreationOptions init;
            init.MaxLeaseInMinutes = 10; // session is leased for 10 minutes
            init.Size = RR::RenderingSessionVmSize::Standard;
            m_client->CreateNewRenderingSessionAsync(init, SessionHandler);
            SetNewState(AppConnectionStatus::CreatingSession, nullptr);
        }
    }

    // Rest of constructor code:
    ...
}

تستدعي التعليمة البرمجية دالتي الأعضاء SetNewSession وSetNewState، التي سنقوم بتنفيذها في الفقرة الآتية مع بقية التعليمة البرمجية لتسلسل الحالات.

لاحظ أن بيانات الاعتماد مرمزة في العينة ويجب ملؤها في مكانها (معرّف الحساب ومفتاح الحساب ومجال الحساب ومجال Remote Rendering).

نلغي التهيئة بشكل متناظر وفي ترتيب عكسي في نهاية نص دالة الإتلاف:

HolographicAppMain::~HolographicAppMain()
{
    // Existing destructor code:
    ...
    
    // Destroy session:
    if (m_session != nullptr)
    {
        m_session->Disconnect();
        m_session = nullptr;
    }

    // Destroy front end:
    m_client = nullptr;

    // One-time de-initialization:
    RR::ShutdownRemoteRendering();
}

تسلسل الحالات

تكون الدوال الأساسية في Remote Rendering لإنشاء جلسة وتحميل نموذج دوال غير متزامنة. لحساب هذا، نحتاج إلى تسلسل حالات بسيط ينتقل أساساً من خلال الحالات الآتية تلقائياً:

التهيئة -> إنشاء الجلسة -> بدء الجلسة -> تحميل النموذج (مع التقدم)

وفقاً لذلك، نضيف قليلاً من معالجة تسلسل الحالات إلى الفئة كخطوة تالية. نعرف تعداد AppConnectionStatus لمختلف الحالات التي يمكن أن يكون تطبيقنا فيها. وهو مماثل لـ RR::ConnectionStatus، ولكنه يحتوي على حالة إضافية لفشل الاتصال.

أضف الأعضاء والدوال الآتية إلى تعريف الفئة:

namespace HolographicApp
{
    // Our application's possible states:
    enum class AppConnectionStatus
    {
        Disconnected,

        CreatingSession,
        StartingSession,
        Connecting,
        Connected,

        // error state:
        ConnectionFailed,
    };

    class HolographicAppMain
    {
        ...
        // Member functions for state transition handling
        void OnConnectionStatusChanged(RR::ConnectionStatus status, RR::Result error);
        void SetNewState(AppConnectionStatus state, const char* statusMsg);
        void SetNewSession(RR::ApiHandle<RR::RenderingSession> newSession);
        void StartModelLoading();

        // Members for state handling:

        // Model loading:
        std::string m_modelURI;
        RR::ApiHandle<RR::LoadModelAsync> m_loadModelAsync;

        // Connection state machine:
        AppConnectionStatus m_currentStatus = AppConnectionStatus::Disconnected;
        std::string m_statusMsg;
        RR::Result m_connectionResult = RR::Result::Success;
        RR::Result m_modelLoadResult = RR::Result::Success;
        bool m_isConnected = false;
        bool m_sessionStarted = false;
        RR::ApiHandle<RR::SessionPropertiesAsync> m_sessionPropertiesAsync;
        bool m_modelLoadTriggered = false;
        float m_modelLoadingProgress = 0.f;
        bool m_modelLoadFinished = false;
        double m_timeAtLastRESTCall = 0;
        bool m_needsCoordinateSystemUpdate = true;
    }

على جانب التنفيذ في ملف ‎.cpp أضف نصوص الدوال هذه:

void HolographicAppMain::StartModelLoading()
{
    m_modelLoadingProgress = 0.f;

    RR::LoadModelFromSasOptions options;
    options.ModelUri = m_modelURI.c_str();
    options.Parent = nullptr;

    // start the async model loading
    m_api->LoadModelFromSasAsync(options,
        // completed callback
        [this](RR::Status status, RR::ApiHandle<RR::LoadModelResult> result)
        {
            m_modelLoadResult = RR::StatusToResult(status);
            m_modelLoadFinished = true;

            if (m_modelLoadResult == RR::Result::Success)
            {
                RR::Double3 pos = { 0.0, 0.0, -2.0 };
                result->GetRoot()->SetPosition(pos);
            }
        },
        // progress update callback
            [this](float progress)
        {
            // progress callback
            m_modelLoadingProgress = progress;
            m_needsStatusUpdate = true;
        });
}



void HolographicAppMain::SetNewState(AppConnectionStatus state, const char* statusMsg)
{
    m_currentStatus = state;
    m_statusMsg = statusMsg ? statusMsg : "";

    // Some log for the VS output panel:
    const char* appStatus = nullptr;

    switch (state)
    {
        case AppConnectionStatus::Disconnected: appStatus = "Disconnected"; break;
        case AppConnectionStatus::CreatingSession: appStatus = "CreatingSession"; break;
        case AppConnectionStatus::StartingSession: appStatus = "StartingSession"; break;
        case AppConnectionStatus::Connecting: appStatus = "Connecting"; break;
        case AppConnectionStatus::Connected: appStatus = "Connected"; break;
        case AppConnectionStatus::ConnectionFailed: appStatus = "ConnectionFailed"; break;
    }

    char buffer[1024];
    sprintf_s(buffer, "Remote Rendering: New status: %s, result: %s\n", appStatus, m_statusMsg.c_str());
    OutputDebugStringA(buffer);
}

void HolographicAppMain::SetNewSession(RR::ApiHandle<RR::RenderingSession> newSession)
{
    SetNewState(AppConnectionStatus::StartingSession, nullptr);

    m_sessionStartingTime = m_timeAtLastRESTCall = m_timer.GetTotalSeconds();
    m_session = newSession;
    m_api = m_session->Connection();
    m_graphicsBinding = m_session->GetGraphicsBinding().as<RR::GraphicsBindingWmrD3d11>();
    m_session->ConnectionStatusChanged([this](auto status, auto error)
        {
            OnConnectionStatusChanged(status, error);
        });

};

void HolographicAppMain::OnConnectionStatusChanged(RR::ConnectionStatus status, RR::Result error)
{
    const char* asString = RR::ResultToString(error);
    m_connectionResult = error;

    switch (status)
    {
    case RR::ConnectionStatus::Connecting:
        SetNewState(AppConnectionStatus::Connecting, asString);
        break;
    case RR::ConnectionStatus::Connected:
        if (error == RR::Result::Success)
        {
            SetNewState(AppConnectionStatus::Connected, asString);
        }
        else
        {
            SetNewState(AppConnectionStatus::ConnectionFailed, asString);
        }
        m_modelLoadTriggered = m_modelLoadFinished = false;
        m_isConnected = error == RR::Result::Success;
        break;
    case RR::ConnectionStatus::Disconnected:
        if (error == RR::Result::Success)
        {
            SetNewState(AppConnectionStatus::Disconnected, asString);
        }
        else
        {
            SetNewState(AppConnectionStatus::ConnectionFailed, asString);
        }
        m_modelLoadTriggered = m_modelLoadFinished = false;
        m_isConnected = false;
        break;
    default:
        break;
    }
    
}

تحديث لكل إطار

علينا تحديث العميل مرة واحدة لكل علامة محاكاة والقيام ببعض تحديثات الحالة الإضافية. توفر الدالة HolographicAppMain::Update ربطاً جيداً للتحديثات لكل إطار.

تحديث تسلسل الحالات

نحتاج إلى استطلاع وضع الجلسة ومعرفة ما إذا كانت قد انتقلت إلى الحالة Ready. إذا نجحنا في الاتصال، فقد بدأنا أخيراً تحميل نموذج عبر StartModelLoading.

أضف التعليمة البرمجية الآتية إلى نص الدالة HolographicAppMain::Update:

// Updates the application state once per frame.
HolographicFrame HolographicAppMain::Update()
{
    if (m_session != nullptr)
    {
        // Tick the client to receive messages
        m_api->Update();

        if (!m_sessionStarted)
        {
            // Important: To avoid server-side throttling of the requests, we should call GetPropertiesAsync very infrequently:
            const double delayBetweenRESTCalls = 10.0;

            // query session status periodically until we reach 'session started'
            if (m_sessionPropertiesAsync == nullptr && m_timer.GetTotalSeconds() - m_timeAtLastRESTCall > delayBetweenRESTCalls)
            {
                m_timeAtLastRESTCall = m_timer.GetTotalSeconds();
                m_session->GetPropertiesAsync([this](RR::Status status, RR::ApiHandle<RR::RenderingSessionPropertiesResult> propertiesResult)
                    {
                        if (status == RR::Status::OK)
                        {
                            auto ctx = propertiesResult->GetContext();
                            if (ctx.Result == RR::Result::Success)
                            {
                                auto res = propertiesResult->GetSessionProperties();
                                switch (res.Status)
                                {
                                case RR::RenderingSessionStatus::Ready:
                                {
                                    // The following ConnectAsync is async, but we'll get notifications via OnConnectionStatusChanged
                                    m_sessionStarted = true;
                                    SetNewState(AppConnectionStatus::Connecting, nullptr);
                                    RR::RendererInitOptions init;
                                    init.IgnoreCertificateValidation = false;
                                    init.RenderMode = RR::ServiceRenderMode::Default;
                                    m_session->ConnectAsync(init, [](RR::Status, RR::ConnectionStatus) {});
                                }
                                break;
                                case RR::RenderingSessionStatus::Error:
                                    SetNewState(AppConnectionStatus::ConnectionFailed, "Session error");
                                    break;
                                case RR::RenderingSessionStatus::Stopped:
                                    SetNewState(AppConnectionStatus::ConnectionFailed, "Session stopped");
                                    break;
                                case RR::RenderingSessionStatus::Expired:
                                    SetNewState(AppConnectionStatus::ConnectionFailed, "Session expired");
                                    break;
                                }
                            }
                            else
                            {
                                SetNewState(AppConnectionStatus::ConnectionFailed, ctx.ErrorMessage.c_str());
                            }
                        }
                        else
                        {
                            SetNewState(AppConnectionStatus::ConnectionFailed, "Failed to retrieve session status");
                        }
                        m_sessionPropertiesQueryInProgress = false; // next try
                    });                }
            }
        }
        if (m_isConnected && !m_modelLoadTriggered)
        {
            m_modelLoadTriggered = true;
            StartModelLoading();
        }
    }

    if (m_needsCoordinateSystemUpdate && m_stationaryReferenceFrame && m_graphicsBinding)
    {
        // Set the coordinate system once. This must be called again whenever the coordinate system changes.
        winrt::com_ptr<ABI::Windows::Perception::Spatial::ISpatialCoordinateSystem> ptr{ m_stationaryReferenceFrame.CoordinateSystem().as<ABI::Windows::Perception::Spatial::ISpatialCoordinateSystem>() };
        m_graphicsBinding->UpdateUserCoordinateSystem(ptr.get());
        m_needsCoordinateSystemUpdate = false;
    }

    // Rest of the body:
    ...
}

تحديث نظام الإحداثيات

نحتاج إلى الموافقة على عرض الخدمة على نظام الإحداثيات لاستخدامها. نحتاج إلى m_stationaryReferenceFrame الذي يتم إنشاؤه في نهاية الدالة HolographicAppMain::OnHolographicDisplayIsAvailableChanged للوصول إلى نظام الإحداثيات الذي نريد استخدامه.

لا يتغير عادةً هذا النظام الإحداثي، لذا هذه التهيئة تكون لمرة واحدة. يجب استدعاؤه مرة أخرى إذا غيَّر التطبيق نظام الإحداثيات.

تحدد التعليمة البرمجية أعلاه نظام الإحداثيات مرة واحدة داخل الدالة Update بمجرد أن يكون لدينا نظام إحداثيات مرجعي وجلسة متصلة.

تحديث الكاميرا

نحتاج إلى تحديث مستويات مقاطع الكاميرا بحيث يتم الاحتفاظ بمزامنة كاميرا الخادم مع الكاميرا المحلية. يمكننا أن نفعل ذلك في نهاية دالة Update:

    ...
    if (m_isConnected)
    {
        // Any near/far plane values of your choosing.
        constexpr float fNear = 0.1f;
        constexpr float fFar = 10.0f;
        for (HolographicCameraPose const& cameraPose : prediction.CameraPoses())
        {
            // Set near and far to the holographic camera as normal
            cameraPose.HolographicCamera().SetNearPlaneDistance(fNear);
            cameraPose.HolographicCamera().SetFarPlaneDistance(fFar);
        }

        // The API to inform the server always requires near < far. Depth buffer data will be converted locally to match what is set on the HolographicCamera.
        auto settings = m_api->GetCameraSettings();
        settings->SetNearAndFarPlane(std::min(fNear, fFar), std::max(fNear, fFar));
        settings->SetEnableDepth(true);
    }

    // The holographic frame will be used to get up-to-date view and projection matrices and
    // to present the swap chain.
    return holographicFrame;
}

استدعاء

آخر شيء يجب القيام به هو استدعاء عرض المحتوى عن بُعد. علينا أن نقوم بهذا الاستدعاء في الموضع الصحيح بالضبط داخل تدفق العرض، بعد وضوح عرض الهدف وتحديد منفذ العرض. أدرج القصاصة البرمجية الآتية في التأمين UseHolographicCameraResources داخل الدالة HolographicAppMain::Render:

        ...
        // Existing clear function:
        context->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
        
        // ...

        // Existing check to test for valid camera:
        bool cameraActive = pCameraResources->AttachViewProjectionBuffer(m_deviceResources);


        // Inject remote rendering: as soon as we are connected, start blitting the remote frame.
        // We do the blitting after the Clear, and before cube rendering.
        if (m_isConnected && cameraActive)
        {
            m_graphicsBinding->BlitRemoteFrame();
        }

        ...

تشغيل تطبيق العرض التوضيحي

يجب أن تكون العينة الآن في حالة تجميعها وتشغيلها.

عند تشغيل العينة بشكل صحيح، يظهر المكعب الدوار أمامك مباشرةً، وبعد إنشاء الجلسة وتحميل النموذج، فإنها تعرض نموذج المحرك الموجود في موضع الرأس الحالي. قد يستغرق إنشاء الجلسة وتحميل النموذج بضع دقائق. تكون الحالة الحالية مكتوبة فقط على لوحة إخراج Visual Studio. ومن ثمَّ فمن المستحسن بدء العينة من داخل Visual Studio.

تنبيه

يتم قطع الاتصال بالعميل من الخادم عند عدم استدعاء دالة التجزئة لبضع ثوان. لذا فإن تشغيل نقاط التوقف يمكن أن يؤدي بسهولة إلى فصل التطبيق.

لعرض الحالة المناسبة مع لوحة نص، راجع الإصدار الذي تم فحصه سابقاً من هذا البرنامج التعليمي على GitHub.

الخطوات التالية

في هذا البرنامج التعليمي، تعلمت جميع الخطوات اللازمة لإضافة Remote Rendering إلى مخزون عينة C++/DirectX11 لتطبيق Holographic. لتحويل نموذجك، راجع التشغيل السريع الآتي: