aboutsummaryrefslogtreecommitdiffstats
path: root/support/hololens/OpenGLESPage.xaml.cpp
blob: c4c7dd21e3a6c7bd6a30724ee35f477f394e3d2e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "pch.h"
#include "OpenGLESPage.xaml.h"
#include "Servo.h"

using namespace hlservo;
using namespace Platform;
using namespace Concurrency;
using namespace Windows::Foundation;

static char sWakeupEvent[] = "SIGNAL_WAKEUP";

OpenGLESPage::OpenGLESPage()
    : OpenGLESPage(nullptr)
{
}

OpenGLESPage::OpenGLESPage(OpenGLES* openGLES)
    : mOpenGLES(openGLES)
    , mRenderSurface(EGL_NO_SURFACE)
{
    InitializeComponent();
    Windows::UI::Core::CoreWindow ^ window = Windows::UI::Xaml::Window::Current->CoreWindow;
    window->VisibilityChanged += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow ^,
      Windows::UI::Core::VisibilityChangedEventArgs ^>(this, &OpenGLESPage::OnVisibilityChanged);
    this->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded);
}

OpenGLESPage::~OpenGLESPage()
{
    StopRenderLoop();
    DestroyRenderSurface();
}

void OpenGLESPage::OnPageLoaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
{
    CreateRenderSurface();
    StartRenderLoop();
}

void OpenGLESPage::OnVisibilityChanged(Windows::UI::Core::CoreWindow ^ sender,
    Windows::UI::Core::VisibilityChangedEventArgs ^ args)
{
    if (args->Visible && mRenderSurface != EGL_NO_SURFACE) {
        StartRenderLoop();
    } else {
        StopRenderLoop();
    }
}

void OpenGLESPage::CreateRenderSurface()
{
    if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE) {
        mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel);
    }
}

void OpenGLESPage::DestroyRenderSurface()
{
    if (mOpenGLES) {
        mOpenGLES->DestroySurface(mRenderSurface);
    }
    mRenderSurface = EGL_NO_SURFACE;
}

void OpenGLESPage::RecoverFromLostDevice()
{
    StopRenderLoop();
    {
        critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);

        DestroyRenderSurface();
        mOpenGLES->Reset();
        CreateRenderSurface();
    }
    StartRenderLoop();
}

void OpenGLESPage::StartRenderLoop()
{
    if (mRenderLoopWorker != nullptr && mRenderLoopWorker->Status == Windows::Foundation::AsyncStatus::Started) {
        return;
    }

    auto loop = [this](Windows::Foundation::IAsyncAction ^ action) {
      critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);

      HANDLE hEvent = ::CreateEventA(nullptr, FALSE, FALSE, sWakeupEvent);

      // Called by Servo
      Servo::sMakeCurrent = [this]() {
        /* EGLint panelWidth = 0; */
        /* EGLint panelHeight = 0; */
        /* mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight); */
        /* glViewport(0, 0, panelWidth, panelHeight); */
        /* mServo->SetSize(panelWidth, panelHeight); */
        mOpenGLES->MakeCurrent(mRenderSurface);
      };

      // Called by Servo
      Servo::sFlush = [this]() {
        if (mOpenGLES->SwapBuffers(mRenderSurface) != GL_TRUE) {
          // The call to eglSwapBuffers might not be successful (i.e. due to Device Lost)
          // If the call fails, then we must reinitialize EGL and the GL resources.
          swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High,
              ref new Windows::UI::Core::DispatchedHandler([=]() {
                RecoverFromLostDevice();
          }, CallbackContext::Any));
        }
      };

      mOpenGLES->MakeCurrent(mRenderSurface);

      EGLint panelWidth = 0;
      EGLint panelHeight = 0;
      mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
      glViewport(0, 0, panelWidth, panelHeight);
      mServo = new Servo(panelWidth, panelHeight);

      while (action->Status == Windows::Foundation::AsyncStatus::Started) {
        // Block until Servo::sWakeUp is called.
        // Or run full speed if animating (see on_animating_changed),
        // it will endup blocking on SwapBuffers to limit rendering to 60FPS
        if (!Servo::sAnimating) {
           ::WaitForSingleObject(hEvent, INFINITE);
        }
        mServo->PerformUpdates();
      }
    };

    auto workItemHandler = ref new Windows::System::Threading::WorkItemHandler(loop);

    // Run Servo task in a high priority background thread.
    mRenderLoopWorker = Windows::System::Threading::ThreadPool::RunAsync(
      workItemHandler,
      Windows::System::Threading::WorkItemPriority::High,
      Windows::System::Threading::WorkItemOptions::TimeSliced);

    Servo::sWakeUp = []() {
      HANDLE hEvent = ::OpenEventA(EVENT_ALL_ACCESS, FALSE, sWakeupEvent);
      ::SetEvent(hEvent);
    };
}

void OpenGLESPage::StopRenderLoop()
{
    if (mRenderLoopWorker) {
        mRenderLoopWorker->Cancel();
        mRenderLoopWorker = nullptr;
    }
}