| // Copyright 2016 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <EGL/egl.h> |
| #include <GLES2/gl2.h> |
| #include <math.h> |
| |
| #include <iomanip> |
| |
| #include "starboard/event.h" |
| #include "starboard/input.h" |
| #include "starboard/log.h" |
| #include "starboard/memory.h" |
| #include "starboard/system.h" |
| #include "starboard/window.h" |
| |
| namespace { |
| |
| EGLint const kAttributeList[] = {EGL_RED_SIZE, |
| 8, |
| EGL_GREEN_SIZE, |
| 8, |
| EGL_BLUE_SIZE, |
| 8, |
| EGL_ALPHA_SIZE, |
| 8, |
| EGL_STENCIL_SIZE, |
| 0, |
| EGL_BUFFER_SIZE, |
| 32, |
| EGL_SURFACE_TYPE, |
| EGL_WINDOW_BIT | EGL_PBUFFER_BIT, |
| EGL_COLOR_BUFFER_TYPE, |
| EGL_RGB_BUFFER, |
| EGL_CONFORMANT, |
| EGL_OPENGL_ES2_BIT, |
| EGL_RENDERABLE_TYPE, |
| EGL_OPENGL_ES2_BIT, |
| EGL_NONE}; |
| } // namespace |
| |
| class Application { |
| public: |
| Application(); |
| ~Application(); |
| |
| private: |
| // The callback function passed to SbEventSchedule(). Its purpose is to |
| // forward to the non-static RenderScene() method. |
| static void RenderSceneEventCallback(void* param); |
| |
| // Renders one frame of the animated scene, incrementing |frame_| each time |
| // it is called. |
| void RenderScene(); |
| |
| // The current frame we are rendering, initialized to 0 and incremented after |
| // each frame. |
| int frame_; |
| |
| // The SbWindow within which we will perform our rendering. |
| SbWindow window_; |
| |
| EGLDisplay display_; |
| EGLSurface surface_; |
| EGLContext context_; |
| |
| EGLint egl_surface_width_; |
| EGLint egl_surface_height_; |
| }; |
| |
| Application::Application() { |
| frame_ = 0; |
| |
| SbWindowOptions options; |
| SbWindowSetDefaultOptions(&options); |
| window_ = SbWindowCreate(&options); |
| SB_CHECK(SbWindowIsValid(window_)); |
| |
| display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| SB_CHECK(EGL_NO_DISPLAY != display_); |
| |
| eglInitialize(display_, NULL, NULL); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| |
| // Some EGL drivers can return a first config that doesn't allow |
| // eglCreateWindowSurface(), with no differences in EGLConfig attribute values |
| // from configs that do allow that. To handle that, we have to attempt |
| // eglCreateWindowSurface() until we find a config that succeeds. |
| |
| // First, query how many configs match the given attribute list. |
| EGLint num_configs = 0; |
| eglChooseConfig(display_, kAttributeList, NULL, 0, &num_configs); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| SB_CHECK(0 != num_configs); |
| |
| // Allocate space to receive the matching configs and retrieve them. |
| EGLConfig* configs = reinterpret_cast<EGLConfig*>( |
| SbMemoryAllocate(num_configs * sizeof(EGLConfig))); |
| eglChooseConfig(display_, kAttributeList, configs, num_configs, &num_configs); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| |
| EGLNativeWindowType native_window = |
| (EGLNativeWindowType)SbWindowGetPlatformHandle(window_); |
| EGLConfig config; |
| |
| // Find the first config that successfully allow a window surface to be |
| // created. |
| for (int config_number = 0; config_number < num_configs; ++config_number) { |
| config = configs[config_number]; |
| surface_ = eglCreateWindowSurface(display_, config, native_window, NULL); |
| if (EGL_SUCCESS == eglGetError()) |
| break; |
| } |
| SB_DCHECK(surface_ != EGL_NO_SURFACE); |
| |
| SbMemoryDeallocate(configs); |
| |
| eglQuerySurface(display_, surface_, EGL_WIDTH, &egl_surface_width_); |
| eglQuerySurface(display_, surface_, EGL_HEIGHT, &egl_surface_height_); |
| SB_DCHECK(egl_surface_width_ > 0); |
| SB_DCHECK(egl_surface_height_ > 0); |
| |
| // Create the GLES2 or GLEX3 Context. |
| context_ = EGL_NO_CONTEXT; |
| EGLint context_attrib_list[] = { |
| EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE, |
| }; |
| #if defined(GLES3_SUPPORTED) |
| // Attempt to create an OpenGL ES 3.0 context. |
| context_ = |
| eglCreateContext(display_, config, EGL_NO_CONTEXT, context_attrib_list); |
| #endif |
| if (context_ == EGL_NO_CONTEXT) { |
| // Create an OpenGL ES 2.0 context. |
| context_attrib_list[1] = 2; |
| context_ = |
| eglCreateContext(display_, config, EGL_NO_CONTEXT, context_attrib_list); |
| } |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| SB_CHECK(context_ != EGL_NO_CONTEXT); |
| |
| /* connect the context to the surface */ |
| eglMakeCurrent(display_, surface_, surface_, context_); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| |
| RenderScene(); |
| } |
| |
| Application::~Application() { |
| // Cleanup all used resources. |
| eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| eglDestroyContext(display_, context_); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| eglDestroySurface(display_, surface_); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| eglTerminate(display_); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| SbWindowDestroy(window_); |
| } |
| |
| void Application::RenderSceneEventCallback(void* param) { |
| // Forward the call to the application instance specified as the parameter. |
| Application* application = static_cast<Application*>(param); |
| application->RenderScene(); |
| } |
| |
| namespace { |
| |
| float getIntensity(int frame, float rate) { |
| float radian = 2 * M_PI * frame / rate; |
| return 0.5 + 0.5 * sin(radian); |
| } |
| |
| } // namespace |
| |
| void Application::RenderScene() { |
| // Render a moving and color changing rectangle using glClear() and |
| // glScissor(). |
| glEnable(GL_SCISSOR_TEST); |
| |
| float radian = 2 * M_PI * frame_ / 600.0f; |
| int offset_x = egl_surface_height_ * sin(radian) / 3.6; |
| int offset_y = egl_surface_height_ * cos(radian) / 3.6; |
| |
| int block_width = egl_surface_width_ / 16; |
| int block_height = egl_surface_height_ / 9; |
| |
| int center_x = (egl_surface_width_ - block_width) / 2; |
| int center_y = (egl_surface_height_ - block_height) / 2; |
| |
| glScissor(center_x + offset_x, center_y + offset_y, block_width, |
| block_height); |
| SB_CHECK(GL_NO_ERROR == glGetError()); |
| |
| glClearColor(getIntensity(frame_, 55.0f), getIntensity(frame_, 60.0f), |
| getIntensity(frame_, 62.5f), 1.0); |
| SB_CHECK(GL_NO_ERROR == glGetError()); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| |
| glFlush(); |
| SB_CHECK(GL_NO_ERROR == glGetError()); |
| |
| eglSwapBuffers(display_, surface_); |
| SB_CHECK(EGL_SUCCESS == eglGetError()); |
| |
| // Schedule another frame render ASAP. |
| SbEventSchedule(&Application::RenderSceneEventCallback, this, 0); |
| ++frame_; |
| } |
| |
| Application* s_application = NULL; |
| |
| // Simple Starboard window event handling to kick off our color animating |
| // application. |
| void SbEventHandle(const SbEvent* event) { |
| switch (event->type) { |
| case kSbEventTypeStart: { |
| // Create the application, after which it will use SbEventSchedule() |
| // on itself to trigger a frame update until the application is |
| // terminated. |
| s_application = new Application(); |
| } break; |
| |
| case kSbEventTypeStop: { |
| // Shutdown the application. |
| delete s_application; |
| } break; |
| |
| default: {} |
| } |
| } |