/* * cocos2d-x http://www.cocos2d-x.org * * Copyright (c) 2010-2014 - cocos2d-x community * * Portions Copyright (c) Microsoft Open Technologies, Inc. * All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ #include "App.xaml.h" #include "OpenGLESPage.xaml.h" using namespace cocos2d; using namespace Platform; using namespace Concurrency; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::Graphics::Display; using namespace Windows::System::Threading; using namespace Windows::UI::Core; using namespace Windows::UI::Input; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Controls::Primitives; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Navigation; OpenGLESPage::OpenGLESPage() : OpenGLESPage(nullptr) { } OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) : mOpenGLES(openGLES), mRenderSurface(EGL_NO_SURFACE), mCustomRenderSurfaceSize(0,0), mUseCustomRenderSurfaceSize(false), m_coreInput(nullptr), m_dpi(0.0f), m_deviceLost(false), m_orientation(DisplayOrientations::Landscape) { InitializeComponent(); Windows::UI::Core::CoreWindow^ window = Windows::UI::Xaml::Window::Current->CoreWindow; window->VisibilityChanged += ref new Windows::Foundation::TypedEventHandler(this, &OpenGLESPage::OnVisibilityChanged); swapChainPanel->SizeChanged += ref new Windows::UI::Xaml::SizeChangedEventHandler(this, &OpenGLESPage::OnSwapChainPanelSizeChanged); DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); currentDisplayInformation->OrientationChanged += ref new TypedEventHandler(this, &OpenGLESPage::OnOrientationChanged); m_orientation = currentDisplayInformation->CurrentOrientation; this->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded); mSwapChainPanelSize = { swapChainPanel->RenderSize.Width, swapChainPanel->RenderSize.Height }; #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) Windows::UI::ViewManagement::StatusBar::GetForCurrentView()->HideAsync(); #else // Disable all pointer visual feedback for better performance when touching. // This is not supported on Windows Phone applications. auto pointerVisualizationSettings = Windows::UI::Input::PointerVisualizationSettings::GetForCurrentView(); pointerVisualizationSettings->IsContactFeedbackEnabled = false; pointerVisualizationSettings->IsBarrelButtonFeedbackEnabled = false; #endif // Register our SwapChainPanel to get independent input pointer events auto workItemHandler = ref new WorkItemHandler([this](IAsyncAction ^) { // The CoreIndependentInputSource will raise pointer events for the specified device types on whichever thread it's created on. m_coreInput = swapChainPanel->CreateCoreIndependentInputSource( Windows::UI::Core::CoreInputDeviceTypes::Mouse | Windows::UI::Core::CoreInputDeviceTypes::Touch | Windows::UI::Core::CoreInputDeviceTypes::Pen ); // Register for pointer events, which will be raised on the background thread. m_coreInput->PointerPressed += ref new TypedEventHandler(this, &OpenGLESPage::OnPointerPressed); m_coreInput->PointerMoved += ref new TypedEventHandler(this, &OpenGLESPage::OnPointerMoved); m_coreInput->PointerReleased += ref new TypedEventHandler(this, &OpenGLESPage::OnPointerReleased); // Begin processing input messages as they're delivered. m_coreInput->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit); }); // Run task on a dedicated high priority background thread. m_inputLoopWorker = ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::TimeSliced); } OpenGLESPage::~OpenGLESPage() { StopRenderLoop(); DestroyRenderSurface(); } void OpenGLESPage::OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { // The SwapChainPanel has been created and arranged in the page layout, so EGL can be initialized. CreateRenderSurface(); StartRenderLoop(); } void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e) { if (m_renderer) { m_renderer->QueuePointerEvent(PointerEventType::PointerPressed, e); } } void OpenGLESPage::OnPointerMoved(Object^ sender, PointerEventArgs^ e) { if (m_renderer) { m_renderer->QueuePointerEvent(PointerEventType::PointerMoved, e); } } void OpenGLESPage::OnPointerReleased(Object^ sender, PointerEventArgs^ e) { if (m_renderer) { m_renderer->QueuePointerEvent(PointerEventType::PointerReleased, e); } } void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args) { critical_section::scoped_lock lock(mSwapChainPanelSizeCriticalSection); m_orientation = sender->CurrentOrientation; } 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::OnSwapChainPanelSizeChanged(Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e) { // Size change events occur outside of the render thread. A lock is required when updating // the swapchainpanel size critical_section::scoped_lock lock(mSwapChainPanelSizeCriticalSection); mSwapChainPanelSize = { e->NewSize.Width, e->NewSize.Height }; } void OpenGLESPage::GetSwapChainPanelSize(GLsizei* width, GLsizei* height) { critical_section::scoped_lock lock(mSwapChainPanelSizeCriticalSection); // If a custom render surface size is specified, return its size instead of // the swapchain panel size. if (mUseCustomRenderSurfaceSize) { *width = static_cast(mCustomRenderSurfaceSize.Width); *height = static_cast(mCustomRenderSurfaceSize.Height); } else { *width = static_cast(mSwapChainPanelSize.Width); *height = static_cast(mSwapChainPanelSize.Height); } } void OpenGLESPage::CreateRenderSurface() { if (mOpenGLES) { // // A Custom render surface size can be specified by uncommenting the following lines. // The render surface will be automatically scaled to fit the entire window. Using a // smaller sized render surface can result in a performance gain. // //mCustomRenderSurfaceSize = Size(800, 600); //mUseCustomRenderSurfaceSize = true; mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, mUseCustomRenderSurfaceSize ? &mCustomRenderSurfaceSize : nullptr); } } void OpenGLESPage::DestroyRenderSurface() { if (mOpenGLES) { mOpenGLES->DestroySurface(mRenderSurface); } mRenderSurface = EGL_NO_SURFACE; } void OpenGLESPage::RecoverFromLostDevice() { // Stop the render loop, reset OpenGLES, recreate the render surface // and start the render loop again to recover from a lost device. StopRenderLoop(); { critical_section::scoped_lock lock(mRenderSurfaceCriticalSection); DestroyRenderSurface(); mOpenGLES->Reset(); CreateRenderSurface(); } StartRenderLoop(); } void OpenGLESPage::StartRenderLoop() { // If the render loop is already running then do not start another thread. if (mRenderLoopWorker != nullptr && mRenderLoopWorker->Status == Windows::Foundation::AsyncStatus::Started) { return; } DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); m_dpi = currentDisplayInformation->LogicalDpi; auto dispatcher = Windows::UI::Xaml::Window::Current->CoreWindow->Dispatcher; // Create a task for rendering that will be run on a background thread. auto workItemHandler = ref new Windows::System::Threading::WorkItemHandler([this, dispatcher](Windows::Foundation::IAsyncAction ^ action) { critical_section::scoped_lock lock(mRenderSurfaceCriticalSection); mOpenGLES->MakeCurrent(mRenderSurface); GLsizei panelWidth = 0; GLsizei panelHeight = 0; GetSwapChainPanelSize(&panelWidth, &panelHeight); if (m_renderer.get() == nullptr) { m_renderer = std::make_shared(panelWidth, panelHeight, m_dpi, m_orientation, dispatcher, swapChainPanel); } if (m_deviceLost) { m_deviceLost = false; m_renderer->DeviceLost(); } else { m_renderer->Resume(); } while (action->Status == Windows::Foundation::AsyncStatus::Started && !m_deviceLost) { GetSwapChainPanelSize(&panelWidth, &panelHeight); m_renderer.get()->Draw(panelWidth, panelHeight, m_dpi, m_orientation); // 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. if (mOpenGLES->SwapBuffers(mRenderSurface) != GL_TRUE) { m_deviceLost = true; // XAML objects like the SwapChainPanel must only be manipulated on the UI thread. swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([=]() { RecoverFromLostDevice(); }, CallbackContext::Any)); return; } } }); // Run task on a dedicated high priority background thread. mRenderLoopWorker = Windows::System::Threading::ThreadPool::RunAsync(workItemHandler, Windows::System::Threading::WorkItemPriority::High, Windows::System::Threading::WorkItemOptions::TimeSliced); } void OpenGLESPage::StopRenderLoop() { if (mRenderLoopWorker) { mRenderLoopWorker->Cancel(); mRenderLoopWorker = nullptr; } if (m_renderer) { m_renderer->Pause(); } }