1/*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//--------------------------------------------------------------------------------
18// GLContext.cpp
19//--------------------------------------------------------------------------------
20//--------------------------------------------------------------------------------
21// includes
22//--------------------------------------------------------------------------------
23#include <unistd.h>
24#include "GLContext.h"
25#include "gl3stub.h"
26
27namespace ndk_helper
28{
29
30//--------------------------------------------------------------------------------
31// eGLContext
32//--------------------------------------------------------------------------------
33
34//--------------------------------------------------------------------------------
35// Ctor
36//--------------------------------------------------------------------------------
37GLContext::GLContext() :
38                display_( EGL_NO_DISPLAY ),
39                surface_( EGL_NO_SURFACE ),
40                context_( EGL_NO_CONTEXT ),
41                screen_width_( 0 ),
42                screen_height_( 0 ),
43                es3_supported_( false ),
44                egl_context_initialized_( false ),
45                gles_initialized_( false )
46{
47}
48
49void GLContext::InitGLES()
50{
51    if( gles_initialized_ )
52        return;
53    //
54    //Initialize OpenGL ES 3 if available
55    //
56    const char* versionStr = (const char*) glGetString( GL_VERSION );
57    if( strstr( versionStr, "OpenGL ES 3." ) && gl3stubInit() )
58    {
59        es3_supported_ = true;
60        gl_version_ = 3.0f;
61    }
62    else
63    {
64        gl_version_ = 2.0f;
65    }
66
67    gles_initialized_ = true;
68}
69
70//--------------------------------------------------------------------------------
71// Dtor
72//--------------------------------------------------------------------------------
73GLContext::~GLContext()
74{
75    Terminate();
76}
77
78bool GLContext::Init( ANativeWindow* window )
79{
80    if( egl_context_initialized_ )
81        return true;
82
83    //
84    //Initialize EGL
85    //
86    window_ = window;
87    InitEGLSurface();
88    InitEGLContext();
89    InitGLES();
90
91    egl_context_initialized_ = true;
92
93    return true;
94}
95
96bool GLContext::InitEGLSurface()
97{
98    display_ = eglGetDisplay( EGL_DEFAULT_DISPLAY );
99    eglInitialize( display_, 0, 0 );
100
101    /*
102     * Here specify the attributes of the desired configuration.
103     * Below, we select an EGLConfig with at least 8 bits per color
104     * component compatible with on-screen windows
105     */
106    const EGLint attribs[] = { EGL_RENDERABLE_TYPE,
107            EGL_OPENGL_ES2_BIT, //Request opengl ES2.0
108            EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8,
109            EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_NONE };
110    color_size_ = 8;
111    depth_size_ = 24;
112
113    EGLint num_configs;
114    eglChooseConfig( display_, attribs, &config_, 1, &num_configs );
115
116    if( !num_configs )
117    {
118        //Fall back to 16bit depth buffer
119        const EGLint attribs[] = { EGL_RENDERABLE_TYPE,
120                EGL_OPENGL_ES2_BIT, //Request opengl ES2.0
121                EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8,
122                EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_NONE };
123        eglChooseConfig( display_, attribs, &config_, 1, &num_configs );
124        depth_size_ = 16;
125    }
126
127    if( !num_configs )
128    {
129        LOGW( "Unable to retrieve EGL config" );
130        return false;
131    }
132
133    surface_ = eglCreateWindowSurface( display_, config_, window_, NULL );
134    eglQuerySurface( display_, surface_, EGL_WIDTH, &screen_width_ );
135    eglQuerySurface( display_, surface_, EGL_HEIGHT, &screen_height_ );
136
137    return true;
138}
139
140bool GLContext::InitEGLContext()
141{
142    const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, //Request opengl ES2.0
143            EGL_NONE };
144    context_ = eglCreateContext( display_, config_, NULL, context_attribs );
145
146    if( eglMakeCurrent( display_, surface_, surface_, context_ ) == EGL_FALSE )
147    {
148        LOGW( "Unable to eglMakeCurrent" );
149        return false;
150    }
151
152    context_valid_ = true;
153    return true;
154}
155
156EGLint GLContext::Swap()
157{
158    bool b = eglSwapBuffers( display_, surface_ );
159    if( !b )
160    {
161        EGLint err = eglGetError();
162        if( err == EGL_BAD_SURFACE )
163        {
164            //Recreate surface
165            InitEGLSurface();
166            return EGL_SUCCESS; //Still consider glContext is valid
167        }
168        else if( err == EGL_CONTEXT_LOST || err == EGL_BAD_CONTEXT )
169        {
170            //Context has been lost!!
171            context_valid_ = false;
172            Terminate();
173            InitEGLContext();
174        }
175        return err;
176    }
177    return EGL_SUCCESS;
178}
179
180void GLContext::Terminate()
181{
182    if( display_ != EGL_NO_DISPLAY )
183    {
184        eglMakeCurrent( display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
185        if( context_ != EGL_NO_CONTEXT )
186        {
187            eglDestroyContext( display_, context_ );
188        }
189
190        if( surface_ != EGL_NO_SURFACE )
191        {
192            eglDestroySurface( display_, surface_ );
193        }
194        eglTerminate( display_ );
195    }
196
197    display_ = EGL_NO_DISPLAY;
198    context_ = EGL_NO_CONTEXT;
199    surface_ = EGL_NO_SURFACE;
200    context_valid_ = false;
201
202}
203
204EGLint GLContext::Resume( ANativeWindow* window )
205{
206    if( egl_context_initialized_ == false )
207    {
208        Init( window );
209        return EGL_SUCCESS;
210    }
211
212    int32_t original_widhth = screen_width_;
213    int32_t original_height = screen_height_;
214
215    //Create surface
216    window_ = window;
217    surface_ = eglCreateWindowSurface( display_, config_, window_, NULL );
218    eglQuerySurface( display_, surface_, EGL_WIDTH, &screen_width_ );
219    eglQuerySurface( display_, surface_, EGL_HEIGHT, &screen_height_ );
220
221    if( screen_width_ != original_widhth || screen_height_ != original_height )
222    {
223        //Screen resized
224        LOGI( "Screen resized" );
225    }
226
227    if( eglMakeCurrent( display_, surface_, surface_, context_ ) == EGL_TRUE )
228        return EGL_SUCCESS;
229
230    EGLint err = eglGetError();
231    LOGW( "Unable to eglMakeCurrent %d", err );
232
233    if( err == EGL_CONTEXT_LOST )
234    {
235        //Recreate context
236        LOGI( "Re-creating egl context" );
237        InitEGLContext();
238    }
239    else
240    {
241        //Recreate surface
242        Terminate();
243        InitEGLSurface();
244        InitEGLContext();
245    }
246
247    return err;
248
249}
250
251void GLContext::Suspend()
252{
253    if( surface_ != EGL_NO_SURFACE )
254    {
255        eglDestroySurface( display_, surface_ );
256        surface_ = EGL_NO_SURFACE;
257    }
258}
259
260bool GLContext::Invalidate()
261{
262    Terminate();
263
264    egl_context_initialized_ = false;
265    return true;
266}
267
268bool GLContext::CheckExtension( const char* extension )
269{
270    if( extension == NULL )
271        return false;
272
273    std::string extensions = std::string( (char*) glGetString( GL_EXTENSIONS ) );
274    std::string str = std::string( extension );
275    str.append( " " );
276
277    size_t pos = 0;
278    if( extensions.find( extension, pos ) != std::string::npos )
279    {
280        return true;
281    }
282
283    return false;
284}
285
286}   //namespace ndkHelper
287