updated cocos2d-x/angle template files to version 0edf96a2f54a43d068244a86aaf9133c1bf71fe5

This commit is contained in:
Dale Stammen 2015-10-16 15:36:34 -07:00
parent ce88dbdee4
commit fdebb373b1
17 changed files with 672 additions and 773 deletions

View File

@ -112,14 +112,7 @@ void OpenGLES::Initialize()
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again // 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again
// using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer. // using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer.
// //
// Note: On Windows Phone, we #ifdef out the first set of calls to eglPlatformDisplayEXT and eglInitialize.
// Windows Phones devices only support D3D11 Feature Level 9_3, but the Windows Phone emulator supports 11_0+.
// We use this #ifdef to limit the Phone emulator to Feature Level 9_3, making it behave more like
// real Windows Phone devices.
// If you wish to test Feature Level 10_0+ in the Windows Phone emulator then you should remove this #ifdef.
//
#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP)
// This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details. // This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details.
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
@ -128,9 +121,8 @@ void OpenGLES::Initialize()
} }
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE)
#endif
{ {
// This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on Windows Phone, or certain Windows tablets). // This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices).
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
{ {
@ -139,7 +131,7 @@ void OpenGLES::Initialize()
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE)
{ {
// This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU (e.g. on Surface RT). // This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU.
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
{ {
@ -188,13 +180,18 @@ void OpenGLES::Reset()
Initialize(); Initialize();
} }
EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize) EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize, const float* resolutionScale)
{ {
if (!panel) if (!panel)
{ {
throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid"); throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid");
} }
if (renderSurfaceSize != nullptr && resolutionScale != nullptr)
{
throw Exception::CreateException(E_INVALIDARG, L"A size and a scale can't both be specified");
}
EGLSurface surface = EGL_NO_SURFACE; EGLSurface surface = EGL_NO_SURFACE;
const EGLint surfaceAttributes[] = const EGLint surfaceAttributes[] =
@ -207,12 +204,18 @@ EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurf
// Create a PropertySet and initialize with the EGLNativeWindowType. // Create a PropertySet and initialize with the EGLNativeWindowType.
PropertySet^ surfaceCreationProperties = ref new PropertySet(); PropertySet^ surfaceCreationProperties = ref new PropertySet();
surfaceCreationProperties->Insert(ref new Platform::String(EGLNativeWindowTypeProperty), panel); surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), panel);
// If a render surface size is specified, add it to the surface creation properties // If a render surface size is specified, add it to the surface creation properties
if (renderSurfaceSize != nullptr) if (renderSurfaceSize != nullptr)
{ {
surfaceCreationProperties->Insert(ref new Platform::String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize)); surfaceCreationProperties->Insert(ref new String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize));
}
// If a resolution scale is specified, add it to the surface creation properties
if (resolutionScale != nullptr)
{
surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(*resolutionScale));
} }
surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes); surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes);
@ -224,6 +227,12 @@ EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurf
return surface; return surface;
} }
void OpenGLES::GetSurfaceDimensions(const EGLSurface surface, EGLint* width, EGLint* height)
{
eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
}
void OpenGLES::DestroySurface(const EGLSurface surface) void OpenGLES::DestroySurface(const EGLSurface surface)
{ {
if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE) if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE)

View File

@ -20,7 +20,6 @@
// OpenGL ES includes // OpenGL ES includes
#include <GLES3/gl3.h> #include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
// EGL includes // EGL includes
#include <EGL/egl.h> #include <EGL/egl.h>
@ -34,7 +33,8 @@ public:
OpenGLES(); OpenGLES();
~OpenGLES(); ~OpenGLES();
EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize); EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize, const float* renderResolutionScale);
void GetSurfaceDimensions(const EGLSurface surface, EGLint *width, EGLint *height);
void DestroySurface(const EGLSurface surface); void DestroySurface(const EGLSurface surface);
void MakeCurrent(const EGLSurface surface); void MakeCurrent(const EGLSurface surface);
EGLBoolean SwapBuffers(const EGLSurface surface); EGLBoolean SwapBuffers(const EGLSurface surface);

View File

@ -24,7 +24,6 @@ using namespace cocos2d;
using namespace Platform; using namespace Platform;
using namespace Concurrency; using namespace Concurrency;
using namespace Windows::Foundation; using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Graphics::Display; using namespace Windows::Graphics::Display;
using namespace Windows::System::Threading; using namespace Windows::System::Threading;
using namespace Windows::UI::Core; using namespace Windows::UI::Core;
@ -50,8 +49,6 @@ OpenGLESPage::OpenGLESPage() :
OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) : OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
mOpenGLES(openGLES), mOpenGLES(openGLES),
mRenderSurface(EGL_NO_SURFACE), mRenderSurface(EGL_NO_SURFACE),
mCustomRenderSurfaceSize(0,0),
mUseCustomRenderSurfaceSize(false),
mCoreInput(nullptr), mCoreInput(nullptr),
mDpi(0.0f), mDpi(0.0f),
mDeviceLost(false), mDeviceLost(false),
@ -72,8 +69,6 @@ OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
window->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &OpenGLESPage::OnCharacterReceived); window->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &OpenGLESPage::OnCharacterReceived);
swapChainPanel->SizeChanged +=
ref new Windows::UI::Xaml::SizeChangedEventHandler(this, &OpenGLESPage::OnSwapChainPanelSizeChanged);
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
@ -85,8 +80,6 @@ OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
this->Loaded += this->Loaded +=
ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded); ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded);
mSwapChainPanelSize = { swapChainPanel->RenderSize.Width, swapChainPanel->RenderSize.Height };
#if _MSC_VER >= 1900 #if _MSC_VER >= 1900
if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar")) if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
{ {
@ -158,6 +151,201 @@ void OpenGLESPage::OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::Rou
mVisible = true; mVisible = true;
} }
void OpenGLESPage::CreateRenderSurface()
{
if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
{
// The app can configure the the SwapChainPanel which may boost performance.
// By default, this template uses the default configuration.
mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, nullptr);
// You can configure the SwapChainPanel to render at a lower resolution and be scaled up to
// the swapchain panel size. This scaling is often free on mobile hardware.
//
// One way to configure the SwapChainPanel is to specify precisely which resolution it should render at.
// Size customRenderSurfaceSize = Size(800, 600);
// mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, &customRenderSurfaceSize, nullptr);
//
// Another way is to tell the SwapChainPanel to render at a certain scale factor compared to its size.
// e.g. if the SwapChainPanel is 1920x1280 then setting a factor of 0.5f will make the app render at 960x640
// float customResolutionScale = 0.5f;
// mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, &customResolutionScale);
//
}
}
void OpenGLESPage::DestroyRenderSurface()
{
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
}
mRenderSurface = EGL_NO_SURFACE;
}
void OpenGLESPage::RecoverFromLostDevice()
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
DestroyRenderSurface();
mOpenGLES->Reset();
CreateRenderSurface();
std::unique_lock<std::mutex> locker(mSleepMutex);
mDeviceLost = false;
mSleepCondition.notify_one();
}
void OpenGLESPage::TerminateApp()
{
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
mOpenGLES->Cleanup();
}
}
Windows::UI::Xaml::Application::Current->Exit();
}
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();
mDpi = 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)
{
mOpenGLES->MakeCurrent(mRenderSurface);
GLsizei panelWidth = 0;
GLsizei panelHeight = 0;
mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
if (mRenderer.get() == nullptr)
{
mRenderer = std::make_shared<Cocos2dRenderer>(panelWidth, panelHeight, mDpi, mOrientation, dispatcher, swapChainPanel);
}
mRenderer->Resume();
while (action->Status == Windows::Foundation::AsyncStatus::Started)
{
if (!mVisible)
{
mRenderer->Pause();
}
// wait until app is visible again or thread is cancelled
while (!mVisible)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (mVisible)
{
mRenderer->Resume();
}
else // spurious wake up
{
continue;
}
}
mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
mRenderer.get()->Draw(panelWidth, panelHeight, mDpi, mOrientation);
// Recreate input dispatch
if (GLViewImpl::sharedOpenGLView() && mCursorVisible != GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
CreateInput();
mCursorVisible = GLViewImpl::sharedOpenGLView()->isCursorVisible();
}
if (mRenderer->AppShouldExit())
{
// run on main UI thread
swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new DispatchedHandler([=]()
{
TerminateApp();
}));
return;
}
EGLBoolean result = GL_FALSE;
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
result = mOpenGLES->SwapBuffers(mRenderSurface);
}
if (result != GL_TRUE)
{
// The call to eglSwapBuffers was not be successful (i.e. due to Device Lost)
// If the call fails, then we must reinitialize EGL and the GL resources.
mRenderer->Pause();
mDeviceLost = 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));
// wait until OpenGL is reset or thread is cancelled
while (mDeviceLost)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (!mDeviceLost)
{
mOpenGLES->MakeCurrent(mRenderSurface);
// restart cocos2d-x
mRenderer->DeviceLost();
}
else // spurious wake up
{
continue;
}
}
}
}
});
// 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();
std::unique_lock<std::mutex> locker(mSleepMutex);
mSleepCondition.notify_one();
mRenderLoopWorker = nullptr;
}
}
void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e) void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e)
{ {
bool isMouseEvent = e->CurrentPoint->PointerDevice->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse; bool isMouseEvent = e->CurrentPoint->PointerDevice->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse;
@ -229,7 +417,6 @@ void OpenGLESPage::OnKeyReleased(CoreWindow^ sender, KeyEventArgs^ e)
void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args) void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
{ {
critical_section::scoped_lock lock(mSwapChainPanelSizeCriticalSection);
mOrientation = sender->CurrentOrientation; mOrientation = sender->CurrentOrientation;
} }
@ -279,216 +466,3 @@ void OpenGLESPage::OnBackButtonPressed(Object^ sender, BackPressedEventArgs^ arg
} }
} }
#endif #endif
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<GLsizei>(mCustomRenderSurfaceSize.Width);
*height = static_cast<GLsizei>(mCustomRenderSurfaceSize.Height);
}
else
{
*width = static_cast<GLsizei>(mSwapChainPanelSize.Width);
*height = static_cast<GLsizei>(mSwapChainPanelSize.Height);
}
}
void OpenGLESPage::CreateRenderSurface()
{
if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
{
//
// 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()
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
DestroyRenderSurface();
mOpenGLES->Reset();
CreateRenderSurface();
std::unique_lock<std::mutex> locker(mSleepMutex);
mDeviceLost = false;
mSleepCondition.notify_one();
}
void OpenGLESPage::TerminateApp()
{
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
mOpenGLES->Cleanup();
}
}
Windows::UI::Xaml::Application::Current->Exit();
}
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();
mDpi = 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)
{
mOpenGLES->MakeCurrent(mRenderSurface);
GLsizei panelWidth = 0;
GLsizei panelHeight = 0;
GetSwapChainPanelSize(&panelWidth, &panelHeight);
if (mRenderer.get() == nullptr)
{
mRenderer = std::make_shared<Cocos2dRenderer>(panelWidth, panelHeight, mDpi, mOrientation, dispatcher, swapChainPanel);
}
mRenderer->Resume();
while (action->Status == Windows::Foundation::AsyncStatus::Started)
{
if (!mVisible)
{
mRenderer->Pause();
}
// wait until app is visible again or thread is cancelled
while (!mVisible)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (mVisible)
{
mRenderer->Resume();
}
else // spurious wake up
{
continue;
}
}
GetSwapChainPanelSize(&panelWidth, &panelHeight);
mRenderer.get()->Draw(panelWidth, panelHeight, mDpi, mOrientation);
// Recreate input dispatch
if (GLViewImpl::sharedOpenGLView() && mCursorVisible != GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
CreateInput();
mCursorVisible = GLViewImpl::sharedOpenGLView()->isCursorVisible();
}
if (mRenderer->AppShouldExit())
{
// run on main UI thread
swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new DispatchedHandler([this]()
{
TerminateApp();
}));
return;
}
EGLBoolean result = GL_FALSE;
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
result = mOpenGLES->SwapBuffers(mRenderSurface);
}
if (result != GL_TRUE)
{
// The call to eglSwapBuffers was not be successful (i.e. due to Device Lost)
// If the call fails, then we must reinitialize EGL and the GL resources.
mRenderer->Pause();
mDeviceLost = 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));
// wait until OpenGL is reset or thread is cancelled
while (mDeviceLost)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (!mDeviceLost)
{
mOpenGLES->MakeCurrent(mRenderSurface);
// restart cocos2d-x
mRenderer->DeviceLost();
}
else // spurious wake up
{
continue;
}
}
}
}
});
// 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();
std::unique_lock<std::mutex> locker(mSleepMutex);
mSleepCondition.notify_one();
mRenderLoopWorker = nullptr;
}
}

View File

@ -41,11 +41,9 @@ namespace CocosAppWinRT
private: private:
void OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); void OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args);
void OnSwapChainPanelSizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e);
#if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || _MSC_VER >= 1900 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || _MSC_VER >= 1900
void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args); void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args);
#endif #endif
void GetSwapChainPanelSize(GLsizei* width, GLsizei* height);
void CreateRenderSurface(); void CreateRenderSurface();
void DestroyRenderSurface(); void DestroyRenderSurface();
void RecoverFromLostDevice(); void RecoverFromLostDevice();
@ -58,12 +56,6 @@ namespace CocosAppWinRT
OpenGLES* mOpenGLES; OpenGLES* mOpenGLES;
std::shared_ptr<Cocos2dRenderer> mRenderer; std::shared_ptr<Cocos2dRenderer> mRenderer;
Windows::Foundation::Size mSwapChainPanelSize;
Concurrency::critical_section mSwapChainPanelSizeCriticalSection;
Windows::Foundation::Size mCustomRenderSurfaceSize;
bool mUseCustomRenderSurfaceSize;
EGLSurface mRenderSurface; // This surface is associated with a swapChainPanel on the page EGLSurface mRenderSurface; // This surface is associated with a swapChainPanel on the page
Concurrency::critical_section mRenderSurfaceCriticalSection; Concurrency::critical_section mRenderSurfaceCriticalSection;
Windows::Foundation::IAsyncAction^ mRenderLoopWorker; Windows::Foundation::IAsyncAction^ mRenderLoopWorker;

View File

@ -37,7 +37,6 @@ THE SOFTWARE.
#include "GLES2/gl2.h" #include "GLES2/gl2.h"
#include "GLES2/gl2ext.h" #include "GLES2/gl2ext.h"
#include "GLES3/gl3.h" #include "GLES3/gl3.h"
#include "GLES3/gl3ext.h"
#define glMapBuffer glMapBufferOES #define glMapBuffer glMapBufferOES

View File

@ -112,14 +112,7 @@ void OpenGLES::Initialize()
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again // 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again
// using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer. // using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer.
// //
// Note: On Windows Phone, we #ifdef out the first set of calls to eglPlatformDisplayEXT and eglInitialize.
// Windows Phones devices only support D3D11 Feature Level 9_3, but the Windows Phone emulator supports 11_0+.
// We use this #ifdef to limit the Phone emulator to Feature Level 9_3, making it behave more like
// real Windows Phone devices.
// If you wish to test Feature Level 10_0+ in the Windows Phone emulator then you should remove this #ifdef.
//
#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP)
// This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details. // This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details.
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
@ -128,9 +121,8 @@ void OpenGLES::Initialize()
} }
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE)
#endif
{ {
// This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on Windows Phone, or certain Windows tablets). // This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices).
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
{ {
@ -139,7 +131,7 @@ void OpenGLES::Initialize()
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE)
{ {
// This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU (e.g. on Surface RT). // This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU.
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
{ {
@ -188,13 +180,18 @@ void OpenGLES::Reset()
Initialize(); Initialize();
} }
EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize) EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize, const float* resolutionScale)
{ {
if (!panel) if (!panel)
{ {
throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid"); throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid");
} }
if (renderSurfaceSize != nullptr && resolutionScale != nullptr)
{
throw Exception::CreateException(E_INVALIDARG, L"A size and a scale can't both be specified");
}
EGLSurface surface = EGL_NO_SURFACE; EGLSurface surface = EGL_NO_SURFACE;
const EGLint surfaceAttributes[] = const EGLint surfaceAttributes[] =
@ -207,12 +204,18 @@ EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurf
// Create a PropertySet and initialize with the EGLNativeWindowType. // Create a PropertySet and initialize with the EGLNativeWindowType.
PropertySet^ surfaceCreationProperties = ref new PropertySet(); PropertySet^ surfaceCreationProperties = ref new PropertySet();
surfaceCreationProperties->Insert(ref new Platform::String(EGLNativeWindowTypeProperty), panel); surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), panel);
// If a render surface size is specified, add it to the surface creation properties // If a render surface size is specified, add it to the surface creation properties
if (renderSurfaceSize != nullptr) if (renderSurfaceSize != nullptr)
{ {
surfaceCreationProperties->Insert(ref new Platform::String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize)); surfaceCreationProperties->Insert(ref new String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize));
}
// If a resolution scale is specified, add it to the surface creation properties
if (resolutionScale != nullptr)
{
surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(*resolutionScale));
} }
surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes); surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes);
@ -224,6 +227,12 @@ EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurf
return surface; return surface;
} }
void OpenGLES::GetSurfaceDimensions(const EGLSurface surface, EGLint* width, EGLint* height)
{
eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
}
void OpenGLES::DestroySurface(const EGLSurface surface) void OpenGLES::DestroySurface(const EGLSurface surface)
{ {
if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE) if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE)

View File

@ -20,7 +20,6 @@
// OpenGL ES includes // OpenGL ES includes
#include <GLES3/gl3.h> #include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
// EGL includes // EGL includes
#include <EGL/egl.h> #include <EGL/egl.h>
@ -34,7 +33,8 @@ public:
OpenGLES(); OpenGLES();
~OpenGLES(); ~OpenGLES();
EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize); EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize, const float* renderResolutionScale);
void GetSurfaceDimensions(const EGLSurface surface, EGLint *width, EGLint *height);
void DestroySurface(const EGLSurface surface); void DestroySurface(const EGLSurface surface);
void MakeCurrent(const EGLSurface surface); void MakeCurrent(const EGLSurface surface);
EGLBoolean SwapBuffers(const EGLSurface surface); EGLBoolean SwapBuffers(const EGLSurface surface);

View File

@ -24,7 +24,6 @@ using namespace cocos2d;
using namespace Platform; using namespace Platform;
using namespace Concurrency; using namespace Concurrency;
using namespace Windows::Foundation; using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Graphics::Display; using namespace Windows::Graphics::Display;
using namespace Windows::System::Threading; using namespace Windows::System::Threading;
using namespace Windows::UI::Core; using namespace Windows::UI::Core;
@ -50,8 +49,6 @@ OpenGLESPage::OpenGLESPage() :
OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) : OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
mOpenGLES(openGLES), mOpenGLES(openGLES),
mRenderSurface(EGL_NO_SURFACE), mRenderSurface(EGL_NO_SURFACE),
mCustomRenderSurfaceSize(0,0),
mUseCustomRenderSurfaceSize(false),
mCoreInput(nullptr), mCoreInput(nullptr),
mDpi(0.0f), mDpi(0.0f),
mDeviceLost(false), mDeviceLost(false),
@ -72,8 +69,6 @@ OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
window->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &OpenGLESPage::OnCharacterReceived); window->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &OpenGLESPage::OnCharacterReceived);
swapChainPanel->SizeChanged +=
ref new Windows::UI::Xaml::SizeChangedEventHandler(this, &OpenGLESPage::OnSwapChainPanelSizeChanged);
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
@ -85,8 +80,6 @@ OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
this->Loaded += this->Loaded +=
ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded); ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded);
mSwapChainPanelSize = { swapChainPanel->RenderSize.Width, swapChainPanel->RenderSize.Height };
#if _MSC_VER >= 1900 #if _MSC_VER >= 1900
if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar")) if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
{ {
@ -158,6 +151,201 @@ void OpenGLESPage::OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::Rou
mVisible = true; mVisible = true;
} }
void OpenGLESPage::CreateRenderSurface()
{
if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
{
// The app can configure the the SwapChainPanel which may boost performance.
// By default, this template uses the default configuration.
mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, nullptr);
// You can configure the SwapChainPanel to render at a lower resolution and be scaled up to
// the swapchain panel size. This scaling is often free on mobile hardware.
//
// One way to configure the SwapChainPanel is to specify precisely which resolution it should render at.
// Size customRenderSurfaceSize = Size(800, 600);
// mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, &customRenderSurfaceSize, nullptr);
//
// Another way is to tell the SwapChainPanel to render at a certain scale factor compared to its size.
// e.g. if the SwapChainPanel is 1920x1280 then setting a factor of 0.5f will make the app render at 960x640
// float customResolutionScale = 0.5f;
// mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, &customResolutionScale);
//
}
}
void OpenGLESPage::DestroyRenderSurface()
{
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
}
mRenderSurface = EGL_NO_SURFACE;
}
void OpenGLESPage::RecoverFromLostDevice()
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
DestroyRenderSurface();
mOpenGLES->Reset();
CreateRenderSurface();
std::unique_lock<std::mutex> locker(mSleepMutex);
mDeviceLost = false;
mSleepCondition.notify_one();
}
void OpenGLESPage::TerminateApp()
{
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
mOpenGLES->Cleanup();
}
}
Windows::UI::Xaml::Application::Current->Exit();
}
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();
mDpi = 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)
{
mOpenGLES->MakeCurrent(mRenderSurface);
GLsizei panelWidth = 0;
GLsizei panelHeight = 0;
mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
if (mRenderer.get() == nullptr)
{
mRenderer = std::make_shared<Cocos2dRenderer>(panelWidth, panelHeight, mDpi, mOrientation, dispatcher, swapChainPanel);
}
mRenderer->Resume();
while (action->Status == Windows::Foundation::AsyncStatus::Started)
{
if (!mVisible)
{
mRenderer->Pause();
}
// wait until app is visible again or thread is cancelled
while (!mVisible)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (mVisible)
{
mRenderer->Resume();
}
else // spurious wake up
{
continue;
}
}
mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
mRenderer.get()->Draw(panelWidth, panelHeight, mDpi, mOrientation);
// Recreate input dispatch
if (GLViewImpl::sharedOpenGLView() && mCursorVisible != GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
CreateInput();
mCursorVisible = GLViewImpl::sharedOpenGLView()->isCursorVisible();
}
if (mRenderer->AppShouldExit())
{
// run on main UI thread
swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new DispatchedHandler([=]()
{
TerminateApp();
}));
return;
}
EGLBoolean result = GL_FALSE;
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
result = mOpenGLES->SwapBuffers(mRenderSurface);
}
if (result != GL_TRUE)
{
// The call to eglSwapBuffers was not be successful (i.e. due to Device Lost)
// If the call fails, then we must reinitialize EGL and the GL resources.
mRenderer->Pause();
mDeviceLost = 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));
// wait until OpenGL is reset or thread is cancelled
while (mDeviceLost)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (!mDeviceLost)
{
mOpenGLES->MakeCurrent(mRenderSurface);
// restart cocos2d-x
mRenderer->DeviceLost();
}
else // spurious wake up
{
continue;
}
}
}
}
});
// 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();
std::unique_lock<std::mutex> locker(mSleepMutex);
mSleepCondition.notify_one();
mRenderLoopWorker = nullptr;
}
}
void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e) void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e)
{ {
bool isMouseEvent = e->CurrentPoint->PointerDevice->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse; bool isMouseEvent = e->CurrentPoint->PointerDevice->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse;
@ -229,7 +417,6 @@ void OpenGLESPage::OnKeyReleased(CoreWindow^ sender, KeyEventArgs^ e)
void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args) void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
{ {
critical_section::scoped_lock lock(mSwapChainPanelSizeCriticalSection);
mOrientation = sender->CurrentOrientation; mOrientation = sender->CurrentOrientation;
} }
@ -279,216 +466,3 @@ void OpenGLESPage::OnBackButtonPressed(Object^ sender, BackPressedEventArgs^ arg
} }
} }
#endif #endif
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<GLsizei>(mCustomRenderSurfaceSize.Width);
*height = static_cast<GLsizei>(mCustomRenderSurfaceSize.Height);
}
else
{
*width = static_cast<GLsizei>(mSwapChainPanelSize.Width);
*height = static_cast<GLsizei>(mSwapChainPanelSize.Height);
}
}
void OpenGLESPage::CreateRenderSurface()
{
if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
{
//
// 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()
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
DestroyRenderSurface();
mOpenGLES->Reset();
CreateRenderSurface();
std::unique_lock<std::mutex> locker(mSleepMutex);
mDeviceLost = false;
mSleepCondition.notify_one();
}
void OpenGLESPage::TerminateApp()
{
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
mOpenGLES->Cleanup();
}
}
Windows::UI::Xaml::Application::Current->Exit();
}
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();
mDpi = 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)
{
mOpenGLES->MakeCurrent(mRenderSurface);
GLsizei panelWidth = 0;
GLsizei panelHeight = 0;
GetSwapChainPanelSize(&panelWidth, &panelHeight);
if (mRenderer.get() == nullptr)
{
mRenderer = std::make_shared<Cocos2dRenderer>(panelWidth, panelHeight, mDpi, mOrientation, dispatcher, swapChainPanel);
}
mRenderer->Resume();
while (action->Status == Windows::Foundation::AsyncStatus::Started)
{
if (!mVisible)
{
mRenderer->Pause();
}
// wait until app is visible again or thread is cancelled
while (!mVisible)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (mVisible)
{
mRenderer->Resume();
}
else // spurious wake up
{
continue;
}
}
GetSwapChainPanelSize(&panelWidth, &panelHeight);
mRenderer.get()->Draw(panelWidth, panelHeight, mDpi, mOrientation);
// Recreate input dispatch
if (GLViewImpl::sharedOpenGLView() && mCursorVisible != GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
CreateInput();
mCursorVisible = GLViewImpl::sharedOpenGLView()->isCursorVisible();
}
if (mRenderer->AppShouldExit())
{
// run on main UI thread
swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new DispatchedHandler([this]()
{
TerminateApp();
}));
return;
}
EGLBoolean result = GL_FALSE;
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
result = mOpenGLES->SwapBuffers(mRenderSurface);
}
if (result != GL_TRUE)
{
// The call to eglSwapBuffers was not be successful (i.e. due to Device Lost)
// If the call fails, then we must reinitialize EGL and the GL resources.
mRenderer->Pause();
mDeviceLost = 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));
// wait until OpenGL is reset or thread is cancelled
while (mDeviceLost)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (!mDeviceLost)
{
mOpenGLES->MakeCurrent(mRenderSurface);
// restart cocos2d-x
mRenderer->DeviceLost();
}
else // spurious wake up
{
continue;
}
}
}
}
});
// 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();
std::unique_lock<std::mutex> locker(mSleepMutex);
mSleepCondition.notify_one();
mRenderLoopWorker = nullptr;
}
}

View File

@ -41,11 +41,9 @@ namespace CocosAppWinRT
private: private:
void OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); void OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args);
void OnSwapChainPanelSizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e);
#if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || _MSC_VER >= 1900 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || _MSC_VER >= 1900
void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args); void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args);
#endif #endif
void GetSwapChainPanelSize(GLsizei* width, GLsizei* height);
void CreateRenderSurface(); void CreateRenderSurface();
void DestroyRenderSurface(); void DestroyRenderSurface();
void RecoverFromLostDevice(); void RecoverFromLostDevice();
@ -58,12 +56,6 @@ namespace CocosAppWinRT
OpenGLES* mOpenGLES; OpenGLES* mOpenGLES;
std::shared_ptr<Cocos2dRenderer> mRenderer; std::shared_ptr<Cocos2dRenderer> mRenderer;
Windows::Foundation::Size mSwapChainPanelSize;
Concurrency::critical_section mSwapChainPanelSizeCriticalSection;
Windows::Foundation::Size mCustomRenderSurfaceSize;
bool mUseCustomRenderSurfaceSize;
EGLSurface mRenderSurface; // This surface is associated with a swapChainPanel on the page EGLSurface mRenderSurface; // This surface is associated with a swapChainPanel on the page
Concurrency::critical_section mRenderSurfaceCriticalSection; Concurrency::critical_section mRenderSurfaceCriticalSection;
Windows::Foundation::IAsyncAction^ mRenderLoopWorker; Windows::Foundation::IAsyncAction^ mRenderLoopWorker;

View File

@ -112,14 +112,7 @@ void OpenGLES::Initialize()
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again // 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again
// using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer. // using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer.
// //
// Note: On Windows Phone, we #ifdef out the first set of calls to eglPlatformDisplayEXT and eglInitialize.
// Windows Phones devices only support D3D11 Feature Level 9_3, but the Windows Phone emulator supports 11_0+.
// We use this #ifdef to limit the Phone emulator to Feature Level 9_3, making it behave more like
// real Windows Phone devices.
// If you wish to test Feature Level 10_0+ in the Windows Phone emulator then you should remove this #ifdef.
//
#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP)
// This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details. // This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details.
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
@ -128,9 +121,8 @@ void OpenGLES::Initialize()
} }
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE)
#endif
{ {
// This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on Windows Phone, or certain Windows tablets). // This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices).
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
{ {
@ -139,7 +131,7 @@ void OpenGLES::Initialize()
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE)
{ {
// This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU (e.g. on Surface RT). // This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU.
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
{ {
@ -188,13 +180,18 @@ void OpenGLES::Reset()
Initialize(); Initialize();
} }
EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize) EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize, const float* resolutionScale)
{ {
if (!panel) if (!panel)
{ {
throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid"); throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid");
} }
if (renderSurfaceSize != nullptr && resolutionScale != nullptr)
{
throw Exception::CreateException(E_INVALIDARG, L"A size and a scale can't both be specified");
}
EGLSurface surface = EGL_NO_SURFACE; EGLSurface surface = EGL_NO_SURFACE;
const EGLint surfaceAttributes[] = const EGLint surfaceAttributes[] =
@ -207,12 +204,18 @@ EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurf
// Create a PropertySet and initialize with the EGLNativeWindowType. // Create a PropertySet and initialize with the EGLNativeWindowType.
PropertySet^ surfaceCreationProperties = ref new PropertySet(); PropertySet^ surfaceCreationProperties = ref new PropertySet();
surfaceCreationProperties->Insert(ref new Platform::String(EGLNativeWindowTypeProperty), panel); surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), panel);
// If a render surface size is specified, add it to the surface creation properties // If a render surface size is specified, add it to the surface creation properties
if (renderSurfaceSize != nullptr) if (renderSurfaceSize != nullptr)
{ {
surfaceCreationProperties->Insert(ref new Platform::String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize)); surfaceCreationProperties->Insert(ref new String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize));
}
// If a resolution scale is specified, add it to the surface creation properties
if (resolutionScale != nullptr)
{
surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(*resolutionScale));
} }
surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes); surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes);
@ -224,6 +227,12 @@ EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurf
return surface; return surface;
} }
void OpenGLES::GetSurfaceDimensions(const EGLSurface surface, EGLint* width, EGLint* height)
{
eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
}
void OpenGLES::DestroySurface(const EGLSurface surface) void OpenGLES::DestroySurface(const EGLSurface surface)
{ {
if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE) if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE)

View File

@ -20,7 +20,6 @@
// OpenGL ES includes // OpenGL ES includes
#include <GLES3/gl3.h> #include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
// EGL includes // EGL includes
#include <EGL/egl.h> #include <EGL/egl.h>
@ -34,7 +33,8 @@ public:
OpenGLES(); OpenGLES();
~OpenGLES(); ~OpenGLES();
EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize); EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize, const float* renderResolutionScale);
void GetSurfaceDimensions(const EGLSurface surface, EGLint *width, EGLint *height);
void DestroySurface(const EGLSurface surface); void DestroySurface(const EGLSurface surface);
void MakeCurrent(const EGLSurface surface); void MakeCurrent(const EGLSurface surface);
EGLBoolean SwapBuffers(const EGLSurface surface); EGLBoolean SwapBuffers(const EGLSurface surface);

View File

@ -24,7 +24,6 @@ using namespace cocos2d;
using namespace Platform; using namespace Platform;
using namespace Concurrency; using namespace Concurrency;
using namespace Windows::Foundation; using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Graphics::Display; using namespace Windows::Graphics::Display;
using namespace Windows::System::Threading; using namespace Windows::System::Threading;
using namespace Windows::UI::Core; using namespace Windows::UI::Core;
@ -50,8 +49,6 @@ OpenGLESPage::OpenGLESPage() :
OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) : OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
mOpenGLES(openGLES), mOpenGLES(openGLES),
mRenderSurface(EGL_NO_SURFACE), mRenderSurface(EGL_NO_SURFACE),
mCustomRenderSurfaceSize(0,0),
mUseCustomRenderSurfaceSize(false),
mCoreInput(nullptr), mCoreInput(nullptr),
mDpi(0.0f), mDpi(0.0f),
mDeviceLost(false), mDeviceLost(false),
@ -72,8 +69,6 @@ OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
window->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &OpenGLESPage::OnCharacterReceived); window->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &OpenGLESPage::OnCharacterReceived);
swapChainPanel->SizeChanged +=
ref new Windows::UI::Xaml::SizeChangedEventHandler(this, &OpenGLESPage::OnSwapChainPanelSizeChanged);
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
@ -85,8 +80,6 @@ OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
this->Loaded += this->Loaded +=
ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded); ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded);
mSwapChainPanelSize = { swapChainPanel->RenderSize.Width, swapChainPanel->RenderSize.Height };
#if _MSC_VER >= 1900 #if _MSC_VER >= 1900
if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar")) if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
{ {
@ -158,6 +151,201 @@ void OpenGLESPage::OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::Rou
mVisible = true; mVisible = true;
} }
void OpenGLESPage::CreateRenderSurface()
{
if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
{
// The app can configure the the SwapChainPanel which may boost performance.
// By default, this template uses the default configuration.
mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, nullptr);
// You can configure the SwapChainPanel to render at a lower resolution and be scaled up to
// the swapchain panel size. This scaling is often free on mobile hardware.
//
// One way to configure the SwapChainPanel is to specify precisely which resolution it should render at.
// Size customRenderSurfaceSize = Size(800, 600);
// mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, &customRenderSurfaceSize, nullptr);
//
// Another way is to tell the SwapChainPanel to render at a certain scale factor compared to its size.
// e.g. if the SwapChainPanel is 1920x1280 then setting a factor of 0.5f will make the app render at 960x640
// float customResolutionScale = 0.5f;
// mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, &customResolutionScale);
//
}
}
void OpenGLESPage::DestroyRenderSurface()
{
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
}
mRenderSurface = EGL_NO_SURFACE;
}
void OpenGLESPage::RecoverFromLostDevice()
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
DestroyRenderSurface();
mOpenGLES->Reset();
CreateRenderSurface();
std::unique_lock<std::mutex> locker(mSleepMutex);
mDeviceLost = false;
mSleepCondition.notify_one();
}
void OpenGLESPage::TerminateApp()
{
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
mOpenGLES->Cleanup();
}
}
Windows::UI::Xaml::Application::Current->Exit();
}
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();
mDpi = 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)
{
mOpenGLES->MakeCurrent(mRenderSurface);
GLsizei panelWidth = 0;
GLsizei panelHeight = 0;
mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
if (mRenderer.get() == nullptr)
{
mRenderer = std::make_shared<Cocos2dRenderer>(panelWidth, panelHeight, mDpi, mOrientation, dispatcher, swapChainPanel);
}
mRenderer->Resume();
while (action->Status == Windows::Foundation::AsyncStatus::Started)
{
if (!mVisible)
{
mRenderer->Pause();
}
// wait until app is visible again or thread is cancelled
while (!mVisible)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (mVisible)
{
mRenderer->Resume();
}
else // spurious wake up
{
continue;
}
}
mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
mRenderer.get()->Draw(panelWidth, panelHeight, mDpi, mOrientation);
// Recreate input dispatch
if (GLViewImpl::sharedOpenGLView() && mCursorVisible != GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
CreateInput();
mCursorVisible = GLViewImpl::sharedOpenGLView()->isCursorVisible();
}
if (mRenderer->AppShouldExit())
{
// run on main UI thread
swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new DispatchedHandler([=]()
{
TerminateApp();
}));
return;
}
EGLBoolean result = GL_FALSE;
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
result = mOpenGLES->SwapBuffers(mRenderSurface);
}
if (result != GL_TRUE)
{
// The call to eglSwapBuffers was not be successful (i.e. due to Device Lost)
// If the call fails, then we must reinitialize EGL and the GL resources.
mRenderer->Pause();
mDeviceLost = 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));
// wait until OpenGL is reset or thread is cancelled
while (mDeviceLost)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (!mDeviceLost)
{
mOpenGLES->MakeCurrent(mRenderSurface);
// restart cocos2d-x
mRenderer->DeviceLost();
}
else // spurious wake up
{
continue;
}
}
}
}
});
// 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();
std::unique_lock<std::mutex> locker(mSleepMutex);
mSleepCondition.notify_one();
mRenderLoopWorker = nullptr;
}
}
void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e) void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e)
{ {
bool isMouseEvent = e->CurrentPoint->PointerDevice->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse; bool isMouseEvent = e->CurrentPoint->PointerDevice->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse;
@ -229,7 +417,6 @@ void OpenGLESPage::OnKeyReleased(CoreWindow^ sender, KeyEventArgs^ e)
void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args) void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
{ {
critical_section::scoped_lock lock(mSwapChainPanelSizeCriticalSection);
mOrientation = sender->CurrentOrientation; mOrientation = sender->CurrentOrientation;
} }
@ -279,216 +466,3 @@ void OpenGLESPage::OnBackButtonPressed(Object^ sender, BackPressedEventArgs^ arg
} }
} }
#endif #endif
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<GLsizei>(mCustomRenderSurfaceSize.Width);
*height = static_cast<GLsizei>(mCustomRenderSurfaceSize.Height);
}
else
{
*width = static_cast<GLsizei>(mSwapChainPanelSize.Width);
*height = static_cast<GLsizei>(mSwapChainPanelSize.Height);
}
}
void OpenGLESPage::CreateRenderSurface()
{
if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
{
//
// 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()
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
DestroyRenderSurface();
mOpenGLES->Reset();
CreateRenderSurface();
std::unique_lock<std::mutex> locker(mSleepMutex);
mDeviceLost = false;
mSleepCondition.notify_one();
}
void OpenGLESPage::TerminateApp()
{
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
mOpenGLES->Cleanup();
}
}
Windows::UI::Xaml::Application::Current->Exit();
}
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();
mDpi = 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)
{
mOpenGLES->MakeCurrent(mRenderSurface);
GLsizei panelWidth = 0;
GLsizei panelHeight = 0;
GetSwapChainPanelSize(&panelWidth, &panelHeight);
if (mRenderer.get() == nullptr)
{
mRenderer = std::make_shared<Cocos2dRenderer>(panelWidth, panelHeight, mDpi, mOrientation, dispatcher, swapChainPanel);
}
mRenderer->Resume();
while (action->Status == Windows::Foundation::AsyncStatus::Started)
{
if (!mVisible)
{
mRenderer->Pause();
}
// wait until app is visible again or thread is cancelled
while (!mVisible)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (mVisible)
{
mRenderer->Resume();
}
else // spurious wake up
{
continue;
}
}
GetSwapChainPanelSize(&panelWidth, &panelHeight);
mRenderer.get()->Draw(panelWidth, panelHeight, mDpi, mOrientation);
// Recreate input dispatch
if (GLViewImpl::sharedOpenGLView() && mCursorVisible != GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
CreateInput();
mCursorVisible = GLViewImpl::sharedOpenGLView()->isCursorVisible();
}
if (mRenderer->AppShouldExit())
{
// run on main UI thread
swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new DispatchedHandler([this]()
{
TerminateApp();
}));
return;
}
EGLBoolean result = GL_FALSE;
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
result = mOpenGLES->SwapBuffers(mRenderSurface);
}
if (result != GL_TRUE)
{
// The call to eglSwapBuffers was not be successful (i.e. due to Device Lost)
// If the call fails, then we must reinitialize EGL and the GL resources.
mRenderer->Pause();
mDeviceLost = 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));
// wait until OpenGL is reset or thread is cancelled
while (mDeviceLost)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (!mDeviceLost)
{
mOpenGLES->MakeCurrent(mRenderSurface);
// restart cocos2d-x
mRenderer->DeviceLost();
}
else // spurious wake up
{
continue;
}
}
}
}
});
// 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();
std::unique_lock<std::mutex> locker(mSleepMutex);
mSleepCondition.notify_one();
mRenderLoopWorker = nullptr;
}
}

View File

@ -41,11 +41,9 @@ namespace CocosAppWinRT
private: private:
void OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); void OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args);
void OnSwapChainPanelSizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e);
#if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || _MSC_VER >= 1900 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || _MSC_VER >= 1900
void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args); void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args);
#endif #endif
void GetSwapChainPanelSize(GLsizei* width, GLsizei* height);
void CreateRenderSurface(); void CreateRenderSurface();
void DestroyRenderSurface(); void DestroyRenderSurface();
void RecoverFromLostDevice(); void RecoverFromLostDevice();
@ -58,12 +56,6 @@ namespace CocosAppWinRT
OpenGLES* mOpenGLES; OpenGLES* mOpenGLES;
std::shared_ptr<Cocos2dRenderer> mRenderer; std::shared_ptr<Cocos2dRenderer> mRenderer;
Windows::Foundation::Size mSwapChainPanelSize;
Concurrency::critical_section mSwapChainPanelSizeCriticalSection;
Windows::Foundation::Size mCustomRenderSurfaceSize;
bool mUseCustomRenderSurfaceSize;
EGLSurface mRenderSurface; // This surface is associated with a swapChainPanel on the page EGLSurface mRenderSurface; // This surface is associated with a swapChainPanel on the page
Concurrency::critical_section mRenderSurfaceCriticalSection; Concurrency::critical_section mRenderSurfaceCriticalSection;
Windows::Foundation::IAsyncAction^ mRenderLoopWorker; Windows::Foundation::IAsyncAction^ mRenderLoopWorker;

View File

@ -112,14 +112,7 @@ void OpenGLES::Initialize()
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again // 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again
// using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer. // using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer.
// //
// Note: On Windows Phone, we #ifdef out the first set of calls to eglPlatformDisplayEXT and eglInitialize.
// Windows Phones devices only support D3D11 Feature Level 9_3, but the Windows Phone emulator supports 11_0+.
// We use this #ifdef to limit the Phone emulator to Feature Level 9_3, making it behave more like
// real Windows Phone devices.
// If you wish to test Feature Level 10_0+ in the Windows Phone emulator then you should remove this #ifdef.
//
#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP)
// This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details. // This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details.
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
@ -128,9 +121,8 @@ void OpenGLES::Initialize()
} }
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE)
#endif
{ {
// This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on Windows Phone, or certain Windows tablets). // This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices).
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
{ {
@ -139,7 +131,7 @@ void OpenGLES::Initialize()
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE)
{ {
// This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU (e.g. on Surface RT). // This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU.
mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes);
if (mEglDisplay == EGL_NO_DISPLAY) if (mEglDisplay == EGL_NO_DISPLAY)
{ {
@ -188,13 +180,18 @@ void OpenGLES::Reset()
Initialize(); Initialize();
} }
EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize) EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurfaceSize, const float* resolutionScale)
{ {
if (!panel) if (!panel)
{ {
throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid"); throw Exception::CreateException(E_INVALIDARG, L"SwapChainPanel parameter is invalid");
} }
if (renderSurfaceSize != nullptr && resolutionScale != nullptr)
{
throw Exception::CreateException(E_INVALIDARG, L"A size and a scale can't both be specified");
}
EGLSurface surface = EGL_NO_SURFACE; EGLSurface surface = EGL_NO_SURFACE;
const EGLint surfaceAttributes[] = const EGLint surfaceAttributes[] =
@ -207,12 +204,18 @@ EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurf
// Create a PropertySet and initialize with the EGLNativeWindowType. // Create a PropertySet and initialize with the EGLNativeWindowType.
PropertySet^ surfaceCreationProperties = ref new PropertySet(); PropertySet^ surfaceCreationProperties = ref new PropertySet();
surfaceCreationProperties->Insert(ref new Platform::String(EGLNativeWindowTypeProperty), panel); surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), panel);
// If a render surface size is specified, add it to the surface creation properties // If a render surface size is specified, add it to the surface creation properties
if (renderSurfaceSize != nullptr) if (renderSurfaceSize != nullptr)
{ {
surfaceCreationProperties->Insert(ref new Platform::String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize)); surfaceCreationProperties->Insert(ref new String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(*renderSurfaceSize));
}
// If a resolution scale is specified, add it to the surface creation properties
if (resolutionScale != nullptr)
{
surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(*resolutionScale));
} }
surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes); surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes);
@ -224,6 +227,12 @@ EGLSurface OpenGLES::CreateSurface(SwapChainPanel^ panel, const Size* renderSurf
return surface; return surface;
} }
void OpenGLES::GetSurfaceDimensions(const EGLSurface surface, EGLint* width, EGLint* height)
{
eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
}
void OpenGLES::DestroySurface(const EGLSurface surface) void OpenGLES::DestroySurface(const EGLSurface surface)
{ {
if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE) if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE)

View File

@ -20,7 +20,6 @@
// OpenGL ES includes // OpenGL ES includes
#include <GLES3/gl3.h> #include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
// EGL includes // EGL includes
#include <EGL/egl.h> #include <EGL/egl.h>
@ -34,7 +33,8 @@ public:
OpenGLES(); OpenGLES();
~OpenGLES(); ~OpenGLES();
EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize); EGLSurface CreateSurface(Windows::UI::Xaml::Controls::SwapChainPanel^ panel, const Windows::Foundation::Size* renderSurfaceSize, const float* renderResolutionScale);
void GetSurfaceDimensions(const EGLSurface surface, EGLint *width, EGLint *height);
void DestroySurface(const EGLSurface surface); void DestroySurface(const EGLSurface surface);
void MakeCurrent(const EGLSurface surface); void MakeCurrent(const EGLSurface surface);
EGLBoolean SwapBuffers(const EGLSurface surface); EGLBoolean SwapBuffers(const EGLSurface surface);

View File

@ -24,7 +24,6 @@ using namespace cocos2d;
using namespace Platform; using namespace Platform;
using namespace Concurrency; using namespace Concurrency;
using namespace Windows::Foundation; using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Graphics::Display; using namespace Windows::Graphics::Display;
using namespace Windows::System::Threading; using namespace Windows::System::Threading;
using namespace Windows::UI::Core; using namespace Windows::UI::Core;
@ -50,8 +49,6 @@ OpenGLESPage::OpenGLESPage() :
OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) : OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
mOpenGLES(openGLES), mOpenGLES(openGLES),
mRenderSurface(EGL_NO_SURFACE), mRenderSurface(EGL_NO_SURFACE),
mCustomRenderSurfaceSize(0,0),
mUseCustomRenderSurfaceSize(false),
mCoreInput(nullptr), mCoreInput(nullptr),
mDpi(0.0f), mDpi(0.0f),
mDeviceLost(false), mDeviceLost(false),
@ -72,8 +69,6 @@ OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
window->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &OpenGLESPage::OnCharacterReceived); window->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &OpenGLESPage::OnCharacterReceived);
swapChainPanel->SizeChanged +=
ref new Windows::UI::Xaml::SizeChangedEventHandler(this, &OpenGLESPage::OnSwapChainPanelSizeChanged);
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
@ -85,8 +80,6 @@ OpenGLESPage::OpenGLESPage(OpenGLES* openGLES) :
this->Loaded += this->Loaded +=
ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded); ref new Windows::UI::Xaml::RoutedEventHandler(this, &OpenGLESPage::OnPageLoaded);
mSwapChainPanelSize = { swapChainPanel->RenderSize.Width, swapChainPanel->RenderSize.Height };
#if _MSC_VER >= 1900 #if _MSC_VER >= 1900
if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar")) if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
{ {
@ -158,6 +151,201 @@ void OpenGLESPage::OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::Rou
mVisible = true; mVisible = true;
} }
void OpenGLESPage::CreateRenderSurface()
{
if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
{
// The app can configure the the SwapChainPanel which may boost performance.
// By default, this template uses the default configuration.
mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, nullptr);
// You can configure the SwapChainPanel to render at a lower resolution and be scaled up to
// the swapchain panel size. This scaling is often free on mobile hardware.
//
// One way to configure the SwapChainPanel is to specify precisely which resolution it should render at.
// Size customRenderSurfaceSize = Size(800, 600);
// mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, &customRenderSurfaceSize, nullptr);
//
// Another way is to tell the SwapChainPanel to render at a certain scale factor compared to its size.
// e.g. if the SwapChainPanel is 1920x1280 then setting a factor of 0.5f will make the app render at 960x640
// float customResolutionScale = 0.5f;
// mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, &customResolutionScale);
//
}
}
void OpenGLESPage::DestroyRenderSurface()
{
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
}
mRenderSurface = EGL_NO_SURFACE;
}
void OpenGLESPage::RecoverFromLostDevice()
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
DestroyRenderSurface();
mOpenGLES->Reset();
CreateRenderSurface();
std::unique_lock<std::mutex> locker(mSleepMutex);
mDeviceLost = false;
mSleepCondition.notify_one();
}
void OpenGLESPage::TerminateApp()
{
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
mOpenGLES->Cleanup();
}
}
Windows::UI::Xaml::Application::Current->Exit();
}
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();
mDpi = 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)
{
mOpenGLES->MakeCurrent(mRenderSurface);
GLsizei panelWidth = 0;
GLsizei panelHeight = 0;
mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
if (mRenderer.get() == nullptr)
{
mRenderer = std::make_shared<Cocos2dRenderer>(panelWidth, panelHeight, mDpi, mOrientation, dispatcher, swapChainPanel);
}
mRenderer->Resume();
while (action->Status == Windows::Foundation::AsyncStatus::Started)
{
if (!mVisible)
{
mRenderer->Pause();
}
// wait until app is visible again or thread is cancelled
while (!mVisible)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (mVisible)
{
mRenderer->Resume();
}
else // spurious wake up
{
continue;
}
}
mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
mRenderer.get()->Draw(panelWidth, panelHeight, mDpi, mOrientation);
// Recreate input dispatch
if (GLViewImpl::sharedOpenGLView() && mCursorVisible != GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
CreateInput();
mCursorVisible = GLViewImpl::sharedOpenGLView()->isCursorVisible();
}
if (mRenderer->AppShouldExit())
{
// run on main UI thread
swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new DispatchedHandler([=]()
{
TerminateApp();
}));
return;
}
EGLBoolean result = GL_FALSE;
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
result = mOpenGLES->SwapBuffers(mRenderSurface);
}
if (result != GL_TRUE)
{
// The call to eglSwapBuffers was not be successful (i.e. due to Device Lost)
// If the call fails, then we must reinitialize EGL and the GL resources.
mRenderer->Pause();
mDeviceLost = 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));
// wait until OpenGL is reset or thread is cancelled
while (mDeviceLost)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (!mDeviceLost)
{
mOpenGLES->MakeCurrent(mRenderSurface);
// restart cocos2d-x
mRenderer->DeviceLost();
}
else // spurious wake up
{
continue;
}
}
}
}
});
// 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();
std::unique_lock<std::mutex> locker(mSleepMutex);
mSleepCondition.notify_one();
mRenderLoopWorker = nullptr;
}
}
void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e) void OpenGLESPage::OnPointerPressed(Object^ sender, PointerEventArgs^ e)
{ {
bool isMouseEvent = e->CurrentPoint->PointerDevice->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse; bool isMouseEvent = e->CurrentPoint->PointerDevice->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse;
@ -229,7 +417,6 @@ void OpenGLESPage::OnKeyReleased(CoreWindow^ sender, KeyEventArgs^ e)
void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args) void OpenGLESPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
{ {
critical_section::scoped_lock lock(mSwapChainPanelSizeCriticalSection);
mOrientation = sender->CurrentOrientation; mOrientation = sender->CurrentOrientation;
} }
@ -279,216 +466,3 @@ void OpenGLESPage::OnBackButtonPressed(Object^ sender, BackPressedEventArgs^ arg
} }
} }
#endif #endif
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<GLsizei>(mCustomRenderSurfaceSize.Width);
*height = static_cast<GLsizei>(mCustomRenderSurfaceSize.Height);
}
else
{
*width = static_cast<GLsizei>(mSwapChainPanelSize.Width);
*height = static_cast<GLsizei>(mSwapChainPanelSize.Height);
}
}
void OpenGLESPage::CreateRenderSurface()
{
if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
{
//
// 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()
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
DestroyRenderSurface();
mOpenGLES->Reset();
CreateRenderSurface();
std::unique_lock<std::mutex> locker(mSleepMutex);
mDeviceLost = false;
mSleepCondition.notify_one();
}
void OpenGLESPage::TerminateApp()
{
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
if (mOpenGLES)
{
mOpenGLES->DestroySurface(mRenderSurface);
mOpenGLES->Cleanup();
}
}
Windows::UI::Xaml::Application::Current->Exit();
}
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();
mDpi = 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)
{
mOpenGLES->MakeCurrent(mRenderSurface);
GLsizei panelWidth = 0;
GLsizei panelHeight = 0;
GetSwapChainPanelSize(&panelWidth, &panelHeight);
if (mRenderer.get() == nullptr)
{
mRenderer = std::make_shared<Cocos2dRenderer>(panelWidth, panelHeight, mDpi, mOrientation, dispatcher, swapChainPanel);
}
mRenderer->Resume();
while (action->Status == Windows::Foundation::AsyncStatus::Started)
{
if (!mVisible)
{
mRenderer->Pause();
}
// wait until app is visible again or thread is cancelled
while (!mVisible)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (mVisible)
{
mRenderer->Resume();
}
else // spurious wake up
{
continue;
}
}
GetSwapChainPanelSize(&panelWidth, &panelHeight);
mRenderer.get()->Draw(panelWidth, panelHeight, mDpi, mOrientation);
// Recreate input dispatch
if (GLViewImpl::sharedOpenGLView() && mCursorVisible != GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
CreateInput();
mCursorVisible = GLViewImpl::sharedOpenGLView()->isCursorVisible();
}
if (mRenderer->AppShouldExit())
{
// run on main UI thread
swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new DispatchedHandler([this]()
{
TerminateApp();
}));
return;
}
EGLBoolean result = GL_FALSE;
{
critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);
result = mOpenGLES->SwapBuffers(mRenderSurface);
}
if (result != GL_TRUE)
{
// The call to eglSwapBuffers was not be successful (i.e. due to Device Lost)
// If the call fails, then we must reinitialize EGL and the GL resources.
mRenderer->Pause();
mDeviceLost = 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));
// wait until OpenGL is reset or thread is cancelled
while (mDeviceLost)
{
std::unique_lock<std::mutex> lock(mSleepMutex);
mSleepCondition.wait(lock);
if (action->Status != Windows::Foundation::AsyncStatus::Started)
{
return; // thread was cancelled. Exit thread
}
if (!mDeviceLost)
{
mOpenGLES->MakeCurrent(mRenderSurface);
// restart cocos2d-x
mRenderer->DeviceLost();
}
else // spurious wake up
{
continue;
}
}
}
}
});
// 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();
std::unique_lock<std::mutex> locker(mSleepMutex);
mSleepCondition.notify_one();
mRenderLoopWorker = nullptr;
}
}

View File

@ -41,11 +41,9 @@ namespace CocosAppWinRT
private: private:
void OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); void OnPageLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args);
void OnSwapChainPanelSizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e);
#if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || _MSC_VER >= 1900 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || _MSC_VER >= 1900
void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args); void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args);
#endif #endif
void GetSwapChainPanelSize(GLsizei* width, GLsizei* height);
void CreateRenderSurface(); void CreateRenderSurface();
void DestroyRenderSurface(); void DestroyRenderSurface();
void RecoverFromLostDevice(); void RecoverFromLostDevice();
@ -58,12 +56,6 @@ namespace CocosAppWinRT
OpenGLES* mOpenGLES; OpenGLES* mOpenGLES;
std::shared_ptr<Cocos2dRenderer> mRenderer; std::shared_ptr<Cocos2dRenderer> mRenderer;
Windows::Foundation::Size mSwapChainPanelSize;
Concurrency::critical_section mSwapChainPanelSizeCriticalSection;
Windows::Foundation::Size mCustomRenderSurfaceSize;
bool mUseCustomRenderSurfaceSize;
EGLSurface mRenderSurface; // This surface is associated with a swapChainPanel on the page EGLSurface mRenderSurface; // This surface is associated with a swapChainPanel on the page
Concurrency::critical_section mRenderSurfaceCriticalSection; Concurrency::critical_section mRenderSurfaceCriticalSection;
Windows::Foundation::IAsyncAction^ mRenderLoopWorker; Windows::Foundation::IAsyncAction^ mRenderLoopWorker;